Coverage Report

Created: 2025-08-11 09:23

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