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