/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 >) 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 | } |