Coverage Report

Created: 2026-03-30 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/sigdem/sigdemdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:    Scaled Integer Gridded DEM .sigdem Driver
4
 * Purpose:    Implementation of Scaled Integer Gridded DEM
5
 * Author:     Paul Austin, paul.austin@revolsys.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018, Paul Austin <paul.austin@revolsys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "sigdemdataset.h"
14
#include "rawdataset.h"
15
16
#include "gdal_frmts.h"
17
#include "gdal_driver.h"
18
#include "gdal_drivermanager.h"
19
#include "gdal_openinfo.h"
20
#include "gdal_cpp_functions.h"
21
22
#include <algorithm>
23
#include <limits>
24
25
static void SWAP_SIGDEM_HEADER(GByte *abyHeader)
26
232
{
27
232
    CPL_MSBPTR16(abyHeader + 6);
28
232
    CPL_MSBPTR32(abyHeader + 8);
29
232
    CPL_MSBPTR64(abyHeader + 12);
30
232
    CPL_MSBPTR64(abyHeader + 20);
31
232
    CPL_MSBPTR64(abyHeader + 28);
32
232
    CPL_MSBPTR64(abyHeader + 36);
33
232
    CPL_MSBPTR64(abyHeader + 44);
34
232
    CPL_MSBPTR64(abyHeader + 52);
35
232
    CPL_MSBPTR64(abyHeader + 60);
36
232
    CPL_MSBPTR64(abyHeader + 68);
37
232
    CPL_MSBPTR64(abyHeader + 76);
38
232
    CPL_MSBPTR64(abyHeader + 84);
39
232
    CPL_MSBPTR64(abyHeader + 92);
40
232
    CPL_MSBPTR64(abyHeader + 100);
41
232
    CPL_MSBPTR32(abyHeader + 108);
42
232
    CPL_MSBPTR32(abyHeader + 112);
43
232
    CPL_MSBPTR64(abyHeader + 116);
44
232
    CPL_MSBPTR64(abyHeader + 124);
45
232
}
46
47
constexpr int CELL_SIZE_FILE = 4;
48
49
constexpr int CELL_SIZE_MEM = 8;
50
51
constexpr vsi_l_offset HEADER_LENGTH = 132;
52
53
constexpr int32_t NO_DATA = 0x80000000;
54
55
constexpr char SIGDEM_FILE_TYPE[6] = {'S', 'I', 'G', 'D', 'E', 'M'};
56
57
static OGRSpatialReference *BuildSRS(const char *pszWKT)
58
0
{
59
0
    OGRSpatialReference *poSRS = new OGRSpatialReference();
60
0
    if (poSRS->importFromWkt(pszWKT) != OGRERR_NONE)
61
0
    {
62
0
        delete poSRS;
63
0
        return nullptr;
64
0
    }
65
0
    else
66
0
    {
67
0
        if (poSRS->AutoIdentifyEPSG() != OGRERR_NONE)
68
0
        {
69
0
            auto poSRSMatch = poSRS->FindBestMatch(100);
70
0
            if (poSRSMatch)
71
0
            {
72
0
                poSRS->Release();
73
0
                poSRS = poSRSMatch;
74
0
                poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
75
0
            }
76
0
        }
77
0
        return poSRS;
78
0
    }
79
0
}
80
81
void GDALRegister_SIGDEM()
82
22
{
83
22
    if (GDALGetDriverByName("SIGDEM") == nullptr)
84
22
    {
85
22
        GDALDriver *poDriver = new GDALDriver();
86
87
22
        poDriver->SetDescription("SIGDEM");
88
22
        poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
89
22
        poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
90
22
                                  "Scaled Integer Gridded DEM .sigdem");
91
22
        poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
92
22
                                  "drivers/raster/sigdem.html");
93
22
        poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "sigdem");
94
95
22
        poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
96
22
        poDriver->pfnCreateCopy = SIGDEMDataset::CreateCopy;
97
22
        poDriver->pfnIdentify = SIGDEMDataset::Identify;
98
22
        poDriver->pfnOpen = SIGDEMDataset::Open;
99
100
22
        GetGDALDriverManager()->RegisterDriver(poDriver);
101
22
    }
102
22
}
103
104
static int32_t GetCoordinateSystemId(const char *pszProjection)
105
0
{
106
0
    int32_t coordinateSystemId = 0;
107
0
    OGRSpatialReference *poSRS = BuildSRS(pszProjection);
108
0
    if (poSRS != nullptr)
109
0
    {
110
0
        std::string pszRoot;
111
0
        if (poSRS->IsProjected())
112
0
        {
113
0
            pszRoot = "PROJCS";
114
0
        }
115
0
        else
116
0
        {
117
0
            pszRoot = "GEOCS";
118
0
        }
119
0
        const char *pszAuthName = poSRS->GetAuthorityName(pszRoot.c_str());
120
0
        const char *pszAuthCode = poSRS->GetAuthorityCode(pszRoot.c_str());
121
0
        if (pszAuthName != nullptr && EQUAL(pszAuthName, "EPSG") &&
122
0
            pszAuthCode != nullptr)
123
0
        {
124
0
            coordinateSystemId = atoi(pszAuthCode);
125
0
        }
126
0
    }
127
0
    delete poSRS;
128
0
    return coordinateSystemId;
129
0
}
130
131
SIGDEMDataset::SIGDEMDataset(const SIGDEMHeader &sHeaderIn)
132
123
    : fpImage(nullptr), sHeader(sHeaderIn)
133
123
{
134
123
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
135
123
    this->nRasterXSize = sHeader.nCols;
136
123
    this->nRasterYSize = sHeader.nRows;
137
138
123
    m_gt.xorig = sHeader.dfMinX;
139
123
    m_gt.xscale = sHeader.dfXDim;
140
123
    m_gt.xrot = 0.0;
141
123
    m_gt.yorig = sHeader.dfMaxY;
142
123
    m_gt.yrot = 0.0;
143
123
    m_gt.yscale = -sHeader.dfYDim;
144
123
}
145
146
SIGDEMDataset::~SIGDEMDataset()
147
123
{
148
123
    FlushCache(true);
149
150
123
    if (fpImage != nullptr)
151
123
    {
152
123
        if (VSIFCloseL(fpImage) != 0)
153
0
        {
154
0
            CPLError(CE_Failure, CPLE_FileIO, "I/O error");
155
0
        }
156
123
    }
157
123
}
158
159
GDALDataset *SIGDEMDataset::CreateCopy(const char *pszFilename,
160
                                       GDALDataset *poSrcDS, int /*bStrict*/,
161
                                       CSLConstList /*papszOptions*/,
162
                                       GDALProgressFunc pfnProgress,
163
                                       void *pProgressData)
164
0
{
165
0
    const int nBands = poSrcDS->GetRasterCount();
166
0
    GDALGeoTransform gt;
167
0
    if (poSrcDS->GetGeoTransform(gt) != CE_None)
168
0
    {
169
0
        CPLError(CE_Failure, CPLE_NotSupported,
170
0
                 "SIGDEM driver requires a valid GeoTransform.");
171
0
        return nullptr;
172
0
    }
173
174
0
    if (nBands != 1)
175
0
    {
176
0
        CPLError(CE_Failure, CPLE_NotSupported,
177
0
                 "SIGDEM driver doesn't support %d bands.  Must be 1 band.",
178
0
                 nBands);
179
0
        return nullptr;
180
0
    }
181
182
0
    VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
183
0
    if (fp == nullptr)
184
0
    {
185
0
        CPLError(CE_Failure, CPLE_OpenFailed,
186
0
                 "Attempt to create file `%s' failed.", pszFilename);
187
0
        return nullptr;
188
0
    }
189
190
0
    GDALRasterBand *band = poSrcDS->GetRasterBand(1);
191
0
    const char *pszProjection = poSrcDS->GetProjectionRef();
192
193
0
    int32_t nCols = poSrcDS->GetRasterXSize();
194
0
    int32_t nRows = poSrcDS->GetRasterYSize();
195
0
    int32_t nCoordinateSystemId = GetCoordinateSystemId(pszProjection);
196
197
0
    SIGDEMHeader sHeader;
198
0
    sHeader.nCoordinateSystemId = nCoordinateSystemId;
199
0
    sHeader.dfMinX = gt.xorig;
200
0
    const char *pszMin = band->GetMetadataItem("STATISTICS_MINIMUM");
201
0
    if (pszMin == nullptr)
202
0
    {
203
0
        sHeader.dfMinZ = -10000;
204
0
    }
205
0
    else
206
0
    {
207
0
        sHeader.dfMinZ = CPLAtof(pszMin);
208
0
    }
209
0
    sHeader.dfMaxY = gt.yorig;
210
0
    const char *pszMax = band->GetMetadataItem("STATISTICS_MAXIMUM");
211
0
    if (pszMax == nullptr)
212
0
    {
213
0
        sHeader.dfMaxZ = 10000;
214
0
    }
215
0
    else
216
0
    {
217
0
        sHeader.dfMaxZ = CPLAtof(pszMax);
218
0
    }
219
0
    sHeader.nCols = poSrcDS->GetRasterXSize();
220
0
    sHeader.nRows = poSrcDS->GetRasterYSize();
221
0
    sHeader.dfXDim = gt.xscale;
222
0
    sHeader.dfYDim = -gt.yscale;
223
0
    sHeader.dfMaxX = sHeader.dfMinX + sHeader.nCols * sHeader.dfXDim;
224
0
    sHeader.dfMinY = sHeader.dfMaxY - sHeader.nRows * sHeader.dfYDim;
225
0
    sHeader.dfOffsetX = sHeader.dfMinX;
226
0
    sHeader.dfOffsetY = sHeader.dfMinY;
227
228
0
    if (!sHeader.Write(fp))
229
0
    {
230
0
        VSIUnlink(pszFilename);
231
0
        VSIFCloseL(fp);
232
0
        return nullptr;
233
0
    }
234
235
    // Write fill with all NO_DATA values
236
0
    int32_t *row =
237
0
        static_cast<int32_t *>(VSI_MALLOC2_VERBOSE(nCols, sizeof(int32_t)));
238
0
    if (!row)
239
0
    {
240
0
        VSIUnlink(pszFilename);
241
0
        VSIFCloseL(fp);
242
0
        return nullptr;
243
0
    }
244
0
    std::fill(row, row + nCols, CPL_MSBWORD32(NO_DATA));
245
0
    for (int i = 0; i < nRows; i++)
246
0
    {
247
0
        if (VSIFWriteL(row, CELL_SIZE_FILE, nCols, fp) !=
248
0
            static_cast<size_t>(nCols))
249
0
        {
250
0
            VSIFree(row);
251
0
            VSIUnlink(pszFilename);
252
0
            VSIFCloseL(fp);
253
0
            return nullptr;
254
0
        }
255
0
    }
256
0
    VSIFree(row);
257
258
0
    if (VSIFCloseL(fp) != 0)
259
0
    {
260
0
        return nullptr;
261
0
    }
262
263
0
    if (nCoordinateSystemId <= 0)
264
0
    {
265
0
        if (!EQUAL(pszProjection, ""))
266
0
        {
267
0
            const CPLString osPrjFilename =
268
0
                CPLResetExtensionSafe(pszFilename, "prj");
269
0
            VSILFILE *fpProj = VSIFOpenL(osPrjFilename, "wt");
270
0
            if (fpProj != nullptr)
271
0
            {
272
0
                OGRSpatialReference oSRS;
273
0
                oSRS.importFromWkt(pszProjection);
274
0
                oSRS.morphToESRI();
275
0
                char *pszESRIProjection = nullptr;
276
0
                oSRS.exportToWkt(&pszESRIProjection);
277
0
                CPL_IGNORE_RET_VAL(VSIFWriteL(
278
0
                    pszESRIProjection, 1, strlen(pszESRIProjection), fpProj));
279
280
0
                CPL_IGNORE_RET_VAL(VSIFCloseL(fpProj));
281
0
                CPLFree(pszESRIProjection);
282
0
            }
283
0
            else
284
0
            {
285
0
                CPLError(CE_Failure, CPLE_FileIO, "Unable to create file %s.",
286
0
                         osPrjFilename.c_str());
287
0
            }
288
0
        }
289
0
    }
290
0
    GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
291
0
    GDALDataset *poDstDS = Open(&oOpenInfo);
292
0
    if (poDstDS != nullptr &&
293
0
        GDALDatasetCopyWholeRaster(poSrcDS, poDstDS, nullptr, pfnProgress,
294
0
                                   pProgressData) == OGRERR_NONE)
295
0
    {
296
0
        return poDstDS;
297
0
    }
298
0
    else
299
0
    {
300
0
        VSIUnlink(pszFilename);
301
0
        return nullptr;
302
0
    }
303
0
}
304
305
CPLErr SIGDEMDataset::GetGeoTransform(GDALGeoTransform &gt) const
306
125
{
307
125
    gt = m_gt;
308
125
    return CE_None;
309
125
}
310
311
const OGRSpatialReference *SIGDEMDataset::GetSpatialRef() const
312
126
{
313
126
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
314
126
}
315
316
int SIGDEMDataset::Identify(GDALOpenInfo *poOpenInfo)
317
465k
{
318
465k
    if (poOpenInfo->nHeaderBytes < static_cast<int>(HEADER_LENGTH))
319
402k
    {
320
402k
        return FALSE;
321
402k
    }
322
62.9k
    return memcmp(poOpenInfo->pabyHeader, SIGDEM_FILE_TYPE,
323
62.9k
                  sizeof(SIGDEM_FILE_TYPE)) == 0;
324
465k
}
325
326
GDALDataset *SIGDEMDataset::Open(GDALOpenInfo *poOpenInfo)
327
232
{
328
232
    VSILFILE *fp = poOpenInfo->fpL;
329
330
232
    SIGDEMHeader sHeader;
331
232
    if (SIGDEMDataset::Identify(poOpenInfo) != TRUE || fp == nullptr)
332
0
    {
333
0
        return nullptr;
334
0
    }
335
336
232
    sHeader.Read(poOpenInfo->pabyHeader);
337
338
232
    if (!GDALCheckDatasetDimensions(sHeader.nCols, sHeader.nRows))
339
37
    {
340
37
        return nullptr;
341
37
    }
342
343
195
    OGRSpatialReference oSRS;
344
195
    oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
345
346
195
    if (sHeader.nCoordinateSystemId > 0)
347
182
    {
348
182
        if (oSRS.importFromEPSG(sHeader.nCoordinateSystemId) != OGRERR_NONE)
349
17
        {
350
17
            CPLError(CE_Failure, CPLE_NotSupported,
351
17
                     "SIGDEM unable to find coordinateSystemId=%d.",
352
17
                     sHeader.nCoordinateSystemId);
353
17
            return nullptr;
354
17
        }
355
182
    }
356
13
    else
357
13
    {
358
13
        CPLString osPrjFilename =
359
13
            CPLResetExtensionSafe(poOpenInfo->pszFilename, "prj");
360
13
        VSIStatBufL sStatBuf;
361
13
        int nRet = VSIStatL(osPrjFilename, &sStatBuf);
362
13
        if (nRet != 0 && VSIIsCaseSensitiveFS(osPrjFilename))
363
13
        {
364
13
            osPrjFilename =
365
13
                CPLResetExtensionSafe(poOpenInfo->pszFilename, "PRJ");
366
13
            nRet = VSIStatL(osPrjFilename, &sStatBuf);
367
13
        }
368
369
13
        if (nRet == 0)
370
0
        {
371
0
            char **papszPrj = CSLLoad(osPrjFilename);
372
0
            if (oSRS.importFromESRI(papszPrj) != OGRERR_NONE)
373
0
            {
374
0
                CPLError(CE_Failure, CPLE_NotSupported,
375
0
                         "SIGDEM unable to read projection from %s.",
376
0
                         osPrjFilename.c_str());
377
0
                CSLDestroy(papszPrj);
378
0
                return nullptr;
379
0
            }
380
0
            CSLDestroy(papszPrj);
381
0
        }
382
13
        else
383
13
        {
384
13
            CPLError(CE_Failure, CPLE_NotSupported,
385
13
                     "SIGDEM unable to find projection.");
386
13
            return nullptr;
387
13
        }
388
13
    }
389
390
165
    if (sHeader.nCols > std::numeric_limits<int>::max() / (CELL_SIZE_MEM))
391
32
    {
392
32
        CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
393
32
        return nullptr;
394
32
    }
395
396
133
    if (!RAWDatasetCheckMemoryUsage(sHeader.nCols, sHeader.nRows, 1, 4, 4,
397
133
                                    4 * sHeader.nCols, 0, 0, poOpenInfo->fpL))
398
10
    {
399
10
        return nullptr;
400
10
    }
401
123
    SIGDEMDataset *poDS = new SIGDEMDataset(sHeader);
402
403
123
    poDS->m_oSRS = std::move(oSRS);
404
405
123
    poDS->fpImage = poOpenInfo->fpL;
406
123
    poOpenInfo->fpL = nullptr;
407
123
    poDS->eAccess = poOpenInfo->eAccess;
408
409
123
    poDS->SetDescription(poOpenInfo->pszFilename);
410
123
    poDS->PamInitialize();
411
412
123
    poDS->nBands = 1;
413
123
    CPLErrorReset();
414
123
    SIGDEMRasterBand *poBand = new SIGDEMRasterBand(
415
123
        poDS, poDS->fpImage, sHeader.dfMinZ, sHeader.dfMaxZ);
416
417
123
    poDS->SetBand(1, poBand);
418
123
    if (CPLGetLastErrorType() != CE_None)
419
0
    {
420
0
        poDS->nBands = 1;
421
0
        delete poDS;
422
0
        return nullptr;
423
0
    }
424
425
    // Initialize any PAM information.
426
123
    poDS->TryLoadXML();
427
428
    // Check for overviews.
429
123
    poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
430
431
123
    return poDS;
432
123
}
433
434
SIGDEMHeader::SIGDEMHeader()
435
232
{
436
232
}
437
438
bool SIGDEMHeader::Read(const GByte *pabyHeader)
439
232
{
440
232
    GByte abyHeader[HEADER_LENGTH];
441
232
    memcpy(abyHeader, pabyHeader, HEADER_LENGTH);
442
443
232
    SWAP_SIGDEM_HEADER(abyHeader);
444
232
    memcpy(&(this->version), abyHeader + 6, 2);
445
232
    memcpy(&(this->nCoordinateSystemId), abyHeader + 8, 4);
446
232
    memcpy(&(this->dfOffsetX), abyHeader + 12, 8);
447
232
    memcpy(&(this->dfScaleFactorX), abyHeader + 20, 8);
448
232
    memcpy(&(this->dfOffsetY), abyHeader + 28, 8);
449
232
    memcpy(&(this->dfScaleFactorY), abyHeader + 36, 8);
450
232
    memcpy(&(this->dfOffsetZ), abyHeader + 44, 8);
451
232
    memcpy(&(this->dfScaleFactorZ), abyHeader + 52, 8);
452
232
    memcpy(&(this->dfMinX), abyHeader + 60, 8);
453
232
    memcpy(&(this->dfMinY), abyHeader + 68, 8);
454
232
    memcpy(&(this->dfMinZ), abyHeader + 76, 8);
455
232
    memcpy(&(this->dfMaxX), abyHeader + 84, 8);
456
232
    memcpy(&(this->dfMaxY), abyHeader + 92, 8);
457
232
    memcpy(&(this->dfMaxZ), abyHeader + 100, 8);
458
232
    memcpy(&(this->nCols), abyHeader + 108, 4);
459
232
    memcpy(&(this->nRows), abyHeader + 112, 4);
460
232
    memcpy(&(this->dfXDim), abyHeader + 116, 8);
461
232
    memcpy(&(this->dfYDim), abyHeader + 124, 8);
462
463
232
    return true;
464
232
}
465
466
bool SIGDEMHeader::Write(VSILFILE *fp)
467
0
{
468
0
    GByte abyHeader[HEADER_LENGTH];
469
470
0
    memcpy(abyHeader, &(SIGDEM_FILE_TYPE), 6);
471
0
    memcpy(abyHeader + 6, &(this->version), 2);
472
0
    memcpy(abyHeader + 8, &(this->nCoordinateSystemId), 4);
473
0
    memcpy(abyHeader + 12, &(this->dfOffsetX), 8);
474
0
    memcpy(abyHeader + 20, &(this->dfScaleFactorX), 8);
475
0
    memcpy(abyHeader + 28, &(this->dfOffsetY), 8);
476
0
    memcpy(abyHeader + 36, &(this->dfScaleFactorY), 8);
477
0
    memcpy(abyHeader + 44, &(this->dfOffsetZ), 8);
478
0
    memcpy(abyHeader + 52, &(this->dfScaleFactorZ), 8);
479
0
    memcpy(abyHeader + 60, &(this->dfMinX), 8);
480
0
    memcpy(abyHeader + 68, &(this->dfMinY), 8);
481
0
    memcpy(abyHeader + 76, &(this->dfMinZ), 8);
482
0
    memcpy(abyHeader + 84, &(this->dfMaxX), 8);
483
0
    memcpy(abyHeader + 92, &(this->dfMaxY), 8);
484
0
    memcpy(abyHeader + 100, &(this->dfMaxZ), 8);
485
0
    memcpy(abyHeader + 108, &(this->nCols), 4);
486
0
    memcpy(abyHeader + 112, &(this->nRows), 4);
487
0
    memcpy(abyHeader + 116, &(this->dfXDim), 8);
488
0
    memcpy(abyHeader + 124, &(this->dfYDim), 8);
489
0
    SWAP_SIGDEM_HEADER(abyHeader);
490
0
    return VSIFWriteL(&abyHeader, HEADER_LENGTH, 1, fp) == 1;
491
0
}
492
493
SIGDEMRasterBand::SIGDEMRasterBand(SIGDEMDataset *poDSIn, VSILFILE *fpRawIn,
494
                                   double dfMinZ, double dfMaxZ)
495
123
    : dfOffsetZ(poDSIn->sHeader.dfOffsetZ),
496
123
      dfScaleFactorZ(poDSIn->sHeader.dfScaleFactorZ), fpRawL(fpRawIn)
497
123
{
498
123
    this->poDS = poDSIn;
499
123
    this->nBand = 1;
500
123
    this->nRasterXSize = poDSIn->GetRasterXSize();
501
123
    this->nRasterYSize = poDSIn->GetRasterYSize();
502
123
    this->nBlockXSize = this->nRasterXSize;
503
123
    this->nBlockYSize = 1;
504
123
    this->eDataType = GDT_Float64;
505
506
123
    this->nBlockSizeBytes = nRasterXSize * CELL_SIZE_FILE;
507
508
123
    this->pBlockBuffer = static_cast<int32_t *>(
509
123
        VSI_MALLOC2_VERBOSE(nRasterXSize, sizeof(int32_t)));
510
123
    SetNoDataValue(-9999);
511
123
    CPLString osValue;
512
123
    SetMetadataItem("STATISTICS_MINIMUM", osValue.Printf("%.15g", dfMinZ));
513
123
    SetMetadataItem("STATISTICS_MAXIMUM", osValue.Printf("%.15g", dfMaxZ));
514
123
}
515
516
CPLErr SIGDEMRasterBand::IReadBlock(int /*nBlockXOff*/, int nBlockYOff,
517
                                    void *pImage)
518
4.50k
{
519
520
4.50k
    const int nBlockIndex = nRasterYSize - nBlockYOff - 1;
521
522
4.50k
    if (nLoadedBlockIndex == nBlockIndex)
523
0
    {
524
0
        return CE_None;
525
0
    }
526
4.50k
    const vsi_l_offset nReadStart =
527
4.50k
        HEADER_LENGTH +
528
4.50k
        static_cast<vsi_l_offset>(nBlockSizeBytes) * nBlockIndex;
529
530
    // Seek to the correct line.
531
4.50k
    if (VSIFSeekL(fpRawL, nReadStart, SEEK_SET) == -1)
532
0
    {
533
0
        if (poDS != nullptr && poDS->GetAccess() == GA_ReadOnly)
534
0
        {
535
0
            CPLError(CE_Failure, CPLE_FileIO,
536
0
                     "Failed to seek to block %d @ " CPL_FRMT_GUIB ".",
537
0
                     nBlockIndex, nReadStart);
538
0
            return CE_Failure;
539
0
        }
540
0
        else
541
0
        {
542
0
            std::fill(pBlockBuffer, pBlockBuffer + nRasterXSize, 0);
543
0
            nLoadedBlockIndex = nBlockIndex;
544
0
            return CE_None;
545
0
        }
546
0
    }
547
4.50k
    const size_t nCellReadCount =
548
4.50k
        VSIFReadL(pBlockBuffer, CELL_SIZE_FILE, nRasterXSize, fpRawL);
549
4.50k
    if (nCellReadCount < static_cast<size_t>(nRasterXSize))
550
22
    {
551
22
        if (poDS != nullptr && poDS->GetAccess() == GA_ReadOnly)
552
22
        {
553
22
            CPLError(CE_Failure, CPLE_FileIO, "Failed to read block %d.",
554
22
                     nBlockIndex);
555
22
            return CE_Failure;
556
22
        }
557
0
        std::fill(pBlockBuffer + nCellReadCount, pBlockBuffer + nRasterXSize,
558
0
                  NO_DATA);
559
0
    }
560
561
4.48k
    nLoadedBlockIndex = nBlockIndex;
562
563
4.48k
    const int32_t *pnSourceValues = pBlockBuffer;
564
4.48k
    double *padfDestValues = static_cast<double *>(pImage);
565
4.48k
    double dfOffset = this->dfOffsetZ;
566
4.48k
    const double dfInvScaleFactor =
567
4.48k
        dfScaleFactorZ != 0.0 ? 1.0 / dfScaleFactorZ : 0.0;
568
4.48k
    int nCellCount = this->nRasterXSize;
569
10.9k
    for (int i = 0; i < nCellCount; i++)
570
6.47k
    {
571
6.47k
        int32_t nValue = CPL_MSBWORD32(*pnSourceValues);
572
6.47k
        if (nValue == NO_DATA)
573
0
        {
574
0
            *padfDestValues = -9999;
575
0
        }
576
6.47k
        else
577
6.47k
        {
578
6.47k
            *padfDestValues = dfOffset + nValue * dfInvScaleFactor;
579
6.47k
        }
580
581
6.47k
        pnSourceValues++;
582
6.47k
        padfDestValues++;
583
6.47k
    }
584
585
4.48k
    return CE_None;
586
4.50k
}
587
588
CPLErr SIGDEMRasterBand::IWriteBlock(int /*nBlockXOff*/, int nBlockYOff,
589
                                     void *pImage)
590
0
{
591
592
0
    const int nBlockIndex = nRasterYSize - nBlockYOff - 1;
593
594
0
    const double *padfSourceValues = static_cast<double *>(pImage);
595
0
    int32_t *pnDestValues = pBlockBuffer;
596
0
    double dfOffset = this->dfOffsetZ;
597
0
    double dfScaleFactor = this->dfScaleFactorZ;
598
0
    int nCellCount = this->nRasterXSize;
599
0
    for (int i = 0; i < nCellCount; i++)
600
0
    {
601
0
        double dfValue = *padfSourceValues;
602
0
        int32_t nValue;
603
0
        if (dfValue == -9999)
604
0
        {
605
0
            nValue = NO_DATA;
606
0
        }
607
0
        else
608
0
        {
609
0
            nValue = static_cast<int32_t>(
610
0
                std::round((dfValue - dfOffset) * dfScaleFactor));
611
0
        }
612
0
        *pnDestValues = CPL_MSBWORD32(nValue);
613
0
        padfSourceValues++;
614
0
        pnDestValues++;
615
0
    }
616
617
0
    const vsi_l_offset nWriteStart =
618
0
        HEADER_LENGTH +
619
0
        static_cast<vsi_l_offset>(nBlockSizeBytes) * nBlockIndex;
620
621
0
    if (VSIFSeekL(fpRawL, nWriteStart, SEEK_SET) == -1 ||
622
0
        VSIFWriteL(pBlockBuffer, CELL_SIZE_FILE, nRasterXSize, fpRawL) <
623
0
            static_cast<size_t>(nRasterXSize))
624
0
    {
625
0
        CPLError(CE_Failure, CPLE_FileIO, "Failed to write block %d to file.",
626
0
                 nBlockIndex);
627
628
0
        return CE_Failure;
629
0
    }
630
0
    return CE_None;
631
0
}
632
633
SIGDEMRasterBand::~SIGDEMRasterBand()
634
123
{
635
123
    SIGDEMRasterBand::FlushCache(true);
636
123
    VSIFree(pBlockBuffer);
637
123
}