LCOV - code coverage report
Current view: top level - frmts/hf2 - hf2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 534 634 84.2 %
Date: 2024-11-21 22:18:42 Functions: 18 18 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  HF2 driver
       4             :  * Purpose:  GDALDataset driver for HF2/HFZ dataset.
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_string.h"
      14             : #include "gdal_frmts.h"
      15             : #include "gdal_pam.h"
      16             : #include "ogr_spatialref.h"
      17             : 
      18             : #include <cstdlib>
      19             : #include <cmath>
      20             : 
      21             : #include <algorithm>
      22             : #include <limits>
      23             : 
      24             : /************************************************************************/
      25             : /* ==================================================================== */
      26             : /*                              HF2Dataset                              */
      27             : /* ==================================================================== */
      28             : /************************************************************************/
      29             : 
      30             : class HF2RasterBand;
      31             : 
      32             : class HF2Dataset final : public GDALPamDataset
      33             : {
      34             :     friend class HF2RasterBand;
      35             : 
      36             :     VSILFILE *fp;
      37             :     double adfGeoTransform[6];
      38             :     OGRSpatialReference m_oSRS{};
      39             :     vsi_l_offset *panBlockOffset;  // tile 0 is a the bottom left
      40             : 
      41             :     int nTileSize;
      42             :     int bHasLoaderBlockMap;
      43             :     int LoadBlockMap();
      44             : 
      45             :   public:
      46             :     HF2Dataset();
      47             :     virtual ~HF2Dataset();
      48             : 
      49             :     virtual CPLErr GetGeoTransform(double *) override;
      50             :     const OGRSpatialReference *GetSpatialRef() const override;
      51             : 
      52             :     static GDALDataset *Open(GDALOpenInfo *);
      53             :     static int Identify(GDALOpenInfo *);
      54             :     static GDALDataset *CreateCopy(const char *pszFilename,
      55             :                                    GDALDataset *poSrcDS, int bStrict,
      56             :                                    char **papszOptions,
      57             :                                    GDALProgressFunc pfnProgress,
      58             :                                    void *pProgressData);
      59             : };
      60             : 
      61             : /************************************************************************/
      62             : /* ==================================================================== */
      63             : /*                            HF2RasterBand                             */
      64             : /* ==================================================================== */
      65             : /************************************************************************/
      66             : 
      67             : class HF2RasterBand final : public GDALPamRasterBand
      68             : {
      69             :     friend class HF2Dataset;
      70             : 
      71             :     float *pafBlockData;
      72             :     int nLastBlockYOffFromBottom;
      73             : 
      74             :   public:
      75             :     HF2RasterBand(HF2Dataset *, int, GDALDataType);
      76             :     virtual ~HF2RasterBand();
      77             : 
      78             :     virtual CPLErr IReadBlock(int, int, void *) override;
      79             : };
      80             : 
      81             : /************************************************************************/
      82             : /*                           HF2RasterBand()                            */
      83             : /************************************************************************/
      84             : 
      85          23 : HF2RasterBand::HF2RasterBand(HF2Dataset *poDSIn, int nBandIn, GDALDataType eDT)
      86          23 :     : pafBlockData(nullptr), nLastBlockYOffFromBottom(-1)
      87             : {
      88          23 :     poDS = poDSIn;
      89          23 :     nBand = nBandIn;
      90             : 
      91          23 :     eDataType = eDT;
      92             : 
      93          23 :     nBlockXSize = poDSIn->nTileSize;
      94          23 :     nBlockYSize = 1;
      95          23 : }
      96             : 
      97             : /************************************************************************/
      98             : /*                          ~HF2RasterBand()                            */
      99             : /************************************************************************/
     100             : 
     101          46 : HF2RasterBand::~HF2RasterBand()
     102             : {
     103          23 :     CPLFree(pafBlockData);
     104          46 : }
     105             : 
     106             : /************************************************************************/
     107             : /*                             IReadBlock()                             */
     108             : /************************************************************************/
     109             : 
     110         764 : CPLErr HF2RasterBand::IReadBlock(int nBlockXOff, int nLineYOff, void *pImage)
     111             : 
     112             : {
     113         764 :     HF2Dataset *poGDS = (HF2Dataset *)poDS;
     114             : 
     115         764 :     const int nXBlocks = DIV_ROUND_UP(nRasterXSize, poGDS->nTileSize);
     116             : 
     117         764 :     if (!poGDS->LoadBlockMap())
     118           0 :         return CE_Failure;
     119             : 
     120         764 :     const int nMaxTileHeight = std::min(poGDS->nTileSize, nRasterYSize);
     121         764 :     if (pafBlockData == nullptr)
     122             :     {
     123           8 :         if (nMaxTileHeight > 10 * 1024 * 1024 / nRasterXSize)
     124             :         {
     125           0 :             VSIFSeekL(poGDS->fp, 0, SEEK_END);
     126           0 :             vsi_l_offset nSize = VSIFTellL(poGDS->fp);
     127           0 :             if (nSize <
     128           0 :                 static_cast<vsi_l_offset>(nMaxTileHeight) * nRasterXSize)
     129             :             {
     130           0 :                 CPLError(CE_Failure, CPLE_FileIO, "File too short");
     131           0 :                 return CE_Failure;
     132             :             }
     133             :         }
     134           8 :         pafBlockData =
     135           8 :             (float *)VSIMalloc3(sizeof(float), nRasterXSize, nMaxTileHeight);
     136           8 :         if (pafBlockData == nullptr)
     137           0 :             return CE_Failure;
     138             :     }
     139             : 
     140         764 :     const int nLineYOffFromBottom = nRasterYSize - 1 - nLineYOff;
     141             : 
     142         764 :     const int nBlockYOffFromBottom = nLineYOffFromBottom / nBlockXSize;
     143         764 :     const int nYOffInTile = nLineYOffFromBottom % nBlockXSize;
     144             : 
     145         764 :     if (nBlockYOffFromBottom != nLastBlockYOffFromBottom)
     146             :     {
     147          10 :         nLastBlockYOffFromBottom = nBlockYOffFromBottom;
     148             : 
     149          10 :         memset(pafBlockData, 0, sizeof(float) * nRasterXSize * nMaxTileHeight);
     150             : 
     151             :         /* 4 * nBlockXSize is the upper bound */
     152          10 :         void *pabyData = CPLMalloc(4 * nBlockXSize);
     153             : 
     154          24 :         for (int nxoff = 0; nxoff < nXBlocks; nxoff++)
     155             :         {
     156          14 :             VSIFSeekL(
     157             :                 poGDS->fp,
     158          14 :                 poGDS->panBlockOffset[nBlockYOffFromBottom * nXBlocks + nxoff],
     159             :                 SEEK_SET);
     160             :             float fScale, fOff;
     161          14 :             VSIFReadL(&fScale, 4, 1, poGDS->fp);
     162          14 :             VSIFReadL(&fOff, 4, 1, poGDS->fp);
     163          14 :             CPL_LSBPTR32(&fScale);
     164          14 :             CPL_LSBPTR32(&fOff);
     165             : 
     166             :             const int nTileWidth =
     167          14 :                 std::min(nBlockXSize, nRasterXSize - nxoff * nBlockXSize);
     168             :             const int nTileHeight = std::min(
     169          14 :                 nBlockXSize, nRasterYSize - nBlockYOffFromBottom * nBlockXSize);
     170             : 
     171         778 :             for (int j = 0; j < nTileHeight; j++)
     172             :             {
     173             :                 GByte nWordSize;
     174         764 :                 VSIFReadL(&nWordSize, 1, 1, poGDS->fp);
     175         764 :                 if (nWordSize != 1 && nWordSize != 2 && nWordSize != 4)
     176             :                 {
     177           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     178             :                              "Unexpected word size : %d", (int)nWordSize);
     179           0 :                     break;
     180             :                 }
     181             : 
     182             :                 GInt32 nVal;
     183         764 :                 VSIFReadL(&nVal, 4, 1, poGDS->fp);
     184         764 :                 CPL_LSBPTR32(&nVal);
     185         764 :                 const size_t nToRead =
     186         764 :                     static_cast<size_t>(nWordSize * (nTileWidth - 1));
     187         764 :                 const size_t nRead = VSIFReadL(pabyData, 1, nToRead, poGDS->fp);
     188         764 :                 if (nRead != nToRead)
     189             :                 {
     190           0 :                     CPLError(CE_Failure, CPLE_FileIO,
     191             :                              "File too short: got %d, expected %d",
     192             :                              static_cast<int>(nRead),
     193             :                              static_cast<int>(nToRead));
     194           0 :                     CPLFree(pabyData);
     195           0 :                     return CE_Failure;
     196             :                 }
     197             : #if defined(CPL_MSB)
     198             :                 if (nWordSize > 1)
     199             :                     GDALSwapWords(pabyData, nWordSize, nTileWidth - 1,
     200             :                                   nWordSize);
     201             : #endif
     202             : 
     203         764 :                 double dfVal = nVal * (double)fScale + fOff;
     204         764 :                 if (dfVal > std::numeric_limits<float>::max())
     205           0 :                     dfVal = std::numeric_limits<float>::max();
     206         764 :                 else if (dfVal < std::numeric_limits<float>::min())
     207         402 :                     dfVal = std::numeric_limits<float>::min();
     208         764 :                 pafBlockData[nxoff * nBlockXSize + j * nRasterXSize + 0] =
     209         764 :                     static_cast<float>(dfVal);
     210      111684 :                 for (int i = 1; i < nTileWidth; i++)
     211             :                 {
     212             :                     int nInc;
     213      110920 :                     if (nWordSize == 1)
     214       38920 :                         nInc = ((signed char *)pabyData)[i - 1];
     215       72000 :                     else if (nWordSize == 2)
     216       72000 :                         nInc = ((GInt16 *)pabyData)[i - 1];
     217             :                     else
     218           0 :                         nInc = ((GInt32 *)pabyData)[i - 1];
     219      110920 :                     if ((nInc >= 0 && nVal > INT_MAX - nInc) ||
     220      110920 :                         (nInc == INT_MIN && nVal < 0) ||
     221       16066 :                         (nInc < 0 && nVal < INT_MIN - nInc))
     222             :                     {
     223           0 :                         CPLError(CE_Failure, CPLE_FileIO, "int32 overflow");
     224           0 :                         CPLFree(pabyData);
     225           0 :                         return CE_Failure;
     226             :                     }
     227      110920 :                     nVal += nInc;
     228      110920 :                     dfVal = nVal * (double)fScale + fOff;
     229      110920 :                     if (dfVal > std::numeric_limits<float>::max())
     230           0 :                         dfVal = std::numeric_limits<float>::max();
     231      110920 :                     else if (dfVal < std::numeric_limits<float>::min())
     232       23778 :                         dfVal = std::numeric_limits<float>::min();
     233      110920 :                     pafBlockData[nxoff * nBlockXSize + j * nRasterXSize + i] =
     234      110920 :                         static_cast<float>(dfVal);
     235             :                 }
     236             :             }
     237             :         }
     238             : 
     239          10 :         CPLFree(pabyData);
     240             :     }
     241             : 
     242             :     const int nTileWidth =
     243         764 :         std::min(nBlockXSize, nRasterXSize - nBlockXOff * nBlockXSize);
     244         764 :     memcpy(pImage,
     245         764 :            pafBlockData + nBlockXOff * nBlockXSize + nYOffInTile * nRasterXSize,
     246         764 :            nTileWidth * sizeof(float));
     247             : 
     248         764 :     return CE_None;
     249             : }
     250             : 
     251             : /************************************************************************/
     252             : /*                            ~HF2Dataset()                            */
     253             : /************************************************************************/
     254             : 
     255          23 : HF2Dataset::HF2Dataset()
     256             :     : fp(nullptr), panBlockOffset(nullptr), nTileSize(0),
     257          23 :       bHasLoaderBlockMap(FALSE)
     258             : {
     259          23 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     260          23 :     adfGeoTransform[0] = 0;
     261          23 :     adfGeoTransform[1] = 1;
     262          23 :     adfGeoTransform[2] = 0;
     263          23 :     adfGeoTransform[3] = 0;
     264          23 :     adfGeoTransform[4] = 0;
     265          23 :     adfGeoTransform[5] = 1;
     266          23 : }
     267             : 
     268             : /************************************************************************/
     269             : /*                            ~HF2Dataset()                            */
     270             : /************************************************************************/
     271             : 
     272          46 : HF2Dataset::~HF2Dataset()
     273             : 
     274             : {
     275          23 :     FlushCache(true);
     276          23 :     CPLFree(panBlockOffset);
     277          23 :     if (fp)
     278          23 :         VSIFCloseL(fp);
     279          46 : }
     280             : 
     281             : /************************************************************************/
     282             : /*                            LoadBlockMap()                            */
     283             : /************************************************************************/
     284             : 
     285         764 : int HF2Dataset::LoadBlockMap()
     286             : {
     287         764 :     if (bHasLoaderBlockMap)
     288         756 :         return panBlockOffset != nullptr;
     289             : 
     290           8 :     bHasLoaderBlockMap = TRUE;
     291             : 
     292           8 :     const int nXBlocks = (nRasterXSize + nTileSize - 1) / nTileSize;
     293           8 :     const int nYBlocks = (nRasterYSize + nTileSize - 1) / nTileSize;
     294           8 :     if (nXBlocks * nYBlocks > 1000000)
     295             :     {
     296           0 :         vsi_l_offset nCurOff = VSIFTellL(fp);
     297           0 :         VSIFSeekL(fp, 0, SEEK_END);
     298           0 :         vsi_l_offset nSize = VSIFTellL(fp);
     299           0 :         VSIFSeekL(fp, nCurOff, SEEK_SET);
     300             :         // Check that the file is big enough to have 8 bytes per block
     301           0 :         if (static_cast<vsi_l_offset>(nXBlocks) * nYBlocks > nSize / 8)
     302             :         {
     303           0 :             return FALSE;
     304             :         }
     305             :     }
     306           8 :     panBlockOffset =
     307           8 :         (vsi_l_offset *)VSIMalloc3(sizeof(vsi_l_offset), nXBlocks, nYBlocks);
     308           8 :     if (panBlockOffset == nullptr)
     309             :     {
     310           0 :         return FALSE;
     311             :     }
     312          18 :     for (int j = 0; j < nYBlocks; j++)
     313             :     {
     314          24 :         for (int i = 0; i < nXBlocks; i++)
     315             :         {
     316          14 :             vsi_l_offset nOff = VSIFTellL(fp);
     317          14 :             panBlockOffset[j * nXBlocks + i] = nOff;
     318             :             // VSIFSeekL(fp, 4 + 4, SEEK_CUR);
     319             :             float fScale, fOff;
     320          14 :             VSIFReadL(&fScale, 4, 1, fp);
     321          14 :             VSIFReadL(&fOff, 4, 1, fp);
     322          14 :             CPL_LSBPTR32(&fScale);
     323          14 :             CPL_LSBPTR32(&fOff);
     324             :             // printf("fScale = %f, fOff = %f\n", fScale, fOff);
     325          14 :             const int nCols = std::min(nTileSize, nRasterXSize - nTileSize * i);
     326             :             const int nLines =
     327          14 :                 std::min(nTileSize, nRasterYSize - nTileSize * j);
     328         778 :             for (int k = 0; k < nLines; k++)
     329             :             {
     330             :                 GByte nWordSize;
     331         764 :                 if (VSIFReadL(&nWordSize, 1, 1, fp) != 1)
     332             :                 {
     333           0 :                     CPLError(CE_Failure, CPLE_FileIO, "File too short");
     334           0 :                     VSIFree(panBlockOffset);
     335           0 :                     panBlockOffset = nullptr;
     336           0 :                     return FALSE;
     337             :                 }
     338             :                 // printf("nWordSize=%d\n", nWordSize);
     339         764 :                 if (nWordSize == 1 || nWordSize == 2 || nWordSize == 4)
     340         764 :                     VSIFSeekL(
     341             :                         fp,
     342         764 :                         static_cast<vsi_l_offset>(4 + nWordSize * (nCols - 1)),
     343             :                         SEEK_CUR);
     344             :                 else
     345             :                 {
     346           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     347             :                              "Got unexpected byte depth (%d) for block (%d, "
     348             :                              "%d) line %d",
     349             :                              (int)nWordSize, i, j, k);
     350           0 :                     VSIFree(panBlockOffset);
     351           0 :                     panBlockOffset = nullptr;
     352           0 :                     return FALSE;
     353             :                 }
     354             :             }
     355             :         }
     356             :     }
     357             : 
     358           8 :     return TRUE;
     359             : }
     360             : 
     361             : /************************************************************************/
     362             : /*                          GetSpatialRef()                             */
     363             : /************************************************************************/
     364             : 
     365          15 : const OGRSpatialReference *HF2Dataset::GetSpatialRef() const
     366             : 
     367             : {
     368          15 :     if (!m_oSRS.IsEmpty())
     369          15 :         return &m_oSRS;
     370           0 :     return GDALPamDataset::GetSpatialRef();
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                             Identify()                               */
     375             : /************************************************************************/
     376             : 
     377       50703 : int HF2Dataset::Identify(GDALOpenInfo *poOpenInfo)
     378             : {
     379             : 
     380       50703 :     GDALOpenInfo *poOpenInfoToDelete = nullptr;
     381             :     /*  GZipped .hf2 files are common, so automagically open them */
     382             :     /*  if the /vsigzip/ has not been explicitly passed */
     383      101402 :     CPLString osFilename;  // keep in that scope
     384       50697 :     if ((EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hfz") ||
     385       50695 :          (strlen(poOpenInfo->pszFilename) > 6 &&
     386       49838 :           EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6,
     387      101406 :                 "hf2.gz"))) &&
     388           8 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
     389             :     {
     390           7 :         osFilename = "/vsigzip/";
     391           7 :         osFilename += poOpenInfo->pszFilename;
     392           7 :         poOpenInfo = poOpenInfoToDelete = new GDALOpenInfo(
     393           7 :             osFilename.c_str(), GA_ReadOnly, poOpenInfo->GetSiblingFiles());
     394             :     }
     395             : 
     396       50703 :     if (poOpenInfo->nHeaderBytes < 28)
     397             :     {
     398       47847 :         delete poOpenInfoToDelete;
     399       47843 :         return FALSE;
     400             :     }
     401             : 
     402        2856 :     if (memcmp(poOpenInfo->pabyHeader, "HF2\0\0\0\0", 6) != 0)
     403             :     {
     404        2825 :         delete poOpenInfoToDelete;
     405        2825 :         return FALSE;
     406             :     }
     407             : 
     408          31 :     delete poOpenInfoToDelete;
     409          31 :     return TRUE;
     410             : }
     411             : 
     412             : /************************************************************************/
     413             : /*                                Open()                                */
     414             : /************************************************************************/
     415             : 
     416          23 : GDALDataset *HF2Dataset::Open(GDALOpenInfo *poOpenInfo)
     417             : 
     418             : {
     419          46 :     CPLString osOriginalFilename(poOpenInfo->pszFilename);
     420             : 
     421          23 :     if (!Identify(poOpenInfo))
     422           0 :         return nullptr;
     423             : 
     424          23 :     GDALOpenInfo *poOpenInfoToDelete = nullptr;
     425             :     /*  GZipped .hf2 files are common, so automagically open them */
     426             :     /*  if the /vsigzip/ has not been explicitly passed */
     427          46 :     CPLString osFilename(poOpenInfo->pszFilename);
     428          23 :     if ((EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hfz") ||
     429          20 :          (strlen(poOpenInfo->pszFilename) > 6 &&
     430          20 :           EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6,
     431          46 :                 "hf2.gz"))) &&
     432           3 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
     433             :     {
     434           2 :         osFilename = "/vsigzip/";
     435           2 :         osFilename += poOpenInfo->pszFilename;
     436           2 :         poOpenInfo = poOpenInfoToDelete = new GDALOpenInfo(
     437           2 :             osFilename.c_str(), GA_ReadOnly, poOpenInfo->GetSiblingFiles());
     438             :     }
     439             : 
     440             :     /* -------------------------------------------------------------------- */
     441             :     /*      Parse header                                                    */
     442             :     /* -------------------------------------------------------------------- */
     443             : 
     444             :     int nXSize, nYSize;
     445          23 :     memcpy(&nXSize, poOpenInfo->pabyHeader + 6, 4);
     446          23 :     CPL_LSBPTR32(&nXSize);
     447          23 :     memcpy(&nYSize, poOpenInfo->pabyHeader + 10, 4);
     448          23 :     CPL_LSBPTR32(&nYSize);
     449             : 
     450             :     GUInt16 nTileSize;
     451          23 :     memcpy(&nTileSize, poOpenInfo->pabyHeader + 14, 2);
     452          23 :     CPL_LSBPTR16(&nTileSize);
     453             : 
     454             :     float fVertPres, fHorizScale;
     455          23 :     memcpy(&fVertPres, poOpenInfo->pabyHeader + 16, 4);
     456          23 :     CPL_LSBPTR32(&fVertPres);
     457          23 :     memcpy(&fHorizScale, poOpenInfo->pabyHeader + 20, 4);
     458          23 :     CPL_LSBPTR32(&fHorizScale);
     459             : 
     460             :     GUInt32 nExtendedHeaderLen;
     461          23 :     memcpy(&nExtendedHeaderLen, poOpenInfo->pabyHeader + 24, 4);
     462          23 :     CPL_LSBPTR32(&nExtendedHeaderLen);
     463             : 
     464          23 :     delete poOpenInfoToDelete;
     465          23 :     poOpenInfoToDelete = nullptr;
     466             : 
     467          23 :     if (nTileSize < 8)
     468           0 :         return nullptr;
     469          23 :     if (nXSize <= 0 || nXSize > INT_MAX - nTileSize || nYSize <= 0 ||
     470          23 :         nYSize > INT_MAX - nTileSize)
     471           0 :         return nullptr;
     472             :     /* To avoid later potential int overflows */
     473          23 :     if (nExtendedHeaderLen > 1024 * 65536)
     474           0 :         return nullptr;
     475             : 
     476          23 :     if (!GDALCheckDatasetDimensions(nXSize, nYSize))
     477             :     {
     478           0 :         return nullptr;
     479             :     }
     480          23 :     const int nXBlocks = (nXSize + nTileSize - 1) / nTileSize;
     481          23 :     const int nYBlocks = (nYSize + nTileSize - 1) / nTileSize;
     482          23 :     if (nXBlocks > INT_MAX / nYBlocks)
     483             :     {
     484           0 :         return nullptr;
     485             :     }
     486             : 
     487             :     /* -------------------------------------------------------------------- */
     488             :     /*      Parse extended blocks                                           */
     489             :     /* -------------------------------------------------------------------- */
     490             : 
     491          23 :     VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "rb");
     492          23 :     if (fp == nullptr)
     493           0 :         return nullptr;
     494             : 
     495          23 :     VSIFSeekL(fp, 28, SEEK_SET);
     496             : 
     497          23 :     int bHasExtent = FALSE;
     498          23 :     double dfMinX = 0.0;
     499          23 :     double dfMaxX = 0.0;
     500          23 :     double dfMinY = 0.0;
     501          23 :     double dfMaxY = 0.0;
     502          23 :     int bHasUTMZone = FALSE;
     503          23 :     GInt16 nUTMZone = 0;
     504          23 :     int bHasEPSGDatumCode = FALSE;
     505          23 :     GInt16 nEPSGDatumCode = 0;
     506          23 :     int bHasEPSGCode = FALSE;
     507          23 :     GInt16 nEPSGCode = 0;
     508          23 :     int bHasRelativePrecision = FALSE;
     509          23 :     float fRelativePrecision = 0.0f;
     510          23 :     char szApplicationName[256] = {0};
     511             : 
     512          23 :     GUInt32 nExtendedHeaderOff = 0;
     513          84 :     while (nExtendedHeaderOff < nExtendedHeaderLen)
     514             :     {
     515             :         char pabyBlockHeader[24];
     516          61 :         VSIFReadL(pabyBlockHeader, 24, 1, fp);
     517             : 
     518             :         char szBlockName[16 + 1];
     519          61 :         memcpy(szBlockName, pabyBlockHeader + 4, 16);
     520          61 :         szBlockName[16] = 0;
     521             :         GUInt32 nBlockSize;
     522          61 :         memcpy(&nBlockSize, pabyBlockHeader + 20, 4);
     523          61 :         CPL_LSBPTR32(&nBlockSize);
     524          61 :         if (nBlockSize > 65536)
     525           0 :             break;
     526             : 
     527          61 :         nExtendedHeaderOff += 24 + nBlockSize;
     528             : 
     529          61 :         if (strcmp(szBlockName, "georef-extents") == 0 && nBlockSize == 34)
     530             :         {
     531             :             char pabyBlockData[34];
     532          23 :             VSIFReadL(pabyBlockData, 34, 1, fp);
     533             : 
     534          23 :             memcpy(&dfMinX, pabyBlockData + 2, 8);
     535          23 :             CPL_LSBPTR64(&dfMinX);
     536          23 :             memcpy(&dfMaxX, pabyBlockData + 2 + 8, 8);
     537          23 :             CPL_LSBPTR64(&dfMaxX);
     538          23 :             memcpy(&dfMinY, pabyBlockData + 2 + 8 + 8, 8);
     539          23 :             CPL_LSBPTR64(&dfMinY);
     540          23 :             memcpy(&dfMaxY, pabyBlockData + 2 + 8 + 8 + 8, 8);
     541          23 :             CPL_LSBPTR64(&dfMaxY);
     542             : 
     543          23 :             bHasExtent = TRUE;
     544             :         }
     545          38 :         else if (strcmp(szBlockName, "georef-utm") == 0 && nBlockSize == 2)
     546             :         {
     547           9 :             VSIFReadL(&nUTMZone, 2, 1, fp);
     548           9 :             CPL_LSBPTR16(&nUTMZone);
     549           9 :             CPLDebug("HF2", "UTM Zone = %d", nUTMZone);
     550             : 
     551           9 :             bHasUTMZone = TRUE;
     552             :         }
     553          29 :         else if (strcmp(szBlockName, "georef-datum") == 0 && nBlockSize == 2)
     554             :         {
     555          23 :             VSIFReadL(&nEPSGDatumCode, 2, 1, fp);
     556          23 :             CPL_LSBPTR16(&nEPSGDatumCode);
     557          23 :             CPLDebug("HF2", "EPSG Datum Code = %d", nEPSGDatumCode);
     558             : 
     559          23 :             bHasEPSGDatumCode = TRUE;
     560             :         }
     561           6 :         else if (strcmp(szBlockName, "georef-epsg-prj") == 0 && nBlockSize == 2)
     562             :         {
     563           6 :             VSIFReadL(&nEPSGCode, 2, 1, fp);
     564           6 :             CPL_LSBPTR16(&nEPSGCode);
     565           6 :             CPLDebug("HF2", "EPSG Code = %d", nEPSGCode);
     566             : 
     567           6 :             bHasEPSGCode = TRUE;
     568             :         }
     569           0 :         else if (strcmp(szBlockName, "precis-rel") == 0 && nBlockSize == 4)
     570             :         {
     571           0 :             VSIFReadL(&fRelativePrecision, 4, 1, fp);
     572           0 :             CPL_LSBPTR32(&fRelativePrecision);
     573             : 
     574           0 :             bHasRelativePrecision = TRUE;
     575             :         }
     576           0 :         else if (strcmp(szBlockName, "app-name") == 0 &&
     577           0 :                  nBlockSize < sizeof(szApplicationName))
     578             :         {
     579           0 :             VSIFReadL(szApplicationName, nBlockSize, 1, fp);
     580           0 :             szApplicationName[nBlockSize] = 0;
     581             :         }
     582             :         else
     583             :         {
     584           0 :             CPLDebug("HF2", "Skipping block %s", szBlockName);
     585           0 :             VSIFSeekL(fp, nBlockSize, SEEK_CUR);
     586             :         }
     587             :     }
     588             : 
     589             :     /* -------------------------------------------------------------------- */
     590             :     /*      Create a corresponding GDALDataset.                             */
     591             :     /* -------------------------------------------------------------------- */
     592          23 :     HF2Dataset *poDS = new HF2Dataset();
     593          23 :     poDS->fp = fp;
     594          23 :     poDS->nRasterXSize = nXSize;
     595          23 :     poDS->nRasterYSize = nYSize;
     596          23 :     poDS->nTileSize = nTileSize;
     597          23 :     CPLDebug("HF2", "nXSize = %d, nYSize = %d, nTileSize = %d", nXSize, nYSize,
     598             :              nTileSize);
     599          23 :     if (bHasExtent)
     600             :     {
     601          23 :         poDS->adfGeoTransform[0] = dfMinX;
     602          23 :         poDS->adfGeoTransform[3] = dfMaxY;
     603          23 :         poDS->adfGeoTransform[1] = (dfMaxX - dfMinX) / nXSize;
     604          23 :         poDS->adfGeoTransform[5] = -(dfMaxY - dfMinY) / nYSize;
     605             :     }
     606             :     else
     607             :     {
     608           0 :         poDS->adfGeoTransform[1] = fHorizScale;
     609           0 :         poDS->adfGeoTransform[5] = fHorizScale;
     610             :     }
     611             : 
     612          23 :     if (bHasEPSGCode)
     613             :     {
     614           6 :         poDS->m_oSRS.importFromEPSG(nEPSGCode);
     615             :     }
     616             :     else
     617             :     {
     618          17 :         bool bHasSRS = false;
     619          34 :         OGRSpatialReference oSRS;
     620          17 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     621          17 :         oSRS.SetGeogCS("unknown", "unknown", "unknown", SRS_WGS84_SEMIMAJOR,
     622             :                        SRS_WGS84_INVFLATTENING);
     623          17 :         if (bHasEPSGDatumCode)
     624             :         {
     625          17 :             if (nEPSGDatumCode == 23 || nEPSGDatumCode == 6326)
     626             :             {
     627          14 :                 bHasSRS = true;
     628          14 :                 oSRS.SetWellKnownGeogCS("WGS84");
     629             :             }
     630           3 :             else if (nEPSGDatumCode >= 6000)
     631             :             {
     632             :                 char szName[32];
     633           3 :                 snprintf(szName, sizeof(szName), "EPSG:%d",
     634           3 :                          nEPSGDatumCode - 2000);
     635           3 :                 oSRS.SetWellKnownGeogCS(szName);
     636           3 :                 bHasSRS = true;
     637             :             }
     638             :         }
     639             : 
     640          17 :         if (bHasUTMZone && std::abs(nUTMZone) >= 1 && std::abs(nUTMZone) <= 60)
     641             :         {
     642           3 :             bHasSRS = true;
     643           3 :             oSRS.SetUTM(std::abs(static_cast<int>(nUTMZone)), nUTMZone > 0);
     644             :         }
     645          17 :         if (bHasSRS)
     646          17 :             poDS->m_oSRS = std::move(oSRS);
     647             :     }
     648             : 
     649             :     /* -------------------------------------------------------------------- */
     650             :     /*      Create band information objects.                                */
     651             :     /* -------------------------------------------------------------------- */
     652          23 :     poDS->nBands = 1;
     653          46 :     for (int i = 0; i < poDS->nBands; i++)
     654             :     {
     655          23 :         poDS->SetBand(i + 1, new HF2RasterBand(poDS, i + 1, GDT_Float32));
     656          23 :         poDS->GetRasterBand(i + 1)->SetUnitType("m");
     657             :     }
     658             : 
     659          23 :     if (szApplicationName[0] != '\0')
     660           0 :         poDS->SetMetadataItem("APPLICATION_NAME", szApplicationName);
     661          23 :     poDS->SetMetadataItem("VERTICAL_PRECISION",
     662          46 :                           CPLString().Printf("%f", fVertPres));
     663          23 :     if (bHasRelativePrecision)
     664           0 :         poDS->SetMetadataItem("RELATIVE_VERTICAL_PRECISION",
     665           0 :                               CPLString().Printf("%f", fRelativePrecision));
     666             : 
     667             :     /* -------------------------------------------------------------------- */
     668             :     /*      Initialize any PAM information.                                 */
     669             :     /* -------------------------------------------------------------------- */
     670          23 :     poDS->SetDescription(osOriginalFilename.c_str());
     671          23 :     poDS->TryLoadXML();
     672             : 
     673             :     /* -------------------------------------------------------------------- */
     674             :     /*      Support overviews.                                              */
     675             :     /* -------------------------------------------------------------------- */
     676          23 :     poDS->oOvManager.Initialize(poDS, osOriginalFilename.c_str());
     677          23 :     return poDS;
     678             : }
     679             : 
     680             : /************************************************************************/
     681             : /*                          GetGeoTransform()                           */
     682             : /************************************************************************/
     683             : 
     684          16 : CPLErr HF2Dataset::GetGeoTransform(double *padfTransform)
     685             : 
     686             : {
     687          16 :     memcpy(padfTransform, adfGeoTransform, 6 * sizeof(double));
     688             : 
     689          16 :     return CE_None;
     690             : }
     691             : 
     692       36065 : static void WriteShort(VSILFILE *fp, GInt16 val)
     693             : {
     694       36065 :     CPL_LSBPTR16(&val);
     695       36065 :     VSIFWriteL(&val, 2, 1, fp);
     696       36065 : }
     697             : 
     698         572 : static void WriteInt(VSILFILE *fp, GInt32 val)
     699             : {
     700         572 :     CPL_LSBPTR32(&val);
     701         572 :     VSIFWriteL(&val, 4, 1, fp);
     702         572 : }
     703             : 
     704          66 : static void WriteFloat(VSILFILE *fp, float val)
     705             : {
     706          66 :     CPL_LSBPTR32(&val);
     707          66 :     VSIFWriteL(&val, 4, 1, fp);
     708          66 : }
     709             : 
     710          60 : static void WriteDouble(VSILFILE *fp, double val)
     711             : {
     712          60 :     CPL_LSBPTR64(&val);
     713          60 :     VSIFWriteL(&val, 8, 1, fp);
     714          60 : }
     715             : 
     716             : /************************************************************************/
     717             : /*                             CreateCopy()                             */
     718             : /************************************************************************/
     719             : 
     720          23 : GDALDataset *HF2Dataset::CreateCopy(const char *pszFilename,
     721             :                                     GDALDataset *poSrcDS, int bStrict,
     722             :                                     char **papszOptions,
     723             :                                     GDALProgressFunc pfnProgress,
     724             :                                     void *pProgressData)
     725             : {
     726             :     /* -------------------------------------------------------------------- */
     727             :     /*      Some some rudimentary checks                                    */
     728             :     /* -------------------------------------------------------------------- */
     729          23 :     int nBands = poSrcDS->GetRasterCount();
     730          23 :     if (nBands == 0)
     731             :     {
     732           1 :         CPLError(
     733             :             CE_Failure, CPLE_NotSupported,
     734             :             "HF2 driver does not support source dataset with zero band.\n");
     735           1 :         return nullptr;
     736             :     }
     737             : 
     738          22 :     if (nBands != 1)
     739             :     {
     740           4 :         CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
     741             :                  "HF2 driver only uses the first band of the dataset.\n");
     742           4 :         if (bStrict)
     743           4 :             return nullptr;
     744             :     }
     745             : 
     746          18 :     if (pfnProgress && !pfnProgress(0.0, nullptr, pProgressData))
     747           0 :         return nullptr;
     748             : 
     749             :     /* -------------------------------------------------------------------- */
     750             :     /*      Get source dataset info                                         */
     751             :     /* -------------------------------------------------------------------- */
     752             : 
     753          18 :     const int nXSize = poSrcDS->GetRasterXSize();
     754          18 :     const int nYSize = poSrcDS->GetRasterYSize();
     755             :     double adfGeoTransform[6];
     756          18 :     poSrcDS->GetGeoTransform(adfGeoTransform);
     757          18 :     int bHasGeoTransform =
     758          18 :         !(adfGeoTransform[0] == 0 && adfGeoTransform[1] == 1 &&
     759           0 :           adfGeoTransform[2] == 0 && adfGeoTransform[3] == 0 &&
     760           0 :           adfGeoTransform[4] == 0 && adfGeoTransform[5] == 1);
     761          18 :     if (adfGeoTransform[2] != 0 || adfGeoTransform[4] != 0)
     762             :     {
     763           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     764             :                  "HF2 driver does not support CreateCopy() from skewed or "
     765             :                  "rotated dataset.\n");
     766           0 :         return nullptr;
     767             :     }
     768             : 
     769          18 :     GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
     770             :     GDALDataType eReqDT;
     771          18 :     float fVertPres = (float)0.01;
     772          18 :     if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16)
     773             :     {
     774           8 :         fVertPres = 1;
     775           8 :         eReqDT = GDT_Int16;
     776             :     }
     777             :     else
     778          10 :         eReqDT = GDT_Float32;
     779             : 
     780             :     /* -------------------------------------------------------------------- */
     781             :     /*      Read creation options                                           */
     782             :     /* -------------------------------------------------------------------- */
     783          18 :     const char *pszCompressed = CSLFetchNameValue(papszOptions, "COMPRESS");
     784          18 :     bool bCompress = false;
     785          18 :     if (pszCompressed)
     786           1 :         bCompress = CPLTestBool(pszCompressed);
     787             : 
     788             :     const char *pszVerticalPrecision =
     789          18 :         CSLFetchNameValue(papszOptions, "VERTICAL_PRECISION");
     790          18 :     if (pszVerticalPrecision)
     791             :     {
     792           0 :         fVertPres = (float)CPLAtofM(pszVerticalPrecision);
     793           0 :         if (fVertPres <= 0)
     794             :         {
     795           0 :             CPLError(
     796             :                 CE_Warning, CPLE_AppDefined,
     797             :                 "Unsupported value for VERTICAL_PRECISION. Defaulting to 0.01");
     798           0 :             fVertPres = (float)0.01;
     799             :         }
     800           0 :         if (eReqDT == GDT_Int16 && fVertPres > 1)
     801           0 :             eReqDT = GDT_Float32;
     802             :     }
     803             : 
     804          18 :     const char *pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
     805          18 :     int nTileSize = 256;
     806          18 :     if (pszBlockSize)
     807             :     {
     808           1 :         nTileSize = atoi(pszBlockSize);
     809           1 :         if (nTileSize < 8 || nTileSize > 4096)
     810             :         {
     811           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     812             :                      "Unsupported value for BLOCKSIZE. Defaulting to 256");
     813           0 :             nTileSize = 256;
     814             :         }
     815             :     }
     816             : 
     817             :     /* -------------------------------------------------------------------- */
     818             :     /*      Parse source dataset georeferencing info                        */
     819             :     /* -------------------------------------------------------------------- */
     820             : 
     821          18 :     int nExtendedHeaderLen = 0;
     822          18 :     if (bHasGeoTransform)
     823          18 :         nExtendedHeaderLen += 58;
     824          18 :     const char *pszProjectionRef = poSrcDS->GetProjectionRef();
     825          18 :     int nDatumCode = -2;
     826          18 :     int nUTMZone = 0;
     827          18 :     int bNorth = FALSE;
     828          18 :     int nEPSGCode = 0;
     829          18 :     int nExtentUnits = 1;
     830          18 :     if (pszProjectionRef != nullptr && pszProjectionRef[0] != '\0')
     831             :     {
     832          36 :         OGRSpatialReference oSRS;
     833          18 :         if (oSRS.importFromWkt(pszProjectionRef) == OGRERR_NONE)
     834             :         {
     835          18 :             const char *pszValue = nullptr;
     836          22 :             if (oSRS.GetAuthorityName("GEOGCS|DATUM") != nullptr &&
     837           4 :                 EQUAL(oSRS.GetAuthorityName("GEOGCS|DATUM"), "EPSG"))
     838           4 :                 nDatumCode = atoi(oSRS.GetAuthorityCode("GEOGCS|DATUM"));
     839          14 :             else if ((pszValue = oSRS.GetAttrValue("GEOGCS|DATUM")) != nullptr)
     840             :             {
     841          14 :                 if (strstr(pszValue, "WGS") && strstr(pszValue, "84"))
     842          14 :                     nDatumCode = 6326;
     843             :             }
     844             : 
     845          18 :             nUTMZone = oSRS.GetUTMZone(&bNorth);
     846             :         }
     847          20 :         if (oSRS.GetAuthorityName("PROJCS") != nullptr &&
     848           2 :             EQUAL(oSRS.GetAuthorityName("PROJCS"), "EPSG"))
     849           2 :             nEPSGCode = atoi(oSRS.GetAuthorityCode("PROJCS"));
     850             : 
     851          18 :         if (oSRS.IsGeographic())
     852             :         {
     853          15 :             nExtentUnits = 0;
     854             :         }
     855             :         else
     856             :         {
     857           3 :             const double dfLinear = oSRS.GetLinearUnits();
     858             : 
     859           3 :             if (std::abs(dfLinear - 0.3048) < 0.0000001)
     860           0 :                 nExtentUnits = 2;
     861           3 :             else if (std::abs(dfLinear - CPLAtof(SRS_UL_US_FOOT_CONV)) <
     862             :                      0.00000001)
     863           0 :                 nExtentUnits = 3;
     864             :             else
     865           3 :                 nExtentUnits = 1;
     866             :         }
     867             :     }
     868          18 :     if (nDatumCode != -2)
     869          18 :         nExtendedHeaderLen += 26;
     870          18 :     if (nUTMZone != 0)
     871           3 :         nExtendedHeaderLen += 26;
     872          18 :     if (nEPSGCode)
     873           2 :         nExtendedHeaderLen += 26;
     874             : 
     875             :     /* -------------------------------------------------------------------- */
     876             :     /*      Create target file                                              */
     877             :     /* -------------------------------------------------------------------- */
     878             : 
     879          36 :     CPLString osFilename;
     880          18 :     if (bCompress)
     881             :     {
     882           1 :         osFilename = "/vsigzip/";
     883           1 :         osFilename += pszFilename;
     884             :     }
     885             :     else
     886          17 :         osFilename = pszFilename;
     887          18 :     VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "wb");
     888          18 :     if (fp == nullptr)
     889             :     {
     890           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszFilename);
     891           3 :         return nullptr;
     892             :     }
     893             : 
     894             :     /* -------------------------------------------------------------------- */
     895             :     /*      Write header                                                    */
     896             :     /* -------------------------------------------------------------------- */
     897             : 
     898          15 :     VSIFWriteL("HF2\0", 4, 1, fp);
     899          15 :     WriteShort(fp, 0);
     900          15 :     WriteInt(fp, nXSize);
     901          15 :     WriteInt(fp, nYSize);
     902          15 :     WriteShort(fp, (GInt16)nTileSize);
     903          15 :     WriteFloat(fp, fVertPres);
     904          15 :     const float fHorizScale =
     905          15 :         (float)((fabs(adfGeoTransform[1]) + fabs(adfGeoTransform[5])) / 2);
     906          15 :     WriteFloat(fp, fHorizScale);
     907          15 :     WriteInt(fp, nExtendedHeaderLen);
     908             : 
     909             :     /* -------------------------------------------------------------------- */
     910             :     /*      Write extended header                                           */
     911             :     /* -------------------------------------------------------------------- */
     912             : 
     913             :     char szBlockName[16 + 1];
     914          15 :     if (bHasGeoTransform)
     915             :     {
     916          15 :         VSIFWriteL("bin\0", 4, 1, fp);
     917          15 :         memset(szBlockName, 0, 16 + 1);
     918          15 :         strcpy(szBlockName, "georef-extents");
     919          15 :         VSIFWriteL(szBlockName, 16, 1, fp);
     920          15 :         WriteInt(fp, 34);
     921          15 :         WriteShort(fp, (GInt16)nExtentUnits);
     922          15 :         WriteDouble(fp, adfGeoTransform[0]);
     923          15 :         WriteDouble(fp, adfGeoTransform[0] + nXSize * adfGeoTransform[1]);
     924          15 :         WriteDouble(fp, adfGeoTransform[3] + nYSize * adfGeoTransform[5]);
     925          15 :         WriteDouble(fp, adfGeoTransform[3]);
     926             :     }
     927          15 :     if (nUTMZone != 0)
     928             :     {
     929           3 :         VSIFWriteL("bin\0", 4, 1, fp);
     930           3 :         memset(szBlockName, 0, 16 + 1);
     931           3 :         strcpy(szBlockName, "georef-utm");
     932           3 :         VSIFWriteL(szBlockName, 16, 1, fp);
     933           3 :         WriteInt(fp, 2);
     934           3 :         WriteShort(fp, (GInt16)((bNorth) ? nUTMZone : -nUTMZone));
     935             :     }
     936          15 :     if (nDatumCode != -2)
     937             :     {
     938          15 :         VSIFWriteL("bin\0", 4, 1, fp);
     939          15 :         memset(szBlockName, 0, 16 + 1);
     940          15 :         strcpy(szBlockName, "georef-datum");
     941          15 :         VSIFWriteL(szBlockName, 16, 1, fp);
     942          15 :         WriteInt(fp, 2);
     943          15 :         WriteShort(fp, (GInt16)nDatumCode);
     944             :     }
     945          15 :     if (nEPSGCode != 0)
     946             :     {
     947           2 :         VSIFWriteL("bin\0", 4, 1, fp);
     948           2 :         memset(szBlockName, 0, 16 + 1);
     949           2 :         strcpy(szBlockName, "georef-epsg-prj");
     950           2 :         VSIFWriteL(szBlockName, 16, 1, fp);
     951           2 :         WriteInt(fp, 2);
     952           2 :         WriteShort(fp, (GInt16)nEPSGCode);
     953             :     }
     954             : 
     955             :     /* -------------------------------------------------------------------- */
     956             :     /*      Copy imagery                                                    */
     957             :     /* -------------------------------------------------------------------- */
     958          15 :     const int nXBlocks = (nXSize + nTileSize - 1) / nTileSize;
     959          15 :     const int nYBlocks = (nYSize + nTileSize - 1) / nTileSize;
     960             : 
     961          15 :     void *pTileBuffer = VSI_MALLOC3_VERBOSE(nTileSize, nTileSize,
     962             :                                             GDALGetDataTypeSizeBytes(eReqDT));
     963          15 :     if (pTileBuffer == nullptr)
     964             :     {
     965           0 :         VSIFCloseL(fp);
     966           0 :         return nullptr;
     967             :     }
     968             : 
     969          15 :     CPLErr eErr = CE_None;
     970          31 :     for (int j = 0; j < nYBlocks && eErr == CE_None; j++)
     971             :     {
     972          34 :         for (int i = 0; i < nXBlocks && eErr == CE_None; i++)
     973             :         {
     974          18 :             const int nReqXSize = std::min(nTileSize, nXSize - i * nTileSize);
     975          18 :             const int nReqYSize = std::min(nTileSize, nYSize - j * nTileSize);
     976          36 :             eErr = poSrcDS->GetRasterBand(1)->RasterIO(
     977             :                 GF_Read, i * nTileSize,
     978          18 :                 std::max(0, nYSize - (j + 1) * nTileSize), nReqXSize, nReqYSize,
     979             :                 pTileBuffer, nReqXSize, nReqYSize, eReqDT, 0, 0, nullptr);
     980          18 :             if (eErr != CE_None)
     981           0 :                 break;
     982             : 
     983          18 :             if (eReqDT == GDT_Int16)
     984             :             {
     985           8 :                 WriteFloat(fp, 1); /* scale */
     986           8 :                 WriteFloat(fp, 0); /* offset */
     987         209 :                 for (int k = 0; k < nReqYSize; k++)
     988             :                 {
     989         201 :                     int nLastVal =
     990             :                         ((short *)
     991         201 :                              pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
     992         201 :                     GByte nWordSize = 1;
     993       15641 :                     for (int l = 1; l < nReqXSize; l++)
     994             :                     {
     995       15440 :                         const int nVal =
     996             :                             ((short *)
     997       15440 :                                  pTileBuffer)[(nReqYSize - k - 1) * nReqXSize +
     998       15440 :                                               l];
     999       15440 :                         const int nDiff = nVal - nLastVal;
    1000       15440 :                         if (nDiff < -32768 || nDiff > 32767)
    1001             :                         {
    1002           0 :                             nWordSize = 4;
    1003           0 :                             break;
    1004             :                         }
    1005       15440 :                         if (nDiff < -128 || nDiff > 127)
    1006           0 :                             nWordSize = 2;
    1007       15440 :                         nLastVal = nVal;
    1008             :                     }
    1009             : 
    1010         201 :                     VSIFWriteL(&nWordSize, 1, 1, fp);
    1011         201 :                     nLastVal =
    1012             :                         ((short *)
    1013         201 :                              pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
    1014         201 :                     WriteInt(fp, nLastVal);
    1015       15641 :                     for (int l = 1; l < nReqXSize; l++)
    1016             :                     {
    1017       15440 :                         const int nVal =
    1018             :                             ((short *)
    1019       15440 :                                  pTileBuffer)[(nReqYSize - k - 1) * nReqXSize +
    1020       15440 :                                               l];
    1021       15440 :                         const int nDiff = nVal - nLastVal;
    1022       15440 :                         if (nWordSize == 1)
    1023             :                         {
    1024       15440 :                             CPLAssert(nDiff >= -128 && nDiff <= 127);
    1025       15440 :                             signed char chDiff = (signed char)nDiff;
    1026       15440 :                             VSIFWriteL(&chDiff, 1, 1, fp);
    1027             :                         }
    1028           0 :                         else if (nWordSize == 2)
    1029             :                         {
    1030           0 :                             CPLAssert(nDiff >= -32768 && nDiff <= 32767);
    1031           0 :                             WriteShort(fp, (short)nDiff);
    1032             :                         }
    1033             :                         else
    1034             :                         {
    1035           0 :                             WriteInt(fp, nDiff);
    1036             :                         }
    1037       15440 :                         nLastVal = nVal;
    1038             :                     }
    1039             :                 }
    1040             :             }
    1041             :             else
    1042             :             {
    1043          10 :                 float fMinVal = ((float *)pTileBuffer)[0];
    1044          10 :                 float fMaxVal = fMinVal;
    1045       41301 :                 for (int k = 1; k < nReqYSize * nReqXSize; k++)
    1046             :                 {
    1047       41291 :                     float fVal = ((float *)pTileBuffer)[k];
    1048       41291 :                     if (std::isnan(fVal))
    1049             :                     {
    1050           0 :                         CPLError(CE_Failure, CPLE_NotSupported,
    1051             :                                  "NaN value found");
    1052           0 :                         eErr = CE_Failure;
    1053           0 :                         break;
    1054             :                     }
    1055       41291 :                     if (fVal < fMinVal)
    1056           0 :                         fMinVal = fVal;
    1057       41291 :                     if (fVal > fMaxVal)
    1058         180 :                         fMaxVal = fVal;
    1059             :                 }
    1060          10 :                 if (eErr == CE_Failure)
    1061           0 :                     break;
    1062             : 
    1063          10 :                 float fIntRange = (fMaxVal - fMinVal) / fVertPres;
    1064          10 :                 if (fIntRange >
    1065          10 :                     static_cast<float>(std::numeric_limits<int>::max()))
    1066             :                 {
    1067           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1068             :                              "VERTICAL_PRECISION too small regarding actual "
    1069             :                              "range of values");
    1070           0 :                     eErr = CE_Failure;
    1071           0 :                     break;
    1072             :                 }
    1073          10 :                 float fScale =
    1074          10 :                     (fMinVal == fMaxVal) ? 1 : (fMaxVal - fMinVal) / fIntRange;
    1075          10 :                 if (fScale == 0.0f)
    1076             :                 {
    1077           0 :                     CPLError(CE_Failure, CPLE_NotSupported, "Scale == 0.0f");
    1078           0 :                     eErr = CE_Failure;
    1079           0 :                     break;
    1080             :                 }
    1081          10 :                 float fOffset = fMinVal;
    1082          10 :                 WriteFloat(fp, fScale);  /* scale */
    1083          10 :                 WriteFloat(fp, fOffset); /* offset */
    1084         301 :                 for (int k = 0; k < nReqYSize; k++)
    1085             :                 {
    1086         291 :                     float fLastVal =
    1087             :                         ((float *)
    1088         291 :                              pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
    1089         291 :                     float fIntLastVal = (fLastVal - fOffset) / fScale;
    1090         291 :                     CPLAssert(
    1091             :                         fIntLastVal <=
    1092             :                         static_cast<float>(std::numeric_limits<int>::max()));
    1093         291 :                     int nLastVal = (int)fIntLastVal;
    1094         291 :                     GByte nWordSize = 1;
    1095       41301 :                     for (int l = 1; l < nReqXSize; l++)
    1096             :                     {
    1097       41010 :                         float fVal =
    1098             :                             ((float *)
    1099       41010 :                                  pTileBuffer)[(nReqYSize - k - 1) * nReqXSize +
    1100       41010 :                                               l];
    1101       41010 :                         float fIntVal = (fVal - fOffset) / fScale;
    1102       41010 :                         CPLAssert(fIntVal <=
    1103             :                                   static_cast<float>(
    1104             :                                       std::numeric_limits<int>::max()));
    1105       41010 :                         const int nVal = (int)fIntVal;
    1106       41010 :                         const int nDiff = nVal - nLastVal;
    1107       41010 :                         CPLAssert((int)((GIntBig)nVal - nLastVal) == nDiff);
    1108       41010 :                         if (nDiff < -32768 || nDiff > 32767)
    1109             :                         {
    1110           0 :                             nWordSize = 4;
    1111           0 :                             break;
    1112             :                         }
    1113       41010 :                         if (nDiff < -128 || nDiff > 127)
    1114         201 :                             nWordSize = 2;
    1115       41010 :                         nLastVal = nVal;
    1116             :                     }
    1117             : 
    1118         291 :                     VSIFWriteL(&nWordSize, 1, 1, fp);
    1119         291 :                     fLastVal =
    1120             :                         ((float *)
    1121         291 :                              pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
    1122         291 :                     fIntLastVal = (fLastVal - fOffset) / fScale;
    1123         291 :                     nLastVal = (int)fIntLastVal;
    1124         291 :                     WriteInt(fp, nLastVal);
    1125       41301 :                     for (int l = 1; l < nReqXSize; l++)
    1126             :                     {
    1127       41010 :                         float fVal =
    1128             :                             ((float *)
    1129       41010 :                                  pTileBuffer)[(nReqYSize - k - 1) * nReqXSize +
    1130       41010 :                                               l];
    1131       41010 :                         float fIntVal = (fVal - fOffset) / fScale;
    1132       41010 :                         int nVal = (int)fIntVal;
    1133       41010 :                         int nDiff = nVal - nLastVal;
    1134       41010 :                         CPLAssert((int)((GIntBig)nVal - nLastVal) == nDiff);
    1135       41010 :                         if (nWordSize == 1)
    1136             :                         {
    1137        5010 :                             CPLAssert(nDiff >= -128 && nDiff <= 127);
    1138        5010 :                             signed char chDiff = (signed char)nDiff;
    1139        5010 :                             VSIFWriteL(&chDiff, 1, 1, fp);
    1140             :                         }
    1141       36000 :                         else if (nWordSize == 2)
    1142             :                         {
    1143       36000 :                             CPLAssert(nDiff >= -32768 && nDiff <= 32767);
    1144       36000 :                             WriteShort(fp, (short)nDiff);
    1145             :                         }
    1146             :                         else
    1147             :                         {
    1148           0 :                             WriteInt(fp, nDiff);
    1149             :                         }
    1150       41010 :                         nLastVal = nVal;
    1151             :                     }
    1152             :                 }
    1153             :             }
    1154             : 
    1155          36 :             if (pfnProgress && !pfnProgress((j * nXBlocks + i + 1) * 1.0 /
    1156          18 :                                                 (nXBlocks * nYBlocks),
    1157             :                                             nullptr, pProgressData))
    1158             :             {
    1159           0 :                 eErr = CE_Failure;
    1160           0 :                 break;
    1161             :             }
    1162             :         }
    1163             :     }
    1164             : 
    1165          15 :     CPLFree(pTileBuffer);
    1166             : 
    1167          15 :     VSIFCloseL(fp);
    1168             : 
    1169          15 :     if (eErr != CE_None)
    1170           0 :         return nullptr;
    1171             : 
    1172          15 :     GDALOpenInfo oOpenInfo(osFilename.c_str(), GA_ReadOnly);
    1173          15 :     HF2Dataset *poDS = reinterpret_cast<HF2Dataset *>(Open(&oOpenInfo));
    1174             : 
    1175          15 :     if (poDS)
    1176          15 :         poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    1177             : 
    1178          15 :     return poDS;
    1179             : }
    1180             : 
    1181             : /************************************************************************/
    1182             : /*                         GDALRegister_HF2()                           */
    1183             : /************************************************************************/
    1184             : 
    1185        1595 : void GDALRegister_HF2()
    1186             : 
    1187             : {
    1188        1595 :     if (GDALGetDriverByName("HF2") != nullptr)
    1189         302 :         return;
    1190             : 
    1191        1293 :     GDALDriver *poDriver = new GDALDriver();
    1192             : 
    1193        1293 :     poDriver->SetDescription("HF2");
    1194        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1195        1293 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HF2/HFZ heightfield raster");
    1196        1293 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/hf2.html");
    1197        1293 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hf2");
    1198        1293 :     poDriver->SetMetadataItem(
    1199             :         GDAL_DMD_CREATIONOPTIONLIST,
    1200             :         "<CreationOptionList>"
    1201             :         "   <Option name='VERTICAL_PRECISION' type='float' default='0.01' "
    1202             :         "description='Vertical precision.'/>"
    1203             :         "   <Option name='COMPRESS' type='boolean' default='false' "
    1204             :         "description='Set to true to produce a GZip compressed file.'/>"
    1205             :         "   <Option name='BLOCKSIZE' type='int' default='256' "
    1206             :         "description='Tile size.'/>"
    1207        1293 :         "</CreationOptionList>");
    1208             : 
    1209        1293 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1210             : 
    1211        1293 :     poDriver->pfnOpen = HF2Dataset::Open;
    1212        1293 :     poDriver->pfnIdentify = HF2Dataset::Identify;
    1213        1293 :     poDriver->pfnCreateCopy = HF2Dataset::CreateCopy;
    1214             : 
    1215        1293 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1216             : }

Generated by: LCOV version 1.14