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