LCOV - code coverage report
Current view: top level - frmts/hf2 - hf2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 527 626 84.2 %
Date: 2025-07-11 10:11:13 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             :     GDALGeoTransform m_gt{};
      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(GDALGeoTransform &gt) const 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 = cpl::down_cast<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 : }
     261             : 
     262             : /************************************************************************/
     263             : /*                            ~HF2Dataset()                            */
     264             : /************************************************************************/
     265             : 
     266          46 : HF2Dataset::~HF2Dataset()
     267             : 
     268             : {
     269          23 :     FlushCache(true);
     270          23 :     CPLFree(panBlockOffset);
     271          23 :     if (fp)
     272          23 :         VSIFCloseL(fp);
     273          46 : }
     274             : 
     275             : /************************************************************************/
     276             : /*                            LoadBlockMap()                            */
     277             : /************************************************************************/
     278             : 
     279         764 : int HF2Dataset::LoadBlockMap()
     280             : {
     281         764 :     if (bHasLoaderBlockMap)
     282         756 :         return panBlockOffset != nullptr;
     283             : 
     284           8 :     bHasLoaderBlockMap = TRUE;
     285             : 
     286           8 :     const int nXBlocks = DIV_ROUND_UP(nRasterXSize, nTileSize);
     287           8 :     const int nYBlocks = DIV_ROUND_UP(nRasterYSize, nTileSize);
     288           8 :     if (nXBlocks * nYBlocks > 1000000)
     289             :     {
     290           0 :         vsi_l_offset nCurOff = VSIFTellL(fp);
     291           0 :         VSIFSeekL(fp, 0, SEEK_END);
     292           0 :         vsi_l_offset nSize = VSIFTellL(fp);
     293           0 :         VSIFSeekL(fp, nCurOff, SEEK_SET);
     294             :         // Check that the file is big enough to have 8 bytes per block
     295           0 :         if (static_cast<vsi_l_offset>(nXBlocks) * nYBlocks > nSize / 8)
     296             :         {
     297           0 :             return FALSE;
     298             :         }
     299             :     }
     300           8 :     panBlockOffset =
     301           8 :         (vsi_l_offset *)VSIMalloc3(sizeof(vsi_l_offset), nXBlocks, nYBlocks);
     302           8 :     if (panBlockOffset == nullptr)
     303             :     {
     304           0 :         return FALSE;
     305             :     }
     306          18 :     for (int j = 0; j < nYBlocks; j++)
     307             :     {
     308          24 :         for (int i = 0; i < nXBlocks; i++)
     309             :         {
     310          14 :             vsi_l_offset nOff = VSIFTellL(fp);
     311          14 :             panBlockOffset[j * nXBlocks + i] = nOff;
     312             :             // VSIFSeekL(fp, 4 + 4, SEEK_CUR);
     313             :             float fScale, fOff;
     314          14 :             VSIFReadL(&fScale, 4, 1, fp);
     315          14 :             VSIFReadL(&fOff, 4, 1, fp);
     316          14 :             CPL_LSBPTR32(&fScale);
     317          14 :             CPL_LSBPTR32(&fOff);
     318             :             // printf("fScale = %f, fOff = %f\n", fScale, fOff);
     319          14 :             const int nCols = std::min(nTileSize, nRasterXSize - nTileSize * i);
     320             :             const int nLines =
     321          14 :                 std::min(nTileSize, nRasterYSize - nTileSize * j);
     322         778 :             for (int k = 0; k < nLines; k++)
     323             :             {
     324             :                 GByte nWordSize;
     325         764 :                 if (VSIFReadL(&nWordSize, 1, 1, fp) != 1)
     326             :                 {
     327           0 :                     CPLError(CE_Failure, CPLE_FileIO, "File too short");
     328           0 :                     VSIFree(panBlockOffset);
     329           0 :                     panBlockOffset = nullptr;
     330           0 :                     return FALSE;
     331             :                 }
     332             :                 // printf("nWordSize=%d\n", nWordSize);
     333         764 :                 if (nWordSize == 1 || nWordSize == 2 || nWordSize == 4)
     334         764 :                     VSIFSeekL(
     335             :                         fp,
     336         764 :                         static_cast<vsi_l_offset>(4 + nWordSize * (nCols - 1)),
     337             :                         SEEK_CUR);
     338             :                 else
     339             :                 {
     340           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     341             :                              "Got unexpected byte depth (%d) for block (%d, "
     342             :                              "%d) line %d",
     343             :                              (int)nWordSize, i, j, k);
     344           0 :                     VSIFree(panBlockOffset);
     345           0 :                     panBlockOffset = nullptr;
     346           0 :                     return FALSE;
     347             :                 }
     348             :             }
     349             :         }
     350             :     }
     351             : 
     352           8 :     return TRUE;
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*                          GetSpatialRef()                             */
     357             : /************************************************************************/
     358             : 
     359          15 : const OGRSpatialReference *HF2Dataset::GetSpatialRef() const
     360             : 
     361             : {
     362          15 :     if (!m_oSRS.IsEmpty())
     363          15 :         return &m_oSRS;
     364           0 :     return GDALPamDataset::GetSpatialRef();
     365             : }
     366             : 
     367             : /************************************************************************/
     368             : /*                             Identify()                               */
     369             : /************************************************************************/
     370             : 
     371       56901 : int HF2Dataset::Identify(GDALOpenInfo *poOpenInfo)
     372             : {
     373             : 
     374       56901 :     GDALOpenInfo *poOpenInfoToDelete = nullptr;
     375             :     /*  GZipped .hf2 files are common, so automagically open them */
     376             :     /*  if the /vsigzip/ has not been explicitly passed */
     377      113813 :     CPLString osFilename;  // keep in that scope
     378       56912 :     if ((poOpenInfo->IsExtensionEqualToCI("hfz") ||
     379       56904 :          (strlen(poOpenInfo->pszFilename) > 6 &&
     380       55663 :           EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6,
     381      113824 :                 "hf2.gz"))) &&
     382           8 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
     383             :     {
     384           7 :         osFilename = "/vsigzip/";
     385           7 :         osFilename += poOpenInfo->pszFilename;
     386           7 :         poOpenInfo = poOpenInfoToDelete = new GDALOpenInfo(
     387           7 :             osFilename.c_str(), GA_ReadOnly, poOpenInfo->GetSiblingFiles());
     388             :     }
     389             : 
     390       56912 :     if (poOpenInfo->nHeaderBytes < 28)
     391             :     {
     392       53852 :         delete poOpenInfoToDelete;
     393       53852 :         return FALSE;
     394             :     }
     395             : 
     396        3060 :     if (memcmp(poOpenInfo->pabyHeader, "HF2\0\0\0\0", 6) != 0)
     397             :     {
     398        3029 :         delete poOpenInfoToDelete;
     399        3029 :         return FALSE;
     400             :     }
     401             : 
     402          31 :     delete poOpenInfoToDelete;
     403          31 :     return TRUE;
     404             : }
     405             : 
     406             : /************************************************************************/
     407             : /*                                Open()                                */
     408             : /************************************************************************/
     409             : 
     410          23 : GDALDataset *HF2Dataset::Open(GDALOpenInfo *poOpenInfo)
     411             : 
     412             : {
     413          46 :     CPLString osOriginalFilename(poOpenInfo->pszFilename);
     414             : 
     415          23 :     if (!Identify(poOpenInfo))
     416           0 :         return nullptr;
     417             : 
     418          23 :     GDALOpenInfo *poOpenInfoToDelete = nullptr;
     419             :     /*  GZipped .hf2 files are common, so automagically open them */
     420             :     /*  if the /vsigzip/ has not been explicitly passed */
     421          46 :     CPLString osFilename(poOpenInfo->pszFilename);
     422          23 :     if ((poOpenInfo->IsExtensionEqualToCI("hfz") ||
     423          20 :          (strlen(poOpenInfo->pszFilename) > 6 &&
     424          20 :           EQUAL(poOpenInfo->pszFilename + strlen(poOpenInfo->pszFilename) - 6,
     425          46 :                 "hf2.gz"))) &&
     426           3 :         !STARTS_WITH_CI(poOpenInfo->pszFilename, "/vsigzip/"))
     427             :     {
     428           2 :         osFilename = "/vsigzip/";
     429           2 :         osFilename += poOpenInfo->pszFilename;
     430           2 :         poOpenInfo = poOpenInfoToDelete = new GDALOpenInfo(
     431           2 :             osFilename.c_str(), GA_ReadOnly, poOpenInfo->GetSiblingFiles());
     432             :     }
     433             : 
     434             :     /* -------------------------------------------------------------------- */
     435             :     /*      Parse header                                                    */
     436             :     /* -------------------------------------------------------------------- */
     437             : 
     438             :     int nXSize, nYSize;
     439          23 :     memcpy(&nXSize, poOpenInfo->pabyHeader + 6, 4);
     440          23 :     CPL_LSBPTR32(&nXSize);
     441          23 :     memcpy(&nYSize, poOpenInfo->pabyHeader + 10, 4);
     442          23 :     CPL_LSBPTR32(&nYSize);
     443             : 
     444             :     GUInt16 nTileSize;
     445          23 :     memcpy(&nTileSize, poOpenInfo->pabyHeader + 14, 2);
     446          23 :     CPL_LSBPTR16(&nTileSize);
     447             : 
     448             :     float fVertPres, fHorizScale;
     449          23 :     memcpy(&fVertPres, poOpenInfo->pabyHeader + 16, 4);
     450          23 :     CPL_LSBPTR32(&fVertPres);
     451          23 :     memcpy(&fHorizScale, poOpenInfo->pabyHeader + 20, 4);
     452          23 :     CPL_LSBPTR32(&fHorizScale);
     453             : 
     454             :     GUInt32 nExtendedHeaderLen;
     455          23 :     memcpy(&nExtendedHeaderLen, poOpenInfo->pabyHeader + 24, 4);
     456          23 :     CPL_LSBPTR32(&nExtendedHeaderLen);
     457             : 
     458          23 :     delete poOpenInfoToDelete;
     459          23 :     poOpenInfoToDelete = nullptr;
     460             : 
     461          23 :     if (nTileSize < 8)
     462           0 :         return nullptr;
     463          23 :     if (nXSize <= 0 || nXSize > INT_MAX - nTileSize || nYSize <= 0 ||
     464          23 :         nYSize > INT_MAX - nTileSize)
     465           0 :         return nullptr;
     466             :     /* To avoid later potential int overflows */
     467          23 :     if (nExtendedHeaderLen > 1024 * 65536)
     468           0 :         return nullptr;
     469             : 
     470          23 :     if (!GDALCheckDatasetDimensions(nXSize, nYSize))
     471             :     {
     472           0 :         return nullptr;
     473             :     }
     474          23 :     const int nXBlocks = DIV_ROUND_UP(nXSize, nTileSize);
     475          23 :     const int nYBlocks = DIV_ROUND_UP(nYSize, nTileSize);
     476          23 :     if (nXBlocks > INT_MAX / nYBlocks)
     477             :     {
     478           0 :         return nullptr;
     479             :     }
     480             : 
     481             :     /* -------------------------------------------------------------------- */
     482             :     /*      Parse extended blocks                                           */
     483             :     /* -------------------------------------------------------------------- */
     484             : 
     485          23 :     VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "rb");
     486          23 :     if (fp == nullptr)
     487           0 :         return nullptr;
     488             : 
     489          23 :     VSIFSeekL(fp, 28, SEEK_SET);
     490             : 
     491          23 :     int bHasExtent = FALSE;
     492          23 :     double dfMinX = 0.0;
     493          23 :     double dfMaxX = 0.0;
     494          23 :     double dfMinY = 0.0;
     495          23 :     double dfMaxY = 0.0;
     496          23 :     int bHasUTMZone = FALSE;
     497          23 :     GInt16 nUTMZone = 0;
     498          23 :     int bHasEPSGDatumCode = FALSE;
     499          23 :     GInt16 nEPSGDatumCode = 0;
     500          23 :     int bHasEPSGCode = FALSE;
     501          23 :     GInt16 nEPSGCode = 0;
     502          23 :     int bHasRelativePrecision = FALSE;
     503          23 :     float fRelativePrecision = 0.0f;
     504          23 :     char szApplicationName[256] = {0};
     505             : 
     506          23 :     GUInt32 nExtendedHeaderOff = 0;
     507          84 :     while (nExtendedHeaderOff < nExtendedHeaderLen)
     508             :     {
     509             :         char pabyBlockHeader[24];
     510          61 :         VSIFReadL(pabyBlockHeader, 24, 1, fp);
     511             : 
     512             :         char szBlockName[16 + 1];
     513          61 :         memcpy(szBlockName, pabyBlockHeader + 4, 16);
     514          61 :         szBlockName[16] = 0;
     515             :         GUInt32 nBlockSize;
     516          61 :         memcpy(&nBlockSize, pabyBlockHeader + 20, 4);
     517          61 :         CPL_LSBPTR32(&nBlockSize);
     518          61 :         if (nBlockSize > 65536)
     519           0 :             break;
     520             : 
     521          61 :         nExtendedHeaderOff += 24 + nBlockSize;
     522             : 
     523          61 :         if (strcmp(szBlockName, "georef-extents") == 0 && nBlockSize == 34)
     524             :         {
     525             :             char pabyBlockData[34];
     526          23 :             VSIFReadL(pabyBlockData, 34, 1, fp);
     527             : 
     528          23 :             memcpy(&dfMinX, pabyBlockData + 2, 8);
     529          23 :             CPL_LSBPTR64(&dfMinX);
     530          23 :             memcpy(&dfMaxX, pabyBlockData + 2 + 8, 8);
     531          23 :             CPL_LSBPTR64(&dfMaxX);
     532          23 :             memcpy(&dfMinY, pabyBlockData + 2 + 8 + 8, 8);
     533          23 :             CPL_LSBPTR64(&dfMinY);
     534          23 :             memcpy(&dfMaxY, pabyBlockData + 2 + 8 + 8 + 8, 8);
     535          23 :             CPL_LSBPTR64(&dfMaxY);
     536             : 
     537          23 :             bHasExtent = TRUE;
     538             :         }
     539          38 :         else if (strcmp(szBlockName, "georef-utm") == 0 && nBlockSize == 2)
     540             :         {
     541           9 :             VSIFReadL(&nUTMZone, 2, 1, fp);
     542           9 :             CPL_LSBPTR16(&nUTMZone);
     543           9 :             CPLDebug("HF2", "UTM Zone = %d", nUTMZone);
     544             : 
     545           9 :             bHasUTMZone = TRUE;
     546             :         }
     547          29 :         else if (strcmp(szBlockName, "georef-datum") == 0 && nBlockSize == 2)
     548             :         {
     549          23 :             VSIFReadL(&nEPSGDatumCode, 2, 1, fp);
     550          23 :             CPL_LSBPTR16(&nEPSGDatumCode);
     551          23 :             CPLDebug("HF2", "EPSG Datum Code = %d", nEPSGDatumCode);
     552             : 
     553          23 :             bHasEPSGDatumCode = TRUE;
     554             :         }
     555           6 :         else if (strcmp(szBlockName, "georef-epsg-prj") == 0 && nBlockSize == 2)
     556             :         {
     557           6 :             VSIFReadL(&nEPSGCode, 2, 1, fp);
     558           6 :             CPL_LSBPTR16(&nEPSGCode);
     559           6 :             CPLDebug("HF2", "EPSG Code = %d", nEPSGCode);
     560             : 
     561           6 :             bHasEPSGCode = TRUE;
     562             :         }
     563           0 :         else if (strcmp(szBlockName, "precis-rel") == 0 && nBlockSize == 4)
     564             :         {
     565           0 :             VSIFReadL(&fRelativePrecision, 4, 1, fp);
     566           0 :             CPL_LSBPTR32(&fRelativePrecision);
     567             : 
     568           0 :             bHasRelativePrecision = TRUE;
     569             :         }
     570           0 :         else if (strcmp(szBlockName, "app-name") == 0 &&
     571           0 :                  nBlockSize < sizeof(szApplicationName))
     572             :         {
     573           0 :             VSIFReadL(szApplicationName, nBlockSize, 1, fp);
     574           0 :             szApplicationName[nBlockSize] = 0;
     575             :         }
     576             :         else
     577             :         {
     578           0 :             CPLDebug("HF2", "Skipping block %s", szBlockName);
     579           0 :             VSIFSeekL(fp, nBlockSize, SEEK_CUR);
     580             :         }
     581             :     }
     582             : 
     583             :     /* -------------------------------------------------------------------- */
     584             :     /*      Create a corresponding GDALDataset.                             */
     585             :     /* -------------------------------------------------------------------- */
     586          23 :     HF2Dataset *poDS = new HF2Dataset();
     587          23 :     poDS->fp = fp;
     588          23 :     poDS->nRasterXSize = nXSize;
     589          23 :     poDS->nRasterYSize = nYSize;
     590          23 :     poDS->nTileSize = nTileSize;
     591          23 :     CPLDebug("HF2", "nXSize = %d, nYSize = %d, nTileSize = %d", nXSize, nYSize,
     592             :              nTileSize);
     593          23 :     if (bHasExtent)
     594             :     {
     595          23 :         poDS->m_gt[0] = dfMinX;
     596          23 :         poDS->m_gt[3] = dfMaxY;
     597          23 :         poDS->m_gt[1] = (dfMaxX - dfMinX) / nXSize;
     598          23 :         poDS->m_gt[5] = -(dfMaxY - dfMinY) / nYSize;
     599             :     }
     600             :     else
     601             :     {
     602           0 :         poDS->m_gt[1] = fHorizScale;
     603           0 :         poDS->m_gt[5] = fHorizScale;
     604             :     }
     605             : 
     606          23 :     if (bHasEPSGCode)
     607             :     {
     608           6 :         poDS->m_oSRS.importFromEPSG(nEPSGCode);
     609             :     }
     610             :     else
     611             :     {
     612          17 :         bool bHasSRS = false;
     613          34 :         OGRSpatialReference oSRS;
     614          17 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     615          17 :         oSRS.SetGeogCS("unknown", "unknown", "unknown", SRS_WGS84_SEMIMAJOR,
     616             :                        SRS_WGS84_INVFLATTENING);
     617          17 :         if (bHasEPSGDatumCode)
     618             :         {
     619          17 :             if (nEPSGDatumCode == 23 || nEPSGDatumCode == 6326)
     620             :             {
     621          14 :                 bHasSRS = true;
     622          14 :                 oSRS.SetWellKnownGeogCS("WGS84");
     623             :             }
     624           3 :             else if (nEPSGDatumCode >= 6000)
     625             :             {
     626             :                 char szName[32];
     627           3 :                 snprintf(szName, sizeof(szName), "EPSG:%d",
     628           3 :                          nEPSGDatumCode - 2000);
     629           3 :                 oSRS.SetWellKnownGeogCS(szName);
     630           3 :                 bHasSRS = true;
     631             :             }
     632             :         }
     633             : 
     634          17 :         if (bHasUTMZone && std::abs(nUTMZone) >= 1 && std::abs(nUTMZone) <= 60)
     635             :         {
     636           3 :             bHasSRS = true;
     637           3 :             oSRS.SetUTM(std::abs(static_cast<int>(nUTMZone)), nUTMZone > 0);
     638             :         }
     639          17 :         if (bHasSRS)
     640          17 :             poDS->m_oSRS = std::move(oSRS);
     641             :     }
     642             : 
     643             :     /* -------------------------------------------------------------------- */
     644             :     /*      Create band information objects.                                */
     645             :     /* -------------------------------------------------------------------- */
     646          23 :     poDS->nBands = 1;
     647          46 :     for (int i = 0; i < poDS->nBands; i++)
     648             :     {
     649          23 :         poDS->SetBand(i + 1, new HF2RasterBand(poDS, i + 1, GDT_Float32));
     650          23 :         poDS->GetRasterBand(i + 1)->SetUnitType("m");
     651             :     }
     652             : 
     653          23 :     if (szApplicationName[0] != '\0')
     654           0 :         poDS->SetMetadataItem("APPLICATION_NAME", szApplicationName);
     655          23 :     poDS->SetMetadataItem("VERTICAL_PRECISION",
     656          46 :                           CPLString().Printf("%f", fVertPres));
     657          23 :     if (bHasRelativePrecision)
     658           0 :         poDS->SetMetadataItem("RELATIVE_VERTICAL_PRECISION",
     659           0 :                               CPLString().Printf("%f", fRelativePrecision));
     660             : 
     661             :     /* -------------------------------------------------------------------- */
     662             :     /*      Initialize any PAM information.                                 */
     663             :     /* -------------------------------------------------------------------- */
     664          23 :     poDS->SetDescription(osOriginalFilename.c_str());
     665          23 :     poDS->TryLoadXML();
     666             : 
     667             :     /* -------------------------------------------------------------------- */
     668             :     /*      Support overviews.                                              */
     669             :     /* -------------------------------------------------------------------- */
     670          23 :     poDS->oOvManager.Initialize(poDS, osOriginalFilename.c_str());
     671          23 :     return poDS;
     672             : }
     673             : 
     674             : /************************************************************************/
     675             : /*                          GetGeoTransform()                           */
     676             : /************************************************************************/
     677             : 
     678          16 : CPLErr HF2Dataset::GetGeoTransform(GDALGeoTransform &gt) const
     679             : 
     680             : {
     681          16 :     gt = m_gt;
     682             : 
     683          16 :     return CE_None;
     684             : }
     685             : 
     686       36065 : static void WriteShort(VSILFILE *fp, GInt16 val)
     687             : {
     688       36065 :     CPL_LSBPTR16(&val);
     689       36065 :     VSIFWriteL(&val, 2, 1, fp);
     690       36065 : }
     691             : 
     692         572 : static void WriteInt(VSILFILE *fp, GInt32 val)
     693             : {
     694         572 :     CPL_LSBPTR32(&val);
     695         572 :     VSIFWriteL(&val, 4, 1, fp);
     696         572 : }
     697             : 
     698          66 : static void WriteFloat(VSILFILE *fp, float val)
     699             : {
     700          66 :     CPL_LSBPTR32(&val);
     701          66 :     VSIFWriteL(&val, 4, 1, fp);
     702          66 : }
     703             : 
     704          60 : static void WriteDouble(VSILFILE *fp, double val)
     705             : {
     706          60 :     CPL_LSBPTR64(&val);
     707          60 :     VSIFWriteL(&val, 8, 1, fp);
     708          60 : }
     709             : 
     710             : /************************************************************************/
     711             : /*                             CreateCopy()                             */
     712             : /************************************************************************/
     713             : 
     714          23 : GDALDataset *HF2Dataset::CreateCopy(const char *pszFilename,
     715             :                                     GDALDataset *poSrcDS, int bStrict,
     716             :                                     char **papszOptions,
     717             :                                     GDALProgressFunc pfnProgress,
     718             :                                     void *pProgressData)
     719             : {
     720             :     /* -------------------------------------------------------------------- */
     721             :     /*      Some some rudimentary checks                                    */
     722             :     /* -------------------------------------------------------------------- */
     723          23 :     int nBands = poSrcDS->GetRasterCount();
     724          23 :     if (nBands == 0)
     725             :     {
     726           1 :         CPLError(
     727             :             CE_Failure, CPLE_NotSupported,
     728             :             "HF2 driver does not support source dataset with zero band.\n");
     729           1 :         return nullptr;
     730             :     }
     731             : 
     732          22 :     if (nBands != 1)
     733             :     {
     734           4 :         CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
     735             :                  "HF2 driver only uses the first band of the dataset.\n");
     736           4 :         if (bStrict)
     737           4 :             return nullptr;
     738             :     }
     739             : 
     740          18 :     if (pfnProgress && !pfnProgress(0.0, nullptr, pProgressData))
     741           0 :         return nullptr;
     742             : 
     743             :     /* -------------------------------------------------------------------- */
     744             :     /*      Get source dataset info                                         */
     745             :     /* -------------------------------------------------------------------- */
     746             : 
     747          18 :     const int nXSize = poSrcDS->GetRasterXSize();
     748          18 :     const int nYSize = poSrcDS->GetRasterYSize();
     749          18 :     GDALGeoTransform gt;
     750          36 :     const bool bHasGeoTransform = poSrcDS->GetGeoTransform(gt) == CE_None &&
     751          18 :                                   !(gt[0] == 0 && gt[1] == 1 && gt[2] == 0 &&
     752           0 :                                     gt[3] == 0 && gt[4] == 0 && gt[5] == 1);
     753          18 :     if (gt[2] != 0 || gt[4] != 0)
     754             :     {
     755           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     756             :                  "HF2 driver does not support CreateCopy() from skewed or "
     757             :                  "rotated dataset.\n");
     758           0 :         return nullptr;
     759             :     }
     760             : 
     761          18 :     GDALDataType eSrcDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
     762             :     GDALDataType eReqDT;
     763          18 :     float fVertPres = (float)0.01;
     764          18 :     if (eSrcDT == GDT_Byte || eSrcDT == GDT_Int16)
     765             :     {
     766           8 :         fVertPres = 1;
     767           8 :         eReqDT = GDT_Int16;
     768             :     }
     769             :     else
     770          10 :         eReqDT = GDT_Float32;
     771             : 
     772             :     /* -------------------------------------------------------------------- */
     773             :     /*      Read creation options                                           */
     774             :     /* -------------------------------------------------------------------- */
     775          18 :     const char *pszCompressed = CSLFetchNameValue(papszOptions, "COMPRESS");
     776          18 :     bool bCompress = false;
     777          18 :     if (pszCompressed)
     778           1 :         bCompress = CPLTestBool(pszCompressed);
     779             : 
     780             :     const char *pszVerticalPrecision =
     781          18 :         CSLFetchNameValue(papszOptions, "VERTICAL_PRECISION");
     782          18 :     if (pszVerticalPrecision)
     783             :     {
     784           0 :         fVertPres = (float)CPLAtofM(pszVerticalPrecision);
     785           0 :         if (fVertPres <= 0)
     786             :         {
     787           0 :             CPLError(
     788             :                 CE_Warning, CPLE_AppDefined,
     789             :                 "Unsupported value for VERTICAL_PRECISION. Defaulting to 0.01");
     790           0 :             fVertPres = (float)0.01;
     791             :         }
     792           0 :         if (eReqDT == GDT_Int16 && fVertPres > 1)
     793           0 :             eReqDT = GDT_Float32;
     794             :     }
     795             : 
     796          18 :     const char *pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
     797          18 :     int nTileSize = 256;
     798          18 :     if (pszBlockSize)
     799             :     {
     800           1 :         nTileSize = atoi(pszBlockSize);
     801           1 :         if (nTileSize < 8 || nTileSize > 4096)
     802             :         {
     803           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     804             :                      "Unsupported value for BLOCKSIZE. Defaulting to 256");
     805           0 :             nTileSize = 256;
     806             :         }
     807             :     }
     808             : 
     809             :     /* -------------------------------------------------------------------- */
     810             :     /*      Parse source dataset georeferencing info                        */
     811             :     /* -------------------------------------------------------------------- */
     812             : 
     813          18 :     int nExtendedHeaderLen = 0;
     814          18 :     if (bHasGeoTransform)
     815          18 :         nExtendedHeaderLen += 58;
     816          18 :     const char *pszProjectionRef = poSrcDS->GetProjectionRef();
     817          18 :     int nDatumCode = -2;
     818          18 :     int nUTMZone = 0;
     819          18 :     int bNorth = FALSE;
     820          18 :     int nEPSGCode = 0;
     821          18 :     int nExtentUnits = 1;
     822          18 :     if (pszProjectionRef != nullptr && pszProjectionRef[0] != '\0')
     823             :     {
     824          36 :         OGRSpatialReference oSRS;
     825          18 :         if (oSRS.importFromWkt(pszProjectionRef) == OGRERR_NONE)
     826             :         {
     827          18 :             const char *pszValue = nullptr;
     828          22 :             if (oSRS.GetAuthorityName("GEOGCS|DATUM") != nullptr &&
     829           4 :                 EQUAL(oSRS.GetAuthorityName("GEOGCS|DATUM"), "EPSG"))
     830           4 :                 nDatumCode = atoi(oSRS.GetAuthorityCode("GEOGCS|DATUM"));
     831          14 :             else if ((pszValue = oSRS.GetAttrValue("GEOGCS|DATUM")) != nullptr)
     832             :             {
     833          14 :                 if (strstr(pszValue, "WGS") && strstr(pszValue, "84"))
     834          14 :                     nDatumCode = 6326;
     835             :             }
     836             : 
     837          18 :             nUTMZone = oSRS.GetUTMZone(&bNorth);
     838             :         }
     839          20 :         if (oSRS.GetAuthorityName("PROJCS") != nullptr &&
     840           2 :             EQUAL(oSRS.GetAuthorityName("PROJCS"), "EPSG"))
     841           2 :             nEPSGCode = atoi(oSRS.GetAuthorityCode("PROJCS"));
     842             : 
     843          18 :         if (oSRS.IsGeographic())
     844             :         {
     845          15 :             nExtentUnits = 0;
     846             :         }
     847             :         else
     848             :         {
     849           3 :             const double dfLinear = oSRS.GetLinearUnits();
     850             : 
     851           3 :             if (std::abs(dfLinear - 0.3048) < 0.0000001)
     852           0 :                 nExtentUnits = 2;
     853           3 :             else if (std::abs(dfLinear - CPLAtof(SRS_UL_US_FOOT_CONV)) <
     854             :                      0.00000001)
     855           0 :                 nExtentUnits = 3;
     856             :             else
     857           3 :                 nExtentUnits = 1;
     858             :         }
     859             :     }
     860          18 :     if (nDatumCode != -2)
     861          18 :         nExtendedHeaderLen += 26;
     862          18 :     if (nUTMZone != 0)
     863           3 :         nExtendedHeaderLen += 26;
     864          18 :     if (nEPSGCode)
     865           2 :         nExtendedHeaderLen += 26;
     866             : 
     867             :     /* -------------------------------------------------------------------- */
     868             :     /*      Create target file                                              */
     869             :     /* -------------------------------------------------------------------- */
     870             : 
     871          36 :     CPLString osFilename;
     872          18 :     if (bCompress)
     873             :     {
     874           1 :         osFilename = "/vsigzip/";
     875           1 :         osFilename += pszFilename;
     876             :     }
     877             :     else
     878          17 :         osFilename = pszFilename;
     879          18 :     VSILFILE *fp = VSIFOpenL(osFilename.c_str(), "wb");
     880          18 :     if (fp == nullptr)
     881             :     {
     882           3 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot create %s", pszFilename);
     883           3 :         return nullptr;
     884             :     }
     885             : 
     886             :     /* -------------------------------------------------------------------- */
     887             :     /*      Write header                                                    */
     888             :     /* -------------------------------------------------------------------- */
     889             : 
     890          15 :     VSIFWriteL("HF2\0", 4, 1, fp);
     891          15 :     WriteShort(fp, 0);
     892          15 :     WriteInt(fp, nXSize);
     893          15 :     WriteInt(fp, nYSize);
     894          15 :     WriteShort(fp, (GInt16)nTileSize);
     895          15 :     WriteFloat(fp, fVertPres);
     896          15 :     const float fHorizScale = (float)((fabs(gt[1]) + fabs(gt[5])) / 2);
     897          15 :     WriteFloat(fp, fHorizScale);
     898          15 :     WriteInt(fp, nExtendedHeaderLen);
     899             : 
     900             :     /* -------------------------------------------------------------------- */
     901             :     /*      Write extended header                                           */
     902             :     /* -------------------------------------------------------------------- */
     903             : 
     904             :     char szBlockName[16 + 1];
     905          15 :     if (bHasGeoTransform)
     906             :     {
     907          15 :         VSIFWriteL("bin\0", 4, 1, fp);
     908          15 :         memset(szBlockName, 0, 16 + 1);
     909          15 :         strcpy(szBlockName, "georef-extents");
     910          15 :         VSIFWriteL(szBlockName, 16, 1, fp);
     911          15 :         WriteInt(fp, 34);
     912          15 :         WriteShort(fp, (GInt16)nExtentUnits);
     913          15 :         WriteDouble(fp, gt[0]);
     914          15 :         WriteDouble(fp, gt[0] + nXSize * gt[1]);
     915          15 :         WriteDouble(fp, gt[3] + nYSize * gt[5]);
     916          15 :         WriteDouble(fp, gt[3]);
     917             :     }
     918          15 :     if (nUTMZone != 0)
     919             :     {
     920           3 :         VSIFWriteL("bin\0", 4, 1, fp);
     921           3 :         memset(szBlockName, 0, 16 + 1);
     922           3 :         strcpy(szBlockName, "georef-utm");
     923           3 :         VSIFWriteL(szBlockName, 16, 1, fp);
     924           3 :         WriteInt(fp, 2);
     925           3 :         WriteShort(fp, (GInt16)((bNorth) ? nUTMZone : -nUTMZone));
     926             :     }
     927          15 :     if (nDatumCode != -2)
     928             :     {
     929          15 :         VSIFWriteL("bin\0", 4, 1, fp);
     930          15 :         memset(szBlockName, 0, 16 + 1);
     931          15 :         strcpy(szBlockName, "georef-datum");
     932          15 :         VSIFWriteL(szBlockName, 16, 1, fp);
     933          15 :         WriteInt(fp, 2);
     934          15 :         WriteShort(fp, (GInt16)nDatumCode);
     935             :     }
     936          15 :     if (nEPSGCode != 0)
     937             :     {
     938           2 :         VSIFWriteL("bin\0", 4, 1, fp);
     939           2 :         memset(szBlockName, 0, 16 + 1);
     940           2 :         strcpy(szBlockName, "georef-epsg-prj");
     941           2 :         VSIFWriteL(szBlockName, 16, 1, fp);
     942           2 :         WriteInt(fp, 2);
     943           2 :         WriteShort(fp, (GInt16)nEPSGCode);
     944             :     }
     945             : 
     946             :     /* -------------------------------------------------------------------- */
     947             :     /*      Copy imagery                                                    */
     948             :     /* -------------------------------------------------------------------- */
     949          15 :     const int nXBlocks = DIV_ROUND_UP(nXSize, nTileSize);
     950          15 :     const int nYBlocks = DIV_ROUND_UP(nYSize, nTileSize);
     951             : 
     952          15 :     void *pTileBuffer = VSI_MALLOC3_VERBOSE(nTileSize, nTileSize,
     953             :                                             GDALGetDataTypeSizeBytes(eReqDT));
     954          15 :     if (pTileBuffer == nullptr)
     955             :     {
     956           0 :         VSIFCloseL(fp);
     957           0 :         return nullptr;
     958             :     }
     959             : 
     960          15 :     CPLErr eErr = CE_None;
     961          31 :     for (int j = 0; j < nYBlocks && eErr == CE_None; j++)
     962             :     {
     963          34 :         for (int i = 0; i < nXBlocks && eErr == CE_None; i++)
     964             :         {
     965          18 :             const int nReqXSize = std::min(nTileSize, nXSize - i * nTileSize);
     966          18 :             const int nReqYSize = std::min(nTileSize, nYSize - j * nTileSize);
     967          36 :             eErr = poSrcDS->GetRasterBand(1)->RasterIO(
     968             :                 GF_Read, i * nTileSize,
     969          18 :                 std::max(0, nYSize - (j + 1) * nTileSize), nReqXSize, nReqYSize,
     970             :                 pTileBuffer, nReqXSize, nReqYSize, eReqDT, 0, 0, nullptr);
     971          18 :             if (eErr != CE_None)
     972           0 :                 break;
     973             : 
     974          18 :             if (eReqDT == GDT_Int16)
     975             :             {
     976           8 :                 WriteFloat(fp, 1); /* scale */
     977           8 :                 WriteFloat(fp, 0); /* offset */
     978         209 :                 for (int k = 0; k < nReqYSize; k++)
     979             :                 {
     980         201 :                     int nLastVal =
     981             :                         ((short *)
     982         201 :                              pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
     983         201 :                     GByte nWordSize = 1;
     984       15641 :                     for (int l = 1; l < nReqXSize; l++)
     985             :                     {
     986       15440 :                         const int nVal =
     987             :                             ((short *)
     988       15440 :                                  pTileBuffer)[(nReqYSize - k - 1) * nReqXSize +
     989       15440 :                                               l];
     990       15440 :                         const int nDiff = nVal - nLastVal;
     991       15440 :                         if (nDiff < -32768 || nDiff > 32767)
     992             :                         {
     993           0 :                             nWordSize = 4;
     994           0 :                             break;
     995             :                         }
     996       15440 :                         if (nDiff < -128 || nDiff > 127)
     997           0 :                             nWordSize = 2;
     998       15440 :                         nLastVal = nVal;
     999             :                     }
    1000             : 
    1001         201 :                     VSIFWriteL(&nWordSize, 1, 1, fp);
    1002         201 :                     nLastVal =
    1003             :                         ((short *)
    1004         201 :                              pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
    1005         201 :                     WriteInt(fp, nLastVal);
    1006       15641 :                     for (int l = 1; l < nReqXSize; l++)
    1007             :                     {
    1008       15440 :                         const int nVal =
    1009             :                             ((short *)
    1010       15440 :                                  pTileBuffer)[(nReqYSize - k - 1) * nReqXSize +
    1011       15440 :                                               l];
    1012       15440 :                         const int nDiff = nVal - nLastVal;
    1013       15440 :                         if (nWordSize == 1)
    1014             :                         {
    1015       15440 :                             CPLAssert(nDiff >= -128 && nDiff <= 127);
    1016       15440 :                             signed char chDiff = (signed char)nDiff;
    1017       15440 :                             VSIFWriteL(&chDiff, 1, 1, fp);
    1018             :                         }
    1019           0 :                         else if (nWordSize == 2)
    1020             :                         {
    1021           0 :                             CPLAssert(nDiff >= -32768 && nDiff <= 32767);
    1022           0 :                             WriteShort(fp, (short)nDiff);
    1023             :                         }
    1024             :                         else
    1025             :                         {
    1026           0 :                             WriteInt(fp, nDiff);
    1027             :                         }
    1028       15440 :                         nLastVal = nVal;
    1029             :                     }
    1030             :                 }
    1031             :             }
    1032             :             else
    1033             :             {
    1034          10 :                 float fMinVal = ((float *)pTileBuffer)[0];
    1035          10 :                 float fMaxVal = fMinVal;
    1036       41301 :                 for (int k = 1; k < nReqYSize * nReqXSize; k++)
    1037             :                 {
    1038       41291 :                     float fVal = ((float *)pTileBuffer)[k];
    1039       41291 :                     if (std::isnan(fVal))
    1040             :                     {
    1041           0 :                         CPLError(CE_Failure, CPLE_NotSupported,
    1042             :                                  "NaN value found");
    1043           0 :                         eErr = CE_Failure;
    1044           0 :                         break;
    1045             :                     }
    1046       41291 :                     if (fVal < fMinVal)
    1047           0 :                         fMinVal = fVal;
    1048       41291 :                     if (fVal > fMaxVal)
    1049         180 :                         fMaxVal = fVal;
    1050             :                 }
    1051          10 :                 if (eErr == CE_Failure)
    1052           0 :                     break;
    1053             : 
    1054          10 :                 float fIntRange = (fMaxVal - fMinVal) / fVertPres;
    1055          10 :                 if (fIntRange >
    1056          10 :                     static_cast<float>(std::numeric_limits<int>::max()))
    1057             :                 {
    1058           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
    1059             :                              "VERTICAL_PRECISION too small regarding actual "
    1060             :                              "range of values");
    1061           0 :                     eErr = CE_Failure;
    1062           0 :                     break;
    1063             :                 }
    1064          10 :                 float fScale =
    1065          10 :                     (fMinVal == fMaxVal) ? 1 : (fMaxVal - fMinVal) / fIntRange;
    1066          10 :                 if (fScale == 0.0f)
    1067             :                 {
    1068           0 :                     CPLError(CE_Failure, CPLE_NotSupported, "Scale == 0.0f");
    1069           0 :                     eErr = CE_Failure;
    1070           0 :                     break;
    1071             :                 }
    1072          10 :                 float fOffset = fMinVal;
    1073          10 :                 WriteFloat(fp, fScale);  /* scale */
    1074          10 :                 WriteFloat(fp, fOffset); /* offset */
    1075         301 :                 for (int k = 0; k < nReqYSize; k++)
    1076             :                 {
    1077         291 :                     float fLastVal =
    1078             :                         ((float *)
    1079         291 :                              pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
    1080         291 :                     float fIntLastVal = (fLastVal - fOffset) / fScale;
    1081         291 :                     CPLAssert(
    1082             :                         fIntLastVal <=
    1083             :                         static_cast<float>(std::numeric_limits<int>::max()));
    1084         291 :                     int nLastVal = (int)fIntLastVal;
    1085         291 :                     GByte nWordSize = 1;
    1086       41301 :                     for (int l = 1; l < nReqXSize; l++)
    1087             :                     {
    1088       41010 :                         float fVal =
    1089             :                             ((float *)
    1090       41010 :                                  pTileBuffer)[(nReqYSize - k - 1) * nReqXSize +
    1091       41010 :                                               l];
    1092       41010 :                         float fIntVal = (fVal - fOffset) / fScale;
    1093       41010 :                         CPLAssert(fIntVal <=
    1094             :                                   static_cast<float>(
    1095             :                                       std::numeric_limits<int>::max()));
    1096       41010 :                         const int nVal = (int)fIntVal;
    1097       41010 :                         const int nDiff = nVal - nLastVal;
    1098       41010 :                         CPLAssert((int)((GIntBig)nVal - nLastVal) == nDiff);
    1099       41010 :                         if (nDiff < -32768 || nDiff > 32767)
    1100             :                         {
    1101           0 :                             nWordSize = 4;
    1102           0 :                             break;
    1103             :                         }
    1104       41010 :                         if (nDiff < -128 || nDiff > 127)
    1105         201 :                             nWordSize = 2;
    1106       41010 :                         nLastVal = nVal;
    1107             :                     }
    1108             : 
    1109         291 :                     VSIFWriteL(&nWordSize, 1, 1, fp);
    1110         291 :                     fLastVal =
    1111             :                         ((float *)
    1112         291 :                              pTileBuffer)[(nReqYSize - k - 1) * nReqXSize + 0];
    1113         291 :                     fIntLastVal = (fLastVal - fOffset) / fScale;
    1114         291 :                     nLastVal = (int)fIntLastVal;
    1115         291 :                     WriteInt(fp, nLastVal);
    1116       41301 :                     for (int l = 1; l < nReqXSize; l++)
    1117             :                     {
    1118       41010 :                         float fVal =
    1119             :                             ((float *)
    1120       41010 :                                  pTileBuffer)[(nReqYSize - k - 1) * nReqXSize +
    1121       41010 :                                               l];
    1122       41010 :                         float fIntVal = (fVal - fOffset) / fScale;
    1123       41010 :                         int nVal = (int)fIntVal;
    1124       41010 :                         int nDiff = nVal - nLastVal;
    1125       41010 :                         CPLAssert((int)((GIntBig)nVal - nLastVal) == nDiff);
    1126       41010 :                         if (nWordSize == 1)
    1127             :                         {
    1128        5010 :                             CPLAssert(nDiff >= -128 && nDiff <= 127);
    1129        5010 :                             signed char chDiff = (signed char)nDiff;
    1130        5010 :                             VSIFWriteL(&chDiff, 1, 1, fp);
    1131             :                         }
    1132       36000 :                         else if (nWordSize == 2)
    1133             :                         {
    1134       36000 :                             CPLAssert(nDiff >= -32768 && nDiff <= 32767);
    1135       36000 :                             WriteShort(fp, (short)nDiff);
    1136             :                         }
    1137             :                         else
    1138             :                         {
    1139           0 :                             WriteInt(fp, nDiff);
    1140             :                         }
    1141       41010 :                         nLastVal = nVal;
    1142             :                     }
    1143             :                 }
    1144             :             }
    1145             : 
    1146          36 :             if (pfnProgress && !pfnProgress((j * nXBlocks + i + 1) * 1.0 /
    1147          18 :                                                 (nXBlocks * nYBlocks),
    1148             :                                             nullptr, pProgressData))
    1149             :             {
    1150           0 :                 eErr = CE_Failure;
    1151           0 :                 break;
    1152             :             }
    1153             :         }
    1154             :     }
    1155             : 
    1156          15 :     CPLFree(pTileBuffer);
    1157             : 
    1158          15 :     VSIFCloseL(fp);
    1159             : 
    1160          15 :     if (eErr != CE_None)
    1161           0 :         return nullptr;
    1162             : 
    1163          15 :     GDALOpenInfo oOpenInfo(osFilename.c_str(), GA_ReadOnly);
    1164          15 :     HF2Dataset *poDS = reinterpret_cast<HF2Dataset *>(Open(&oOpenInfo));
    1165             : 
    1166          15 :     if (poDS)
    1167          15 :         poDS->CloneInfo(poSrcDS, GCIF_PAM_DEFAULT);
    1168             : 
    1169          15 :     return poDS;
    1170             : }
    1171             : 
    1172             : /************************************************************************/
    1173             : /*                         GDALRegister_HF2()                           */
    1174             : /************************************************************************/
    1175             : 
    1176        1935 : void GDALRegister_HF2()
    1177             : 
    1178             : {
    1179        1935 :     if (GDALGetDriverByName("HF2") != nullptr)
    1180         282 :         return;
    1181             : 
    1182        1653 :     GDALDriver *poDriver = new GDALDriver();
    1183             : 
    1184        1653 :     poDriver->SetDescription("HF2");
    1185        1653 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1186        1653 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "HF2/HFZ heightfield raster");
    1187        1653 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/hf2.html");
    1188        1653 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hf2");
    1189        1653 :     poDriver->SetMetadataItem(
    1190             :         GDAL_DMD_CREATIONOPTIONLIST,
    1191             :         "<CreationOptionList>"
    1192             :         "   <Option name='VERTICAL_PRECISION' type='float' default='0.01' "
    1193             :         "description='Vertical precision.'/>"
    1194             :         "   <Option name='COMPRESS' type='boolean' default='false' "
    1195             :         "description='Set to true to produce a GZip compressed file.'/>"
    1196             :         "   <Option name='BLOCKSIZE' type='int' default='256' "
    1197             :         "description='Tile size.'/>"
    1198        1653 :         "</CreationOptionList>");
    1199             : 
    1200        1653 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1201             : 
    1202        1653 :     poDriver->pfnOpen = HF2Dataset::Open;
    1203        1653 :     poDriver->pfnIdentify = HF2Dataset::Identify;
    1204        1653 :     poDriver->pfnCreateCopy = HF2Dataset::CreateCopy;
    1205             : 
    1206        1653 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1207             : }

Generated by: LCOV version 1.14