/src/gdal/frmts/raw/ehdrdataset.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: ESRI .hdr Driver |
4 | | * Purpose: Implementation of EHdrDataset |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 1999, Frank Warmerdam <warmerdam@pobox.com> |
9 | | * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_port.h" |
15 | | #include "ehdrdataset.h" |
16 | | #include "rawdataset.h" |
17 | | |
18 | | #include <algorithm> |
19 | | #include <cctype> |
20 | | #include <cerrno> |
21 | | #include <climits> |
22 | | #include <cmath> |
23 | | #include <cstddef> |
24 | | #include <cstdio> |
25 | | #include <cstdlib> |
26 | | #include <cstring> |
27 | | #if HAVE_FCNTL_H |
28 | | #include <fcntl.h> |
29 | | #endif |
30 | | |
31 | | #include <limits> |
32 | | |
33 | | #include "cpl_conv.h" |
34 | | #include "cpl_error.h" |
35 | | #include "cpl_progress.h" |
36 | | #include "cpl_string.h" |
37 | | #include "cpl_vsi.h" |
38 | | #include "gdal.h" |
39 | | #include "gdal_frmts.h" |
40 | | #include "gdal_pam.h" |
41 | | #include "gdal_priv.h" |
42 | | #include "ogr_core.h" |
43 | | #include "ogr_spatialref.h" |
44 | | |
45 | | constexpr int HAS_MIN_FLAG = 0x1; |
46 | | constexpr int HAS_MAX_FLAG = 0x2; |
47 | | constexpr int HAS_MEAN_FLAG = 0x4; |
48 | | constexpr int HAS_STDDEV_FLAG = 0x8; |
49 | | constexpr int HAS_ALL_FLAGS = |
50 | | HAS_MIN_FLAG | HAS_MAX_FLAG | HAS_MEAN_FLAG | HAS_STDDEV_FLAG; |
51 | | |
52 | | /************************************************************************/ |
53 | | /* EHdrRasterBand() */ |
54 | | /************************************************************************/ |
55 | | |
56 | | EHdrRasterBand::EHdrRasterBand(GDALDataset *poDSIn, int nBandIn, |
57 | | VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn, |
58 | | int nPixelOffsetIn, int nLineOffsetIn, |
59 | | GDALDataType eDataTypeIn, |
60 | | RawRasterBand::ByteOrder eByteOrderIn, |
61 | | int nBitsIn) |
62 | 0 | : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn, |
63 | 0 | nLineOffsetIn, eDataTypeIn, eByteOrderIn, |
64 | 0 | RawRasterBand::OwnFP::NO), |
65 | 0 | nBits(nBitsIn), nStartBit(0), nPixelOffsetBits(0), nLineOffsetBits(0), |
66 | 0 | bNoDataSet(FALSE), dfNoData(0.0), dfMin(0.0), dfMax(0.0), dfMean(0.0), |
67 | 0 | dfStdDev(0.0), minmaxmeanstddev(0) |
68 | 0 | { |
69 | 0 | m_bValid = RawRasterBand::IsValid(); |
70 | |
|
71 | 0 | EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS); |
72 | |
|
73 | 0 | if (nBits < 8) |
74 | 0 | { |
75 | 0 | const int nSkipBytes = atoi(poEDS->GetKeyValue("SKIPBYTES")); |
76 | 0 | if (nSkipBytes < 0 || nSkipBytes > std::numeric_limits<int>::max() / 8) |
77 | 0 | { |
78 | 0 | m_bValid = false; |
79 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Invalid SKIPBYTES: %d", |
80 | 0 | nSkipBytes); |
81 | 0 | nStartBit = 0; |
82 | 0 | } |
83 | 0 | else |
84 | 0 | { |
85 | 0 | nStartBit = static_cast<vsi_l_offset>(nSkipBytes) * 8; |
86 | 0 | } |
87 | 0 | if (nBand >= 2) |
88 | 0 | { |
89 | 0 | GIntBig nBandRowBytes = |
90 | 0 | CPLAtoGIntBig(poEDS->GetKeyValue("BANDROWBYTES")); |
91 | 0 | if (nBandRowBytes < 0) |
92 | 0 | { |
93 | 0 | m_bValid = false; |
94 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
95 | 0 | "Invalid BANDROWBYTES: " CPL_FRMT_GIB, nBandRowBytes); |
96 | 0 | nBandRowBytes = 0; |
97 | 0 | } |
98 | 0 | vsi_l_offset nRowBytes = 0; |
99 | 0 | if (nBandRowBytes == 0) |
100 | 0 | nRowBytes = |
101 | 0 | (static_cast<vsi_l_offset>(nBits) * poDS->GetRasterXSize() + |
102 | 0 | 7) / |
103 | 0 | 8; |
104 | 0 | else |
105 | 0 | nRowBytes = static_cast<vsi_l_offset>(nBandRowBytes); |
106 | |
|
107 | 0 | nStartBit += nRowBytes * (nBand - 1) * 8; |
108 | 0 | } |
109 | |
|
110 | 0 | nPixelOffsetBits = nBits; |
111 | 0 | GIntBig nTotalRowBytes = |
112 | 0 | CPLAtoGIntBig(poEDS->GetKeyValue("TOTALROWBYTES")); |
113 | 0 | if (nTotalRowBytes < 0 || |
114 | 0 | nTotalRowBytes > GINTBIG_MAX / 8 / poDS->GetRasterYSize()) |
115 | 0 | { |
116 | 0 | m_bValid = false; |
117 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
118 | 0 | "Invalid TOTALROWBYTES: " CPL_FRMT_GIB, nTotalRowBytes); |
119 | 0 | nTotalRowBytes = 0; |
120 | 0 | } |
121 | 0 | if (nTotalRowBytes > 0) |
122 | 0 | nLineOffsetBits = static_cast<vsi_l_offset>(nTotalRowBytes * 8); |
123 | 0 | else |
124 | 0 | nLineOffsetBits = static_cast<vsi_l_offset>(nPixelOffsetBits) * |
125 | 0 | poDS->GetRasterXSize(); |
126 | |
|
127 | 0 | nBlockXSize = poDS->GetRasterXSize(); |
128 | 0 | nBlockYSize = 1; |
129 | |
|
130 | 0 | SetMetadataItem("NBITS", CPLString().Printf("%d", nBits), |
131 | 0 | "IMAGE_STRUCTURE"); |
132 | 0 | } |
133 | 0 | } |
134 | | |
135 | | /************************************************************************/ |
136 | | /* IReadBlock() */ |
137 | | /************************************************************************/ |
138 | | |
139 | | CPLErr EHdrRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage) |
140 | | |
141 | 0 | { |
142 | 0 | if (nBits >= 8) |
143 | 0 | return RawRasterBand::IReadBlock(nBlockXOff, nBlockYOff, pImage); |
144 | | |
145 | | // Establish desired position. |
146 | 0 | const vsi_l_offset nLineStart = |
147 | 0 | (nStartBit + nLineOffsetBits * nBlockYOff) / 8; |
148 | 0 | int iBitOffset = |
149 | 0 | static_cast<int>((nStartBit + nLineOffsetBits * nBlockYOff) % 8); |
150 | 0 | const vsi_l_offset nLineEnd = |
151 | 0 | (nStartBit + nLineOffsetBits * nBlockYOff + |
152 | 0 | static_cast<vsi_l_offset>(nPixelOffsetBits) * nBlockXSize - 1) / |
153 | 0 | 8; |
154 | 0 | const vsi_l_offset nLineBytesBig = nLineEnd - nLineStart + 1; |
155 | 0 | if (nLineBytesBig > |
156 | 0 | static_cast<vsi_l_offset>(std::numeric_limits<int>::max())) |
157 | 0 | return CE_Failure; |
158 | 0 | const unsigned int nLineBytes = static_cast<unsigned int>(nLineBytesBig); |
159 | | |
160 | | // Read data into buffer. |
161 | 0 | GByte *pabyBuffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nLineBytes)); |
162 | 0 | if (pabyBuffer == nullptr) |
163 | 0 | return CE_Failure; |
164 | | |
165 | 0 | if (VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0 || |
166 | 0 | VSIFReadL(pabyBuffer, 1, nLineBytes, GetFPL()) != nLineBytes) |
167 | 0 | { |
168 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
169 | 0 | "Failed to read %u bytes at offset %lu.\n%s", nLineBytes, |
170 | 0 | static_cast<unsigned long>(nLineStart), VSIStrerror(errno)); |
171 | 0 | CPLFree(pabyBuffer); |
172 | 0 | return CE_Failure; |
173 | 0 | } |
174 | | |
175 | | // Copy data, promoting to 8bit. |
176 | 0 | for (int iX = 0, iPixel = 0; iX < nBlockXSize; iX++) |
177 | 0 | { |
178 | 0 | int nOutWord = 0; |
179 | |
|
180 | 0 | for (int iBit = 0; iBit < nBits; iBit++) |
181 | 0 | { |
182 | 0 | if (pabyBuffer[iBitOffset >> 3] & (0x80 >> (iBitOffset & 7))) |
183 | 0 | nOutWord |= (1 << (nBits - 1 - iBit)); |
184 | 0 | iBitOffset++; |
185 | 0 | } |
186 | |
|
187 | 0 | iBitOffset = iBitOffset + nPixelOffsetBits - nBits; |
188 | |
|
189 | 0 | reinterpret_cast<GByte *>(pImage)[iPixel++] = |
190 | 0 | static_cast<GByte>(nOutWord); |
191 | 0 | } |
192 | |
|
193 | 0 | CPLFree(pabyBuffer); |
194 | |
|
195 | 0 | return CE_None; |
196 | 0 | } |
197 | | |
198 | | /************************************************************************/ |
199 | | /* IWriteBlock() */ |
200 | | /************************************************************************/ |
201 | | |
202 | | CPLErr EHdrRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff, void *pImage) |
203 | | |
204 | 0 | { |
205 | 0 | if (nBits >= 8) |
206 | 0 | return RawRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage); |
207 | | |
208 | | // Establish desired position. |
209 | 0 | const vsi_l_offset nLineStart = |
210 | 0 | (nStartBit + nLineOffsetBits * nBlockYOff) / 8; |
211 | 0 | int iBitOffset = |
212 | 0 | static_cast<int>((nStartBit + nLineOffsetBits * nBlockYOff) % 8); |
213 | 0 | const vsi_l_offset nLineEnd = |
214 | 0 | (nStartBit + nLineOffsetBits * nBlockYOff + |
215 | 0 | static_cast<vsi_l_offset>(nPixelOffsetBits) * nBlockXSize - 1) / |
216 | 0 | 8; |
217 | 0 | const vsi_l_offset nLineBytesBig = nLineEnd - nLineStart + 1; |
218 | 0 | if (nLineBytesBig > |
219 | 0 | static_cast<vsi_l_offset>(std::numeric_limits<int>::max())) |
220 | 0 | return CE_Failure; |
221 | 0 | const unsigned int nLineBytes = static_cast<unsigned int>(nLineBytesBig); |
222 | | |
223 | | // Read data into buffer. |
224 | 0 | GByte *pabyBuffer = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nLineBytes, 1)); |
225 | 0 | if (pabyBuffer == nullptr) |
226 | 0 | return CE_Failure; |
227 | | |
228 | 0 | if (VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0) |
229 | 0 | { |
230 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
231 | 0 | "Failed to read %u bytes at offset %lu.\n%s", nLineBytes, |
232 | 0 | static_cast<unsigned long>(nLineStart), VSIStrerror(errno)); |
233 | 0 | CPLFree(pabyBuffer); |
234 | 0 | return CE_Failure; |
235 | 0 | } |
236 | | |
237 | 0 | CPL_IGNORE_RET_VAL(VSIFReadL(pabyBuffer, nLineBytes, 1, GetFPL())); |
238 | | |
239 | | // Copy data, promoting to 8bit. |
240 | 0 | for (int iX = 0, iPixel = 0; iX < nBlockXSize; iX++) |
241 | 0 | { |
242 | 0 | const int nOutWord = reinterpret_cast<GByte *>(pImage)[iPixel++]; |
243 | |
|
244 | 0 | for (int iBit = 0; iBit < nBits; iBit++) |
245 | 0 | { |
246 | 0 | if (nOutWord & (1 << (nBits - 1 - iBit))) |
247 | 0 | pabyBuffer[iBitOffset >> 3] |= (0x80 >> (iBitOffset & 7)); |
248 | 0 | else |
249 | 0 | pabyBuffer[iBitOffset >> 3] &= ~((0x80 >> (iBitOffset & 7))); |
250 | |
|
251 | 0 | iBitOffset++; |
252 | 0 | } |
253 | |
|
254 | 0 | iBitOffset = iBitOffset + nPixelOffsetBits - nBits; |
255 | 0 | } |
256 | | |
257 | | // Write the data back out. |
258 | 0 | if (VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0 || |
259 | 0 | VSIFWriteL(pabyBuffer, 1, nLineBytes, GetFPL()) != nLineBytes) |
260 | 0 | { |
261 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
262 | 0 | "Failed to write %u bytes at offset %lu.\n%s", nLineBytes, |
263 | 0 | static_cast<unsigned long>(nLineStart), VSIStrerror(errno)); |
264 | 0 | return CE_Failure; |
265 | 0 | } |
266 | | |
267 | 0 | CPLFree(pabyBuffer); |
268 | |
|
269 | 0 | return CE_None; |
270 | 0 | } |
271 | | |
272 | | /************************************************************************/ |
273 | | /* IRasterIO() */ |
274 | | /************************************************************************/ |
275 | | |
276 | | CPLErr EHdrRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
277 | | int nXSize, int nYSize, void *pData, |
278 | | int nBufXSize, int nBufYSize, |
279 | | GDALDataType eBufType, GSpacing nPixelSpace, |
280 | | GSpacing nLineSpace, |
281 | | GDALRasterIOExtraArg *psExtraArg) |
282 | | |
283 | 0 | { |
284 | | // Defer to RawRasterBand |
285 | 0 | if (nBits >= 8) |
286 | 0 | return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, |
287 | 0 | pData, nBufXSize, nBufYSize, eBufType, |
288 | 0 | nPixelSpace, nLineSpace, psExtraArg); |
289 | | |
290 | | // Force use of IReadBlock() and IWriteBlock() |
291 | 0 | return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, |
292 | 0 | pData, nBufXSize, nBufYSize, eBufType, |
293 | 0 | nPixelSpace, nLineSpace, psExtraArg); |
294 | 0 | } |
295 | | |
296 | | /************************************************************************/ |
297 | | /* OSR_GDS() */ |
298 | | /************************************************************************/ |
299 | | |
300 | | static const char *OSR_GDS(char *pszResult, int nResultLen, char **papszNV, |
301 | | const char *pszField, const char *pszDefaultValue) |
302 | | |
303 | 0 | { |
304 | 0 | if (papszNV == nullptr || papszNV[0] == nullptr) |
305 | 0 | return pszDefaultValue; |
306 | | |
307 | 0 | int iLine = 0; // Used after for. |
308 | 0 | for (; papszNV[iLine] != nullptr && |
309 | 0 | !EQUALN(papszNV[iLine], pszField, strlen(pszField)); |
310 | 0 | iLine++) |
311 | 0 | { |
312 | 0 | } |
313 | |
|
314 | 0 | if (papszNV[iLine] == nullptr) |
315 | 0 | return pszDefaultValue; |
316 | | |
317 | 0 | char **papszTokens = CSLTokenizeString(papszNV[iLine]); |
318 | |
|
319 | 0 | if (CSLCount(papszTokens) > 1) |
320 | 0 | strncpy(pszResult, papszTokens[1], nResultLen - 1); |
321 | 0 | else |
322 | 0 | strncpy(pszResult, pszDefaultValue, nResultLen - 1); |
323 | 0 | pszResult[nResultLen - 1] = '\0'; |
324 | |
|
325 | 0 | CSLDestroy(papszTokens); |
326 | 0 | return pszResult; |
327 | 0 | } |
328 | | |
329 | | /************************************************************************/ |
330 | | /* ==================================================================== */ |
331 | | /* EHdrDataset */ |
332 | | /* ==================================================================== */ |
333 | | /************************************************************************/ |
334 | | |
335 | | /************************************************************************/ |
336 | | /* EHdrDataset() */ |
337 | | /************************************************************************/ |
338 | | |
339 | | EHdrDataset::EHdrDataset() |
340 | 0 | : fpImage(nullptr), osHeaderExt("hdr"), bGotTransform(false), |
341 | 0 | bHDRDirty(false), papszHDR(nullptr), bCLRDirty(false) |
342 | 0 | { |
343 | 0 | m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
344 | 0 | adfGeoTransform[0] = 0.0; |
345 | 0 | adfGeoTransform[1] = 1.0; |
346 | 0 | adfGeoTransform[2] = 0.0; |
347 | 0 | adfGeoTransform[3] = 0.0; |
348 | 0 | adfGeoTransform[4] = 0.0; |
349 | 0 | adfGeoTransform[5] = 1.0; |
350 | 0 | } |
351 | | |
352 | | /************************************************************************/ |
353 | | /* ~EHdrDataset() */ |
354 | | /************************************************************************/ |
355 | | |
356 | | EHdrDataset::~EHdrDataset() |
357 | | |
358 | 0 | { |
359 | 0 | EHdrDataset::Close(); |
360 | 0 | } |
361 | | |
362 | | /************************************************************************/ |
363 | | /* Close() */ |
364 | | /************************************************************************/ |
365 | | |
366 | | CPLErr EHdrDataset::Close() |
367 | 0 | { |
368 | 0 | CPLErr eErr = CE_None; |
369 | 0 | if (nOpenFlags != OPEN_FLAGS_CLOSED) |
370 | 0 | { |
371 | 0 | if (EHdrDataset::FlushCache(true) != CE_None) |
372 | 0 | eErr = CE_Failure; |
373 | |
|
374 | 0 | if (nBands > 0 && GetAccess() == GA_Update) |
375 | 0 | { |
376 | 0 | int bNoDataSet; |
377 | 0 | RawRasterBand *poBand = |
378 | 0 | reinterpret_cast<RawRasterBand *>(GetRasterBand(1)); |
379 | |
|
380 | 0 | const double dfNoData = poBand->GetNoDataValue(&bNoDataSet); |
381 | 0 | if (bNoDataSet) |
382 | 0 | { |
383 | 0 | ResetKeyValue("NODATA", CPLString().Printf("%.8g", dfNoData)); |
384 | 0 | } |
385 | |
|
386 | 0 | if (bCLRDirty) |
387 | 0 | RewriteCLR(poBand); |
388 | |
|
389 | 0 | if (bHDRDirty) |
390 | 0 | { |
391 | 0 | if (RewriteHDR() != CE_None) |
392 | 0 | eErr = CE_Failure; |
393 | 0 | } |
394 | 0 | } |
395 | |
|
396 | 0 | if (fpImage) |
397 | 0 | { |
398 | 0 | if (VSIFCloseL(fpImage) != 0) |
399 | 0 | { |
400 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
401 | 0 | eErr = CE_Failure; |
402 | 0 | } |
403 | 0 | } |
404 | |
|
405 | 0 | CSLDestroy(papszHDR); |
406 | 0 | if (GDALPamDataset::Close() != CE_None) |
407 | 0 | eErr = CE_Failure; |
408 | 0 | } |
409 | 0 | return eErr; |
410 | 0 | } |
411 | | |
412 | | /************************************************************************/ |
413 | | /* GetKeyValue() */ |
414 | | /************************************************************************/ |
415 | | |
416 | | const char *EHdrDataset::GetKeyValue(const char *pszKey, const char *pszDefault) |
417 | | |
418 | 0 | { |
419 | 0 | for (int i = 0; papszHDR[i] != nullptr; i++) |
420 | 0 | { |
421 | 0 | if (EQUALN(pszKey, papszHDR[i], strlen(pszKey)) && |
422 | 0 | isspace(static_cast<unsigned char>(papszHDR[i][strlen(pszKey)]))) |
423 | 0 | { |
424 | 0 | const char *pszValue = papszHDR[i] + strlen(pszKey); |
425 | 0 | while (isspace(static_cast<unsigned char>(*pszValue))) |
426 | 0 | pszValue++; |
427 | |
|
428 | 0 | return pszValue; |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | 0 | return pszDefault; |
433 | 0 | } |
434 | | |
435 | | /************************************************************************/ |
436 | | /* ResetKeyValue() */ |
437 | | /* */ |
438 | | /* Replace or add the keyword with the indicated value in the */ |
439 | | /* papszHDR list. */ |
440 | | /************************************************************************/ |
441 | | |
442 | | void EHdrDataset::ResetKeyValue(const char *pszKey, const char *pszValue) |
443 | | |
444 | 0 | { |
445 | 0 | if (strlen(pszValue) > 65) |
446 | 0 | { |
447 | 0 | CPLAssert(strlen(pszValue) <= 65); |
448 | 0 | return; |
449 | 0 | } |
450 | | |
451 | 0 | char szNewLine[82] = {'\0'}; |
452 | 0 | snprintf(szNewLine, sizeof(szNewLine), "%-15s%s", pszKey, pszValue); |
453 | |
|
454 | 0 | for (int i = CSLCount(papszHDR) - 1; i >= 0; i--) |
455 | 0 | { |
456 | 0 | if (EQUALN(papszHDR[i], szNewLine, strlen(pszKey) + 1)) |
457 | 0 | { |
458 | 0 | if (strcmp(papszHDR[i], szNewLine) != 0) |
459 | 0 | { |
460 | 0 | CPLFree(papszHDR[i]); |
461 | 0 | papszHDR[i] = CPLStrdup(szNewLine); |
462 | 0 | bHDRDirty = true; |
463 | 0 | } |
464 | 0 | return; |
465 | 0 | } |
466 | 0 | } |
467 | | |
468 | 0 | bHDRDirty = true; |
469 | 0 | papszHDR = CSLAddString(papszHDR, szNewLine); |
470 | 0 | } |
471 | | |
472 | | /************************************************************************/ |
473 | | /* RewriteCLR() */ |
474 | | /************************************************************************/ |
475 | | |
476 | | void EHdrDataset::RewriteCLR(GDALRasterBand *poBand) const |
477 | | |
478 | 0 | { |
479 | 0 | CPLString osCLRFilename = CPLResetExtensionSafe(GetDescription(), "clr"); |
480 | 0 | GDALColorTable *poTable = poBand->GetColorTable(); |
481 | 0 | GDALRasterAttributeTable *poRAT = poBand->GetDefaultRAT(); |
482 | 0 | if (poTable || poRAT) |
483 | 0 | { |
484 | 0 | VSILFILE *fp = VSIFOpenL(osCLRFilename, "wt"); |
485 | 0 | if (fp != nullptr) |
486 | 0 | { |
487 | | // Write RAT in priority if both are defined |
488 | 0 | if (poRAT) |
489 | 0 | { |
490 | 0 | for (int iEntry = 0; iEntry < poRAT->GetRowCount(); iEntry++) |
491 | 0 | { |
492 | 0 | CPLString oLine; |
493 | 0 | oLine.Printf("%3d %3d %3d %3d\n", |
494 | 0 | poRAT->GetValueAsInt(iEntry, 0), |
495 | 0 | poRAT->GetValueAsInt(iEntry, 1), |
496 | 0 | poRAT->GetValueAsInt(iEntry, 2), |
497 | 0 | poRAT->GetValueAsInt(iEntry, 3)); |
498 | 0 | if (VSIFWriteL(reinterpret_cast<void *>( |
499 | 0 | const_cast<char *>(oLine.c_str())), |
500 | 0 | strlen(oLine), 1, fp) != 1) |
501 | 0 | { |
502 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
503 | 0 | "Error while write color table"); |
504 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
505 | 0 | return; |
506 | 0 | } |
507 | 0 | } |
508 | 0 | } |
509 | 0 | else |
510 | 0 | { |
511 | 0 | for (int iColor = 0; iColor < poTable->GetColorEntryCount(); |
512 | 0 | iColor++) |
513 | 0 | { |
514 | 0 | GDALColorEntry sEntry; |
515 | 0 | poTable->GetColorEntryAsRGB(iColor, &sEntry); |
516 | | |
517 | | // I wish we had a way to mark transparency. |
518 | 0 | CPLString oLine; |
519 | 0 | oLine.Printf("%3d %3d %3d %3d\n", iColor, sEntry.c1, |
520 | 0 | sEntry.c2, sEntry.c3); |
521 | 0 | if (VSIFWriteL(reinterpret_cast<void *>( |
522 | 0 | const_cast<char *>(oLine.c_str())), |
523 | 0 | strlen(oLine), 1, fp) != 1) |
524 | 0 | { |
525 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
526 | 0 | "Error while write color table"); |
527 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
528 | 0 | return; |
529 | 0 | } |
530 | 0 | } |
531 | 0 | } |
532 | 0 | if (VSIFCloseL(fp) != 0) |
533 | 0 | { |
534 | 0 | CPLError(CE_Failure, CPLE_FileIO, |
535 | 0 | "Error while write color table"); |
536 | 0 | } |
537 | 0 | } |
538 | 0 | else |
539 | 0 | { |
540 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
541 | 0 | "Unable to create color file %s.", osCLRFilename.c_str()); |
542 | 0 | } |
543 | 0 | } |
544 | 0 | else |
545 | 0 | { |
546 | 0 | VSIUnlink(osCLRFilename); |
547 | 0 | } |
548 | 0 | } |
549 | | |
550 | | /************************************************************************/ |
551 | | /* SetSpatialRef() */ |
552 | | /************************************************************************/ |
553 | | |
554 | | CPLErr EHdrDataset::SetSpatialRef(const OGRSpatialReference *poSRS) |
555 | | |
556 | 0 | { |
557 | | // Reset coordinate system on the dataset. |
558 | 0 | m_oSRS.Clear(); |
559 | 0 | if (poSRS == nullptr) |
560 | 0 | return CE_None; |
561 | | |
562 | 0 | m_oSRS = *poSRS; |
563 | | // Convert to ESRI WKT. |
564 | 0 | char *pszESRI_SRS = nullptr; |
565 | 0 | const char *const apszOptions[] = {"FORMAT=WKT1_ESRI", nullptr}; |
566 | 0 | m_oSRS.exportToWkt(&pszESRI_SRS, apszOptions); |
567 | |
|
568 | 0 | if (pszESRI_SRS) |
569 | 0 | { |
570 | | // Write to .prj file. |
571 | 0 | CPLString osPrjFilename = |
572 | 0 | CPLResetExtensionSafe(GetDescription(), "prj"); |
573 | 0 | VSILFILE *fp = VSIFOpenL(osPrjFilename.c_str(), "wt"); |
574 | 0 | if (fp != nullptr) |
575 | 0 | { |
576 | 0 | size_t nCount = VSIFWriteL(pszESRI_SRS, strlen(pszESRI_SRS), 1, fp); |
577 | 0 | nCount += VSIFWriteL("\n", 1, 1, fp); |
578 | 0 | if (VSIFCloseL(fp) != 0 || nCount != 2) |
579 | 0 | { |
580 | 0 | CPLFree(pszESRI_SRS); |
581 | 0 | return CE_Failure; |
582 | 0 | } |
583 | 0 | } |
584 | | |
585 | 0 | CPLFree(pszESRI_SRS); |
586 | 0 | } |
587 | | |
588 | 0 | return CE_None; |
589 | 0 | } |
590 | | |
591 | | /************************************************************************/ |
592 | | /* GetGeoTransform() */ |
593 | | /************************************************************************/ |
594 | | |
595 | | CPLErr EHdrDataset::GetGeoTransform(double *padfTransform) |
596 | | |
597 | 0 | { |
598 | 0 | if (bGotTransform) |
599 | 0 | { |
600 | 0 | memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6); |
601 | 0 | return CE_None; |
602 | 0 | } |
603 | | |
604 | 0 | return GDALPamDataset::GetGeoTransform(padfTransform); |
605 | 0 | } |
606 | | |
607 | | /************************************************************************/ |
608 | | /* SetGeoTransform() */ |
609 | | /************************************************************************/ |
610 | | |
611 | | CPLErr EHdrDataset::SetGeoTransform(double *padfGeoTransform) |
612 | | |
613 | 0 | { |
614 | | // We only support non-rotated images with info in the .HDR file. |
615 | 0 | if (padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0) |
616 | 0 | { |
617 | 0 | return GDALPamDataset::SetGeoTransform(padfGeoTransform); |
618 | 0 | } |
619 | | |
620 | | // Record new geotransform. |
621 | 0 | bGotTransform = true; |
622 | 0 | memcpy(adfGeoTransform, padfGeoTransform, sizeof(double) * 6); |
623 | | |
624 | | // Strip out all old geotransform keywords from HDR records. |
625 | 0 | for (int i = CSLCount(papszHDR) - 1; i >= 0; i--) |
626 | 0 | { |
627 | 0 | if (STARTS_WITH_CI(papszHDR[i], "ul") || |
628 | 0 | STARTS_WITH_CI(papszHDR[i] + 1, "ll") || |
629 | 0 | STARTS_WITH_CI(papszHDR[i], "cell") || |
630 | 0 | STARTS_WITH_CI(papszHDR[i] + 1, "dim")) |
631 | 0 | { |
632 | 0 | papszHDR = CSLRemoveStrings(papszHDR, i, 1, nullptr); |
633 | 0 | } |
634 | 0 | } |
635 | | |
636 | | // Set the transformation information. |
637 | 0 | CPLString oValue; |
638 | |
|
639 | 0 | oValue.Printf("%.15g", adfGeoTransform[0] + adfGeoTransform[1] * 0.5); |
640 | 0 | ResetKeyValue("ULXMAP", oValue); |
641 | |
|
642 | 0 | oValue.Printf("%.15g", adfGeoTransform[3] + adfGeoTransform[5] * 0.5); |
643 | 0 | ResetKeyValue("ULYMAP", oValue); |
644 | |
|
645 | 0 | oValue.Printf("%.15g", adfGeoTransform[1]); |
646 | 0 | ResetKeyValue("XDIM", oValue); |
647 | |
|
648 | 0 | oValue.Printf("%.15g", fabs(adfGeoTransform[5])); |
649 | 0 | ResetKeyValue("YDIM", oValue); |
650 | |
|
651 | 0 | return CE_None; |
652 | 0 | } |
653 | | |
654 | | /************************************************************************/ |
655 | | /* RewriteHDR() */ |
656 | | /************************************************************************/ |
657 | | |
658 | | CPLErr EHdrDataset::RewriteHDR() |
659 | | |
660 | 0 | { |
661 | 0 | const CPLString osPath = CPLGetPathSafe(GetDescription()); |
662 | 0 | const CPLString osName = CPLGetBasenameSafe(GetDescription()); |
663 | 0 | CPLString osHDRFilename = |
664 | 0 | CPLFormCIFilenameSafe(osPath, osName, osHeaderExt); |
665 | | |
666 | | // Write .hdr file. |
667 | 0 | VSILFILE *fp = VSIFOpenL(osHDRFilename, "wt"); |
668 | |
|
669 | 0 | if (fp == nullptr) |
670 | 0 | { |
671 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, "Failed to rewrite .hdr file %s.", |
672 | 0 | osHDRFilename.c_str()); |
673 | 0 | return CE_Failure; |
674 | 0 | } |
675 | | |
676 | 0 | for (int i = 0; papszHDR[i] != nullptr; i++) |
677 | 0 | { |
678 | 0 | size_t nCount = VSIFWriteL(papszHDR[i], strlen(papszHDR[i]), 1, fp); |
679 | 0 | nCount += VSIFWriteL("\n", 1, 1, fp); |
680 | 0 | if (nCount != 2) |
681 | 0 | { |
682 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
683 | 0 | return CE_Failure; |
684 | 0 | } |
685 | 0 | } |
686 | | |
687 | 0 | bHDRDirty = false; |
688 | |
|
689 | 0 | if (VSIFCloseL(fp) != 0) |
690 | 0 | return CE_Failure; |
691 | | |
692 | 0 | return CE_None; |
693 | 0 | } |
694 | | |
695 | | /************************************************************************/ |
696 | | /* RewriteSTX() */ |
697 | | /************************************************************************/ |
698 | | |
699 | | CPLErr EHdrDataset::RewriteSTX() const |
700 | 0 | { |
701 | 0 | const CPLString osPath = CPLGetPathSafe(GetDescription()); |
702 | 0 | const CPLString osName = CPLGetBasenameSafe(GetDescription()); |
703 | 0 | const CPLString osSTXFilename = |
704 | 0 | CPLFormCIFilenameSafe(osPath, osName, "stx"); |
705 | |
|
706 | 0 | VSILFILE *fp = VSIFOpenL(osSTXFilename, "wt"); |
707 | 0 | if (fp == nullptr) |
708 | 0 | { |
709 | 0 | CPLDebug("EHDR", "Failed to rewrite .stx file %s.", |
710 | 0 | osSTXFilename.c_str()); |
711 | 0 | return CE_Failure; |
712 | 0 | } |
713 | | |
714 | 0 | bool bOK = true; |
715 | 0 | for (int i = 0; bOK && i < nBands; ++i) |
716 | 0 | { |
717 | 0 | EHdrRasterBand *poBand = |
718 | 0 | reinterpret_cast<EHdrRasterBand *>(papoBands[i]); |
719 | 0 | bOK &= VSIFPrintfL(fp, "%d %.10f %.10f ", i + 1, poBand->dfMin, |
720 | 0 | poBand->dfMax) >= 0; |
721 | 0 | if (poBand->minmaxmeanstddev & HAS_MEAN_FLAG) |
722 | 0 | bOK &= VSIFPrintfL(fp, "%.10f ", poBand->dfMean) >= 0; |
723 | 0 | else |
724 | 0 | bOK &= VSIFPrintfL(fp, "# ") >= 0; |
725 | |
|
726 | 0 | if (poBand->minmaxmeanstddev & HAS_STDDEV_FLAG) |
727 | 0 | bOK &= VSIFPrintfL(fp, "%.10f\n", poBand->dfStdDev) >= 0; |
728 | 0 | else |
729 | 0 | bOK &= VSIFPrintfL(fp, "#\n") >= 0; |
730 | 0 | } |
731 | |
|
732 | 0 | if (VSIFCloseL(fp) != 0) |
733 | 0 | bOK = false; |
734 | |
|
735 | 0 | return bOK ? CE_None : CE_Failure; |
736 | 0 | } |
737 | | |
738 | | /************************************************************************/ |
739 | | /* ReadSTX() */ |
740 | | /************************************************************************/ |
741 | | |
742 | | CPLErr EHdrDataset::ReadSTX() const |
743 | 0 | { |
744 | 0 | const CPLString osPath = CPLGetPathSafe(GetDescription()); |
745 | 0 | const CPLString osName = CPLGetBasenameSafe(GetDescription()); |
746 | 0 | const CPLString osSTXFilename = |
747 | 0 | CPLFormCIFilenameSafe(osPath, osName, "stx"); |
748 | |
|
749 | 0 | VSILFILE *fp = VSIFOpenL(osSTXFilename, "rt"); |
750 | 0 | if (fp == nullptr) |
751 | 0 | return CE_None; |
752 | | |
753 | 0 | const char *pszLine = nullptr; |
754 | 0 | while ((pszLine = CPLReadLineL(fp)) != nullptr) |
755 | 0 | { |
756 | 0 | char **papszTokens = |
757 | 0 | CSLTokenizeStringComplex(pszLine, " \t", TRUE, FALSE); |
758 | 0 | const int nTokens = CSLCount(papszTokens); |
759 | 0 | if (nTokens >= 5) |
760 | 0 | { |
761 | 0 | const int i = atoi(papszTokens[0]); |
762 | 0 | if (i > 0 && i <= nBands) |
763 | 0 | { |
764 | 0 | EHdrRasterBand *poBand = |
765 | 0 | reinterpret_cast<EHdrRasterBand *>(papoBands[i - 1]); |
766 | 0 | poBand->dfMin = CPLAtof(papszTokens[1]); |
767 | 0 | poBand->dfMax = CPLAtof(papszTokens[2]); |
768 | |
|
769 | 0 | int bNoDataSet = FALSE; |
770 | 0 | const double dfNoData = poBand->GetNoDataValue(&bNoDataSet); |
771 | 0 | if (bNoDataSet && dfNoData == poBand->dfMin) |
772 | 0 | { |
773 | | // Triggered by |
774 | | // /vsicurl/http://eros.usgs.gov/archive/nslrsda/GeoTowns/HongKong/srtm/n22e113.zip/n22e113.bil |
775 | 0 | CPLDebug( |
776 | 0 | "EHDr", |
777 | 0 | "Ignoring .stx file where min == nodata. " |
778 | 0 | "The nodata value should not be taken into account " |
779 | 0 | "in minimum value computation."); |
780 | 0 | CSLDestroy(papszTokens); |
781 | 0 | papszTokens = nullptr; |
782 | 0 | break; |
783 | 0 | } |
784 | | |
785 | 0 | poBand->minmaxmeanstddev = HAS_MIN_FLAG | HAS_MAX_FLAG; |
786 | | // Reads optional mean and stddev. |
787 | 0 | if (!EQUAL(papszTokens[3], "#")) |
788 | 0 | { |
789 | 0 | poBand->dfMean = CPLAtof(papszTokens[3]); |
790 | 0 | poBand->minmaxmeanstddev |= HAS_MEAN_FLAG; |
791 | 0 | } |
792 | 0 | if (!EQUAL(papszTokens[4], "#")) |
793 | 0 | { |
794 | 0 | poBand->dfStdDev = CPLAtof(papszTokens[4]); |
795 | 0 | poBand->minmaxmeanstddev |= HAS_STDDEV_FLAG; |
796 | 0 | } |
797 | |
|
798 | 0 | if (nTokens >= 6 && !EQUAL(papszTokens[5], "#")) |
799 | 0 | poBand->SetMetadataItem("STRETCHMIN", papszTokens[5], |
800 | 0 | "RENDERING_HINTS"); |
801 | |
|
802 | 0 | if (nTokens >= 7 && !EQUAL(papszTokens[6], "#")) |
803 | 0 | poBand->SetMetadataItem("STRETCHMAX", papszTokens[6], |
804 | 0 | "RENDERING_HINTS"); |
805 | 0 | } |
806 | 0 | } |
807 | | |
808 | 0 | CSLDestroy(papszTokens); |
809 | 0 | } |
810 | |
|
811 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
812 | |
|
813 | 0 | return CE_None; |
814 | 0 | } |
815 | | |
816 | | /************************************************************************/ |
817 | | /* GetImageRepFilename() */ |
818 | | /************************************************************************/ |
819 | | |
820 | | // Check for IMAGE.REP (Spatiocarte Defense 1.0) or name_of_image.rep |
821 | | // if it is a GIS-GeoSPOT image. |
822 | | // For the specification of SPDF (in French), see |
823 | | // http://eden.ign.fr/download/pub/doc/emabgi/spdf10.pdf/download |
824 | | |
825 | | CPLString EHdrDataset::GetImageRepFilename(const char *pszFilename) |
826 | 0 | { |
827 | |
|
828 | 0 | const CPLString osPath = CPLGetPathSafe(pszFilename); |
829 | 0 | const CPLString osName = CPLGetBasenameSafe(pszFilename); |
830 | 0 | CPLString osREPFilename = CPLFormCIFilenameSafe(osPath, osName, "rep"); |
831 | |
|
832 | 0 | VSIStatBufL sStatBuf; |
833 | 0 | if (VSIStatExL(osREPFilename.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0) |
834 | 0 | return osREPFilename; |
835 | | |
836 | 0 | if (EQUAL(CPLGetFilename(pszFilename), "imspatio.bil") || |
837 | 0 | EQUAL(CPLGetFilename(pszFilename), "haspatio.bil")) |
838 | 0 | { |
839 | 0 | CPLString osImageRepFilename( |
840 | 0 | CPLFormCIFilenameSafe(osPath, "image", "rep")); |
841 | 0 | if (VSIStatExL(osImageRepFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == |
842 | 0 | 0) |
843 | 0 | return osImageRepFilename; |
844 | | |
845 | | // Try in the upper directories if not found in the BIL image directory. |
846 | 0 | CPLString dirName(CPLGetDirnameSafe(osPath)); |
847 | 0 | if (CPLIsFilenameRelative(osPath.c_str())) |
848 | 0 | { |
849 | 0 | char *cwd = CPLGetCurrentDir(); |
850 | 0 | if (cwd) |
851 | 0 | { |
852 | 0 | dirName = CPLFormFilenameSafe(cwd, dirName.c_str(), nullptr); |
853 | 0 | CPLFree(cwd); |
854 | 0 | } |
855 | 0 | } |
856 | 0 | while (dirName[0] != 0 && EQUAL(dirName, ".") == FALSE && |
857 | 0 | EQUAL(dirName, "/") == FALSE) |
858 | 0 | { |
859 | 0 | osImageRepFilename = |
860 | 0 | CPLFormCIFilenameSafe(dirName.c_str(), "image", "rep"); |
861 | 0 | if (VSIStatExL(osImageRepFilename.c_str(), &sStatBuf, |
862 | 0 | VSI_STAT_EXISTS_FLAG) == 0) |
863 | 0 | return osImageRepFilename; |
864 | | |
865 | | // Don't try to recurse above the 'image' subdirectory. |
866 | 0 | if (EQUAL(dirName, "image")) |
867 | 0 | { |
868 | 0 | break; |
869 | 0 | } |
870 | 0 | dirName = CPLString(CPLGetDirnameSafe(dirName)); |
871 | 0 | } |
872 | 0 | } |
873 | 0 | return CPLString(); |
874 | 0 | } |
875 | | |
876 | | /************************************************************************/ |
877 | | /* GetFileList() */ |
878 | | /************************************************************************/ |
879 | | |
880 | | char **EHdrDataset::GetFileList() |
881 | | |
882 | 0 | { |
883 | 0 | const CPLString osPath = CPLGetPathSafe(GetDescription()); |
884 | 0 | const CPLString osName = CPLGetBasenameSafe(GetDescription()); |
885 | | |
886 | | // Main data file, etc. |
887 | 0 | char **papszFileList = GDALPamDataset::GetFileList(); |
888 | | |
889 | | // Header file. |
890 | 0 | CPLString osFilename = CPLFormCIFilenameSafe(osPath, osName, osHeaderExt); |
891 | 0 | papszFileList = CSLAddString(papszFileList, osFilename); |
892 | | |
893 | | // Statistics file |
894 | 0 | osFilename = CPLFormCIFilenameSafe(osPath, osName, "stx"); |
895 | 0 | VSIStatBufL sStatBuf; |
896 | 0 | if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0) |
897 | 0 | papszFileList = CSLAddString(papszFileList, osFilename); |
898 | | |
899 | | // color table file. |
900 | 0 | osFilename = CPLFormCIFilenameSafe(osPath, osName, "clr"); |
901 | 0 | if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0) |
902 | 0 | papszFileList = CSLAddString(papszFileList, osFilename); |
903 | | |
904 | | // projections file. |
905 | 0 | osFilename = CPLFormCIFilenameSafe(osPath, osName, "prj"); |
906 | 0 | if (VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0) |
907 | 0 | papszFileList = CSLAddString(papszFileList, osFilename); |
908 | |
|
909 | 0 | const CPLString imageRepFilename = GetImageRepFilename(GetDescription()); |
910 | 0 | if (!imageRepFilename.empty()) |
911 | 0 | papszFileList = CSLAddString(papszFileList, imageRepFilename.c_str()); |
912 | |
|
913 | 0 | return papszFileList; |
914 | 0 | } |
915 | | |
916 | | /************************************************************************/ |
917 | | /* Open() */ |
918 | | /************************************************************************/ |
919 | | |
920 | | GDALDataset *EHdrDataset::Open(GDALOpenInfo *poOpenInfo) |
921 | | |
922 | 475k | { |
923 | 475k | return Open(poOpenInfo, true); |
924 | 475k | } |
925 | | |
926 | | GDALDataset *EHdrDataset::Open(GDALOpenInfo *poOpenInfo, bool bFileSizeCheck) |
927 | | |
928 | 475k | { |
929 | | // Assume the caller is pointing to the binary (i.e. .bil) file. |
930 | 475k | if (poOpenInfo->nHeaderBytes < 2 || poOpenInfo->fpL == nullptr) |
931 | 473k | return nullptr; |
932 | | |
933 | | // Tear apart the filename to form a .HDR filename. |
934 | 1.61k | const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename); |
935 | 1.61k | const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename); |
936 | | |
937 | 1.61k | const char *pszHeaderExt = "hdr"; |
938 | 1.61k | if (poOpenInfo->IsExtensionEqualToCI("SRC") && osName.size() == 7 && |
939 | 1.61k | (osName[0] == 'e' || osName[0] == 'E' || osName[0] == 'w' || |
940 | 0 | osName[0] == 'W') && |
941 | 1.61k | (osName[4] == 'n' || osName[4] == 'N' || osName[4] == 's' || |
942 | 0 | osName[4] == 'S')) |
943 | 0 | { |
944 | | // It is a GTOPO30 or SRTM30 source file, whose header extension is .sch |
945 | | // see http://dds.cr.usgs.gov/srtm/version1/SRTM30/GTOPO30_Documentation |
946 | 0 | pszHeaderExt = "sch"; |
947 | 0 | } |
948 | | |
949 | 1.61k | char **papszSiblingFiles = poOpenInfo->GetSiblingFiles(); |
950 | 1.61k | CPLString osHDRFilename; |
951 | 1.61k | if (papszSiblingFiles) |
952 | 1.59k | { |
953 | 1.59k | const int iFile = CSLFindString( |
954 | 1.59k | papszSiblingFiles, |
955 | 1.59k | CPLFormFilenameSafe(nullptr, osName, pszHeaderExt).c_str()); |
956 | 1.59k | if (iFile < 0) // Return if there is no corresponding .hdr file. |
957 | 1.59k | return nullptr; |
958 | | |
959 | 0 | osHDRFilename = |
960 | 0 | CPLFormFilenameSafe(osPath, papszSiblingFiles[iFile], nullptr); |
961 | 0 | } |
962 | 25 | else |
963 | 25 | { |
964 | 25 | osHDRFilename = CPLFormCIFilenameSafe(osPath, osName, pszHeaderExt); |
965 | 25 | } |
966 | | |
967 | 25 | const bool bSelectedHDR = EQUAL(osHDRFilename, poOpenInfo->pszFilename); |
968 | | |
969 | | // Do we have a .hdr file? |
970 | 25 | VSILFILE *fp = VSIFOpenL(osHDRFilename, "r"); |
971 | 25 | if (fp == nullptr) |
972 | 25 | { |
973 | 25 | return nullptr; |
974 | 25 | } |
975 | | |
976 | | // Is this file an ESRI header file? Read a few lines of text |
977 | | // searching for something starting with nrows or ncols. |
978 | 0 | int nRows = -1; |
979 | 0 | int nCols = -1; |
980 | 0 | int l_nBands = 1; |
981 | 0 | int nSkipBytes = 0; |
982 | 0 | double dfULXMap = 0.5; |
983 | 0 | double dfULYMap = 0.5; |
984 | 0 | double dfYLLCorner = -123.456; |
985 | 0 | int bCenter = TRUE; |
986 | 0 | double dfXDim = 1.0; |
987 | 0 | double dfYDim = 1.0; |
988 | 0 | double dfNoData = 0.0; |
989 | 0 | int nLineCount = 0; |
990 | 0 | int bNoDataSet = FALSE; |
991 | 0 | GDALDataType eDataType = GDT_Byte; |
992 | 0 | int nBits = -1; |
993 | 0 | char chByteOrder = 'M'; |
994 | 0 | char chPixelType = 'N'; // Not defined. |
995 | 0 | char szLayout[10] = "BIL"; |
996 | 0 | char **papszHDR = nullptr; |
997 | 0 | int bHasInternalProjection = FALSE; |
998 | 0 | int bHasMin = FALSE; |
999 | 0 | int bHasMax = FALSE; |
1000 | 0 | double dfMin = 0; |
1001 | 0 | double dfMax = 0; |
1002 | |
|
1003 | 0 | const char *pszLine = nullptr; |
1004 | 0 | while ((pszLine = CPLReadLineL(fp)) != nullptr) |
1005 | 0 | { |
1006 | 0 | nLineCount++; |
1007 | |
|
1008 | 0 | if (nLineCount > 50 || strlen(pszLine) > 1000) |
1009 | 0 | break; |
1010 | | |
1011 | 0 | papszHDR = CSLAddString(papszHDR, pszLine); |
1012 | |
|
1013 | 0 | char **papszTokens = |
1014 | 0 | CSLTokenizeStringComplex(pszLine, " \t", TRUE, FALSE); |
1015 | 0 | if (CSLCount(papszTokens) < 2) |
1016 | 0 | { |
1017 | 0 | CSLDestroy(papszTokens); |
1018 | 0 | continue; |
1019 | 0 | } |
1020 | | |
1021 | 0 | if (EQUAL(papszTokens[0], "ncols")) |
1022 | 0 | { |
1023 | 0 | nCols = atoi(papszTokens[1]); |
1024 | 0 | } |
1025 | 0 | else if (EQUAL(papszTokens[0], "nrows")) |
1026 | 0 | { |
1027 | 0 | nRows = atoi(papszTokens[1]); |
1028 | 0 | } |
1029 | 0 | else if (EQUAL(papszTokens[0], "skipbytes")) |
1030 | 0 | { |
1031 | 0 | nSkipBytes = atoi(papszTokens[1]); |
1032 | 0 | } |
1033 | 0 | else if (EQUAL(papszTokens[0], "ulxmap") || |
1034 | 0 | EQUAL(papszTokens[0], "xllcorner") || |
1035 | 0 | EQUAL(papszTokens[0], "xllcenter")) |
1036 | 0 | { |
1037 | 0 | dfULXMap = CPLAtofM(papszTokens[1]); |
1038 | 0 | if (EQUAL(papszTokens[0], "xllcorner")) |
1039 | 0 | bCenter = FALSE; |
1040 | 0 | } |
1041 | 0 | else if (EQUAL(papszTokens[0], "ulymap")) |
1042 | 0 | { |
1043 | 0 | dfULYMap = CPLAtofM(papszTokens[1]); |
1044 | 0 | } |
1045 | 0 | else if (EQUAL(papszTokens[0], "yllcorner") || |
1046 | 0 | EQUAL(papszTokens[0], "yllcenter")) |
1047 | 0 | { |
1048 | 0 | dfYLLCorner = CPLAtofM(papszTokens[1]); |
1049 | 0 | if (EQUAL(papszTokens[0], "yllcorner")) |
1050 | 0 | bCenter = FALSE; |
1051 | 0 | } |
1052 | 0 | else if (EQUAL(papszTokens[0], "xdim")) |
1053 | 0 | { |
1054 | 0 | dfXDim = CPLAtofM(papszTokens[1]); |
1055 | 0 | } |
1056 | 0 | else if (EQUAL(papszTokens[0], "ydim")) |
1057 | 0 | { |
1058 | 0 | dfYDim = CPLAtofM(papszTokens[1]); |
1059 | 0 | } |
1060 | 0 | else if (EQUAL(papszTokens[0], "cellsize")) |
1061 | 0 | { |
1062 | 0 | dfXDim = CPLAtofM(papszTokens[1]); |
1063 | 0 | dfYDim = dfXDim; |
1064 | 0 | } |
1065 | 0 | else if (EQUAL(papszTokens[0], "nbands")) |
1066 | 0 | { |
1067 | 0 | l_nBands = atoi(papszTokens[1]); |
1068 | 0 | } |
1069 | 0 | else if (EQUAL(papszTokens[0], "layout")) |
1070 | 0 | { |
1071 | 0 | snprintf(szLayout, sizeof(szLayout), "%s", papszTokens[1]); |
1072 | 0 | } |
1073 | 0 | else if (EQUAL(papszTokens[0], "NODATA_value") || |
1074 | 0 | EQUAL(papszTokens[0], "NODATA")) |
1075 | 0 | { |
1076 | 0 | dfNoData = CPLAtofM(papszTokens[1]); |
1077 | 0 | bNoDataSet = TRUE; |
1078 | 0 | } |
1079 | 0 | else if (EQUAL(papszTokens[0], "NBITS")) |
1080 | 0 | { |
1081 | 0 | nBits = atoi(papszTokens[1]); |
1082 | 0 | } |
1083 | 0 | else if (EQUAL(papszTokens[0], "PIXELTYPE")) |
1084 | 0 | { |
1085 | 0 | chPixelType = static_cast<char>( |
1086 | 0 | toupper(static_cast<unsigned char>(papszTokens[1][0]))); |
1087 | 0 | } |
1088 | 0 | else if (EQUAL(papszTokens[0], "byteorder")) |
1089 | 0 | { |
1090 | 0 | chByteOrder = static_cast<char>( |
1091 | 0 | toupper(static_cast<unsigned char>(papszTokens[1][0]))); |
1092 | 0 | } |
1093 | | |
1094 | | // http://www.worldclim.org/futdown.htm have the projection extensions |
1095 | 0 | else if (EQUAL(papszTokens[0], "Projection")) |
1096 | 0 | { |
1097 | 0 | bHasInternalProjection = TRUE; |
1098 | 0 | } |
1099 | 0 | else if (EQUAL(papszTokens[0], "MinValue") || |
1100 | 0 | EQUAL(papszTokens[0], "MIN_VALUE")) |
1101 | 0 | { |
1102 | 0 | dfMin = CPLAtofM(papszTokens[1]); |
1103 | 0 | bHasMin = TRUE; |
1104 | 0 | } |
1105 | 0 | else if (EQUAL(papszTokens[0], "MaxValue") || |
1106 | 0 | EQUAL(papszTokens[0], "MAX_VALUE")) |
1107 | 0 | { |
1108 | 0 | dfMax = CPLAtofM(papszTokens[1]); |
1109 | 0 | bHasMax = TRUE; |
1110 | 0 | } |
1111 | |
|
1112 | 0 | CSLDestroy(papszTokens); |
1113 | 0 | } |
1114 | |
|
1115 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
1116 | | |
1117 | | // Did we get the required keywords? If not, return with this never having |
1118 | | // been considered to be a match. This isn't an error! |
1119 | 0 | if (nRows == -1 || nCols == -1) |
1120 | 0 | { |
1121 | 0 | CSLDestroy(papszHDR); |
1122 | 0 | return nullptr; |
1123 | 0 | } |
1124 | | |
1125 | 0 | if (!GDALCheckDatasetDimensions(nCols, nRows) || |
1126 | 0 | !GDALCheckBandCount(l_nBands, FALSE)) |
1127 | 0 | { |
1128 | 0 | CSLDestroy(papszHDR); |
1129 | 0 | return nullptr; |
1130 | 0 | } |
1131 | | |
1132 | | // Has the caller selected the .hdr file to open? |
1133 | 0 | if (bSelectedHDR) |
1134 | 0 | { |
1135 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1136 | 0 | "The selected file is an ESRI BIL header file, but to " |
1137 | 0 | "open ESRI BIL datasets, the data file should be selected " |
1138 | 0 | "instead of the .hdr file. Please try again selecting " |
1139 | 0 | "the data file (often with the extension .bil) corresponding " |
1140 | 0 | "to the header file: %s", |
1141 | 0 | poOpenInfo->pszFilename); |
1142 | 0 | CSLDestroy(papszHDR); |
1143 | 0 | return nullptr; |
1144 | 0 | } |
1145 | | |
1146 | | // If we aren't sure of the file type, check the data file size. If it is 4 |
1147 | | // bytes or more per pixel then we assume it is floating point data. |
1148 | 0 | if (nBits == -1 && chPixelType == 'N') |
1149 | 0 | { |
1150 | 0 | VSIStatBufL sStatBuf; |
1151 | 0 | if (VSIStatL(poOpenInfo->pszFilename, &sStatBuf) == 0) |
1152 | 0 | { |
1153 | 0 | const size_t nBytes = static_cast<size_t>(sStatBuf.st_size / nCols / |
1154 | 0 | nRows / l_nBands); |
1155 | 0 | if (nBytes > 0 && nBytes != 3) |
1156 | 0 | nBits = static_cast<int>(nBytes * 8); |
1157 | |
|
1158 | 0 | if (nBytes == 4) |
1159 | 0 | chPixelType = 'F'; |
1160 | 0 | } |
1161 | 0 | } |
1162 | | |
1163 | | // If the extension is FLT it is likely a floating point file. |
1164 | 0 | if (chPixelType == 'N') |
1165 | 0 | { |
1166 | 0 | if (poOpenInfo->IsExtensionEqualToCI("FLT")) |
1167 | 0 | chPixelType = 'F'; |
1168 | 0 | } |
1169 | | |
1170 | | // If we have a negative nodata value, assume that the |
1171 | | // pixel type is signed. This is necessary for datasets from |
1172 | | // http://www.worldclim.org/futdown.htm |
1173 | |
|
1174 | 0 | if (bNoDataSet && dfNoData < 0 && chPixelType == 'N') |
1175 | 0 | { |
1176 | 0 | chPixelType = 'S'; |
1177 | 0 | } |
1178 | |
|
1179 | 0 | auto poDS = std::make_unique<EHdrDataset>(); |
1180 | |
|
1181 | 0 | poDS->osHeaderExt = pszHeaderExt; |
1182 | |
|
1183 | 0 | poDS->nRasterXSize = nCols; |
1184 | 0 | poDS->nRasterYSize = nRows; |
1185 | 0 | poDS->papszHDR = papszHDR; |
1186 | 0 | std::swap(poDS->fpImage, poOpenInfo->fpL); |
1187 | 0 | poDS->eAccess = poOpenInfo->eAccess; |
1188 | | |
1189 | | // Figure out the data type. |
1190 | 0 | if (nBits == 16) |
1191 | 0 | { |
1192 | 0 | if (chPixelType == 'S') |
1193 | 0 | eDataType = GDT_Int16; |
1194 | 0 | else |
1195 | 0 | eDataType = GDT_UInt16; // Default |
1196 | 0 | } |
1197 | 0 | else if (nBits == 32) |
1198 | 0 | { |
1199 | 0 | if (chPixelType == 'S') |
1200 | 0 | eDataType = GDT_Int32; |
1201 | 0 | else if (chPixelType == 'F') |
1202 | 0 | eDataType = GDT_Float32; |
1203 | 0 | else |
1204 | 0 | eDataType = GDT_UInt32; // Default |
1205 | 0 | } |
1206 | 0 | else if (nBits >= 1 && nBits <= 8) |
1207 | 0 | { |
1208 | 0 | if (chPixelType == 'S') |
1209 | 0 | eDataType = GDT_Int8; |
1210 | 0 | else |
1211 | 0 | eDataType = GDT_Byte; |
1212 | 0 | nBits = 8; |
1213 | 0 | } |
1214 | 0 | else if (nBits == -1) |
1215 | 0 | { |
1216 | 0 | if (chPixelType == 'F') |
1217 | 0 | { |
1218 | 0 | eDataType = GDT_Float32; |
1219 | 0 | nBits = 32; |
1220 | 0 | } |
1221 | 0 | else |
1222 | 0 | { |
1223 | 0 | eDataType = GDT_Byte; |
1224 | 0 | nBits = 8; |
1225 | 0 | } |
1226 | 0 | } |
1227 | 0 | else |
1228 | 0 | { |
1229 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1230 | 0 | "EHdr driver does not support %d NBITS value.", nBits); |
1231 | 0 | return nullptr; |
1232 | 0 | } |
1233 | | |
1234 | | // Compute the line offset. |
1235 | 0 | const int nItemSize = GDALGetDataTypeSizeBytes(eDataType); |
1236 | 0 | CPLAssert(nItemSize != 0); |
1237 | 0 | CPLAssert(l_nBands != 0); |
1238 | |
|
1239 | 0 | int nPixelOffset = 0; |
1240 | 0 | int nLineOffset = 0; |
1241 | 0 | vsi_l_offset nBandOffset = 0; |
1242 | |
|
1243 | 0 | if (EQUAL(szLayout, "BIP")) |
1244 | 0 | { |
1245 | 0 | if (nCols > std::numeric_limits<int>::max() / (nItemSize * l_nBands)) |
1246 | 0 | { |
1247 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred."); |
1248 | 0 | return nullptr; |
1249 | 0 | } |
1250 | 0 | nPixelOffset = nItemSize * l_nBands; |
1251 | 0 | nLineOffset = nPixelOffset * nCols; |
1252 | 0 | nBandOffset = static_cast<vsi_l_offset>(nItemSize); |
1253 | 0 | } |
1254 | 0 | else if (EQUAL(szLayout, "BSQ")) |
1255 | 0 | { |
1256 | 0 | if (nCols > std::numeric_limits<int>::max() / nItemSize) |
1257 | 0 | { |
1258 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred."); |
1259 | 0 | return nullptr; |
1260 | 0 | } |
1261 | 0 | nPixelOffset = nItemSize; |
1262 | 0 | nLineOffset = nPixelOffset * nCols; |
1263 | 0 | nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows; |
1264 | 0 | } |
1265 | 0 | else |
1266 | 0 | { |
1267 | | // Assume BIL. |
1268 | 0 | if (nCols > std::numeric_limits<int>::max() / (nItemSize * l_nBands)) |
1269 | 0 | { |
1270 | 0 | CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred."); |
1271 | 0 | return nullptr; |
1272 | 0 | } |
1273 | 0 | nPixelOffset = nItemSize; |
1274 | 0 | nLineOffset = nItemSize * l_nBands * nCols; |
1275 | 0 | nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nCols; |
1276 | 0 | } |
1277 | | |
1278 | 0 | if (nBits >= 8 && bFileSizeCheck && |
1279 | 0 | !RAWDatasetCheckMemoryUsage( |
1280 | 0 | poDS->nRasterXSize, poDS->nRasterYSize, l_nBands, nItemSize, |
1281 | 0 | nPixelOffset, nLineOffset, nSkipBytes, nBandOffset, poDS->fpImage)) |
1282 | 0 | { |
1283 | 0 | return nullptr; |
1284 | 0 | } |
1285 | | |
1286 | 0 | poDS->SetDescription(poOpenInfo->pszFilename); |
1287 | 0 | poDS->PamInitialize(); |
1288 | | |
1289 | | // Create band information objects. |
1290 | 0 | for (int i = 0; i < l_nBands; i++) |
1291 | 0 | { |
1292 | 0 | auto poBand = std::make_unique<EHdrRasterBand>( |
1293 | 0 | poDS.get(), i + 1, poDS->fpImage, nSkipBytes + nBandOffset * i, |
1294 | 0 | nPixelOffset, nLineOffset, eDataType, |
1295 | 0 | chByteOrder == 'I' || chByteOrder == 'L' |
1296 | 0 | ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN |
1297 | 0 | : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN, |
1298 | 0 | nBits); |
1299 | 0 | if (!poBand->IsValid()) |
1300 | 0 | return nullptr; |
1301 | | |
1302 | 0 | poBand->bNoDataSet = bNoDataSet; |
1303 | 0 | poBand->dfNoData = dfNoData; |
1304 | |
|
1305 | 0 | if (bHasMin && bHasMax) |
1306 | 0 | { |
1307 | 0 | poBand->dfMin = dfMin; |
1308 | 0 | poBand->dfMax = dfMax; |
1309 | 0 | poBand->minmaxmeanstddev = HAS_MIN_FLAG | HAS_MAX_FLAG; |
1310 | 0 | } |
1311 | |
|
1312 | 0 | poDS->SetBand(i + 1, std::move(poBand)); |
1313 | 0 | } |
1314 | | |
1315 | | // If we didn't get bounds in the .hdr, look for a worldfile. |
1316 | 0 | if (dfYLLCorner != -123.456) |
1317 | 0 | { |
1318 | 0 | if (bCenter) |
1319 | 0 | dfULYMap = dfYLLCorner + (nRows - 1) * dfYDim; |
1320 | 0 | else |
1321 | 0 | dfULYMap = dfYLLCorner + nRows * dfYDim; |
1322 | 0 | } |
1323 | |
|
1324 | 0 | if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0) |
1325 | 0 | { |
1326 | 0 | poDS->bGotTransform = true; |
1327 | |
|
1328 | 0 | if (bCenter) |
1329 | 0 | { |
1330 | 0 | poDS->adfGeoTransform[0] = dfULXMap - dfXDim * 0.5; |
1331 | 0 | poDS->adfGeoTransform[1] = dfXDim; |
1332 | 0 | poDS->adfGeoTransform[2] = 0.0; |
1333 | 0 | poDS->adfGeoTransform[3] = dfULYMap + dfYDim * 0.5; |
1334 | 0 | poDS->adfGeoTransform[4] = 0.0; |
1335 | 0 | poDS->adfGeoTransform[5] = -dfYDim; |
1336 | 0 | } |
1337 | 0 | else |
1338 | 0 | { |
1339 | 0 | poDS->adfGeoTransform[0] = dfULXMap; |
1340 | 0 | poDS->adfGeoTransform[1] = dfXDim; |
1341 | 0 | poDS->adfGeoTransform[2] = 0.0; |
1342 | 0 | poDS->adfGeoTransform[3] = dfULYMap; |
1343 | 0 | poDS->adfGeoTransform[4] = 0.0; |
1344 | 0 | poDS->adfGeoTransform[5] = -dfYDim; |
1345 | 0 | } |
1346 | 0 | } |
1347 | |
|
1348 | 0 | if (!poDS->bGotTransform) |
1349 | 0 | poDS->bGotTransform = CPL_TO_BOOL(GDALReadWorldFile( |
1350 | 0 | poOpenInfo->pszFilename, nullptr, poDS->adfGeoTransform)); |
1351 | |
|
1352 | 0 | if (!poDS->bGotTransform) |
1353 | 0 | poDS->bGotTransform = CPL_TO_BOOL(GDALReadWorldFile( |
1354 | 0 | poOpenInfo->pszFilename, "wld", poDS->adfGeoTransform)); |
1355 | | |
1356 | | // Check for a .prj file. |
1357 | 0 | std::string osPrjFilename = CPLFormCIFilenameSafe(osPath, osName, "prj"); |
1358 | |
|
1359 | 0 | fp = VSIFOpenL(osPrjFilename.c_str(), "r"); |
1360 | | |
1361 | | // .hdr files from http://www.worldclim.org/futdown.htm have the projection |
1362 | | // info in the .hdr file itself. |
1363 | 0 | if (fp == nullptr && bHasInternalProjection) |
1364 | 0 | { |
1365 | 0 | osPrjFilename = std::move(osHDRFilename); |
1366 | 0 | fp = VSIFOpenL(osPrjFilename.c_str(), "r"); |
1367 | 0 | } |
1368 | |
|
1369 | 0 | if (fp != nullptr) |
1370 | 0 | { |
1371 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
1372 | 0 | fp = nullptr; |
1373 | |
|
1374 | 0 | char **papszLines = CSLLoad(osPrjFilename.c_str()); |
1375 | |
|
1376 | 0 | if (poDS->m_oSRS.importFromESRI(papszLines) == OGRERR_NONE) |
1377 | 0 | { |
1378 | | // If geographic values are in seconds, we must transform. |
1379 | | // Is there a code for minutes too? |
1380 | 0 | char szResult[80] = {'\0'}; |
1381 | 0 | if (poDS->m_oSRS.IsGeographic() && |
1382 | 0 | EQUAL(OSR_GDS(szResult, sizeof(szResult), papszLines, "Units", |
1383 | 0 | ""), |
1384 | 0 | "DS")) |
1385 | 0 | { |
1386 | 0 | poDS->adfGeoTransform[0] /= 3600.0; |
1387 | 0 | poDS->adfGeoTransform[1] /= 3600.0; |
1388 | 0 | poDS->adfGeoTransform[2] /= 3600.0; |
1389 | 0 | poDS->adfGeoTransform[3] /= 3600.0; |
1390 | 0 | poDS->adfGeoTransform[4] /= 3600.0; |
1391 | 0 | poDS->adfGeoTransform[5] /= 3600.0; |
1392 | 0 | } |
1393 | 0 | } |
1394 | 0 | else |
1395 | 0 | { |
1396 | 0 | poDS->m_oSRS.Clear(); |
1397 | 0 | } |
1398 | |
|
1399 | 0 | CSLDestroy(papszLines); |
1400 | 0 | } |
1401 | 0 | else |
1402 | 0 | { |
1403 | | // Check for IMAGE.REP (Spatiocarte Defense 1.0) or name_of_image.rep |
1404 | | // if it is a GIS-GeoSPOT image |
1405 | | // For the specification of SPDF (in French), see |
1406 | | // http://eden.ign.fr/download/pub/doc/emabgi/spdf10.pdf/download |
1407 | 0 | const CPLString szImageRepFilename = |
1408 | 0 | GetImageRepFilename(poOpenInfo->pszFilename); |
1409 | 0 | if (!szImageRepFilename.empty()) |
1410 | 0 | { |
1411 | 0 | fp = VSIFOpenL(szImageRepFilename.c_str(), "r"); |
1412 | 0 | } |
1413 | 0 | if (fp != nullptr) |
1414 | 0 | { |
1415 | 0 | bool bUTM = false; |
1416 | 0 | bool bWGS84 = false; |
1417 | 0 | int bNorth = FALSE; |
1418 | 0 | bool bSouth = false; |
1419 | 0 | int utmZone = 0; |
1420 | |
|
1421 | 0 | while ((pszLine = CPLReadLineL(fp)) != nullptr) |
1422 | 0 | { |
1423 | 0 | if (STARTS_WITH(pszLine, "PROJ_ID") && strstr(pszLine, "UTM")) |
1424 | 0 | { |
1425 | 0 | bUTM = true; |
1426 | 0 | } |
1427 | 0 | else if (STARTS_WITH(pszLine, "PROJ_ZONE")) |
1428 | 0 | { |
1429 | 0 | const char *c = strchr(pszLine, '"'); |
1430 | 0 | if (c) |
1431 | 0 | { |
1432 | 0 | c++; |
1433 | 0 | if (*c >= '0' && *c <= '9') |
1434 | 0 | { |
1435 | 0 | utmZone = atoi(c); |
1436 | 0 | if (utmZone >= 1 && utmZone <= 60) |
1437 | 0 | { |
1438 | 0 | if (strstr(pszLine, "Nord") || |
1439 | 0 | strstr(pszLine, "NORD")) |
1440 | 0 | { |
1441 | 0 | bNorth = TRUE; |
1442 | 0 | } |
1443 | 0 | else if (strstr(pszLine, "Sud") || |
1444 | 0 | strstr(pszLine, "SUD")) |
1445 | 0 | { |
1446 | 0 | bSouth = true; |
1447 | 0 | } |
1448 | 0 | } |
1449 | 0 | } |
1450 | 0 | } |
1451 | 0 | } |
1452 | 0 | else if (STARTS_WITH(pszLine, "PROJ_CODE") && |
1453 | 0 | strstr(pszLine, "FR-MINDEF")) |
1454 | 0 | { |
1455 | 0 | const char *c = strchr(pszLine, 'A'); |
1456 | 0 | if (c) |
1457 | 0 | { |
1458 | 0 | c++; |
1459 | 0 | if (*c >= '0' && *c <= '9') |
1460 | 0 | { |
1461 | 0 | utmZone = atoi(c); |
1462 | 0 | if (utmZone >= 1 && utmZone <= 60) |
1463 | 0 | { |
1464 | 0 | if (c[1] == 'N' || |
1465 | 0 | (c[1] != '\0' && c[2] == 'N')) |
1466 | 0 | { |
1467 | 0 | bNorth = TRUE; |
1468 | 0 | } |
1469 | 0 | else if (c[1] == 'S' || |
1470 | 0 | (c[1] != '\0' && c[2] == 'S')) |
1471 | 0 | { |
1472 | 0 | bSouth = true; |
1473 | 0 | } |
1474 | 0 | } |
1475 | 0 | } |
1476 | 0 | } |
1477 | 0 | } |
1478 | 0 | else if (STARTS_WITH(pszLine, "HORIZ_DATUM") && |
1479 | 0 | (strstr(pszLine, "WGS 84") || |
1480 | 0 | strstr(pszLine, "WGS84"))) |
1481 | 0 | { |
1482 | 0 | bWGS84 = true; |
1483 | 0 | } |
1484 | 0 | else if (STARTS_WITH(pszLine, "MAP_NUMBER")) |
1485 | 0 | { |
1486 | 0 | const char *c = strchr(pszLine, '"'); |
1487 | 0 | if (c) |
1488 | 0 | { |
1489 | 0 | char *pszMapNumber = CPLStrdup(c + 1); |
1490 | 0 | char *c2 = strchr(pszMapNumber, '"'); |
1491 | 0 | if (c2) |
1492 | 0 | *c2 = 0; |
1493 | 0 | poDS->SetMetadataItem("SPDF_MAP_NUMBER", pszMapNumber); |
1494 | 0 | CPLFree(pszMapNumber); |
1495 | 0 | } |
1496 | 0 | } |
1497 | 0 | else if (STARTS_WITH(pszLine, "PRODUCTION_DATE")) |
1498 | 0 | { |
1499 | 0 | const char *c = pszLine + strlen("PRODUCTION_DATE"); |
1500 | 0 | while (*c == ' ') |
1501 | 0 | c++; |
1502 | 0 | if (*c) |
1503 | 0 | { |
1504 | 0 | poDS->SetMetadataItem("SPDF_PRODUCTION_DATE", c); |
1505 | 0 | } |
1506 | 0 | } |
1507 | 0 | } |
1508 | |
|
1509 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
1510 | |
|
1511 | 0 | if (utmZone >= 1 && utmZone <= 60 && bUTM && bWGS84 && |
1512 | 0 | (bNorth || bSouth)) |
1513 | 0 | { |
1514 | 0 | char projCSStr[64] = {'\0'}; |
1515 | 0 | snprintf(projCSStr, sizeof(projCSStr), "WGS 84 / UTM zone %d%c", |
1516 | 0 | utmZone, (bNorth) ? 'N' : 'S'); |
1517 | |
|
1518 | 0 | poDS->m_oSRS.SetProjCS(projCSStr); |
1519 | 0 | poDS->m_oSRS.SetWellKnownGeogCS("WGS84"); |
1520 | 0 | poDS->m_oSRS.SetUTM(utmZone, bNorth); |
1521 | 0 | poDS->m_oSRS.SetAuthority("PROJCS", "EPSG", |
1522 | 0 | (bNorth ? 32600 : 32700) + utmZone); |
1523 | 0 | poDS->m_oSRS.AutoIdentifyEPSG(); |
1524 | 0 | } |
1525 | 0 | else |
1526 | 0 | { |
1527 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1528 | 0 | "Cannot retrieve projection from IMAGE.REP"); |
1529 | 0 | } |
1530 | 0 | } |
1531 | 0 | } |
1532 | | |
1533 | | // Check for a color table. |
1534 | 0 | const std::string osCLRFilename = |
1535 | 0 | CPLFormCIFilenameSafe(osPath, osName, "clr"); |
1536 | | |
1537 | | // Only read the .clr for byte, int16 or uint16 bands. |
1538 | 0 | if (nItemSize <= 2) |
1539 | 0 | fp = VSIFOpenL(osCLRFilename.c_str(), "r"); |
1540 | 0 | else |
1541 | 0 | fp = nullptr; |
1542 | |
|
1543 | 0 | if (fp != nullptr) |
1544 | 0 | { |
1545 | 0 | std::shared_ptr<GDALRasterAttributeTable> poRat( |
1546 | 0 | new GDALDefaultRasterAttributeTable()); |
1547 | 0 | poRat->CreateColumn("Value", GFT_Integer, GFU_Generic); |
1548 | 0 | poRat->CreateColumn("Red", GFT_Integer, GFU_Red); |
1549 | 0 | poRat->CreateColumn("Green", GFT_Integer, GFU_Green); |
1550 | 0 | poRat->CreateColumn("Blue", GFT_Integer, GFU_Blue); |
1551 | |
|
1552 | 0 | poDS->m_poColorTable.reset(new GDALColorTable()); |
1553 | |
|
1554 | 0 | bool bHasFoundNonCTValues = false; |
1555 | 0 | int nRatRow = 0; |
1556 | |
|
1557 | 0 | while (true) |
1558 | 0 | { |
1559 | 0 | pszLine = CPLReadLineL(fp); |
1560 | 0 | if (!pszLine) |
1561 | 0 | break; |
1562 | | |
1563 | 0 | if (*pszLine == '#' || *pszLine == '!') |
1564 | 0 | continue; |
1565 | | |
1566 | 0 | char **papszValues = |
1567 | 0 | CSLTokenizeString2(pszLine, "\t ", CSLT_HONOURSTRINGS); |
1568 | |
|
1569 | 0 | if (CSLCount(papszValues) >= 4) |
1570 | 0 | { |
1571 | 0 | const int nIndex = atoi(papszValues[0]); |
1572 | 0 | poRat->SetValue(nRatRow, 0, nIndex); |
1573 | 0 | poRat->SetValue(nRatRow, 1, atoi(papszValues[1])); |
1574 | 0 | poRat->SetValue(nRatRow, 2, atoi(papszValues[2])); |
1575 | 0 | poRat->SetValue(nRatRow, 3, atoi(papszValues[3])); |
1576 | 0 | nRatRow++; |
1577 | |
|
1578 | 0 | if (nIndex >= 0 && nIndex < 65536) |
1579 | 0 | { |
1580 | 0 | const GDALColorEntry oEntry = { |
1581 | 0 | static_cast<short>(atoi(papszValues[1])), // Red |
1582 | 0 | static_cast<short>(atoi(papszValues[2])), // Green |
1583 | 0 | static_cast<short>(atoi(papszValues[3])), // Blue |
1584 | 0 | 255}; |
1585 | |
|
1586 | 0 | poDS->m_poColorTable->SetColorEntry(nIndex, &oEntry); |
1587 | 0 | } |
1588 | 0 | else |
1589 | 0 | { |
1590 | | // Negative values are valid. At least we can find use of |
1591 | | // them here: |
1592 | | // http://www.ngdc.noaa.gov/mgg/topo/elev/esri/clr/ |
1593 | | // But, there's no way of representing them with GDAL color |
1594 | | // table model. |
1595 | 0 | if (!bHasFoundNonCTValues) |
1596 | 0 | CPLDebug("EHdr", "Ignoring color index : %d", nIndex); |
1597 | 0 | bHasFoundNonCTValues = true; |
1598 | 0 | } |
1599 | 0 | } |
1600 | |
|
1601 | 0 | CSLDestroy(papszValues); |
1602 | 0 | } |
1603 | |
|
1604 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
1605 | |
|
1606 | 0 | if (bHasFoundNonCTValues) |
1607 | 0 | { |
1608 | 0 | poDS->m_poRAT.swap(poRat); |
1609 | 0 | } |
1610 | |
|
1611 | 0 | for (int i = 1; i <= poDS->nBands; i++) |
1612 | 0 | { |
1613 | 0 | EHdrRasterBand *poBand = |
1614 | 0 | cpl::down_cast<EHdrRasterBand *>(poDS->GetRasterBand(i)); |
1615 | 0 | poBand->m_poColorTable = poDS->m_poColorTable; |
1616 | 0 | poBand->m_poRAT = poDS->m_poRAT; |
1617 | 0 | poBand->SetColorInterpretation(GCI_PaletteIndex); |
1618 | 0 | } |
1619 | |
|
1620 | 0 | poDS->bCLRDirty = false; |
1621 | 0 | } |
1622 | | |
1623 | | // Read statistics (.STX). |
1624 | 0 | poDS->ReadSTX(); |
1625 | | |
1626 | | // Initialize any PAM information. |
1627 | 0 | poDS->TryLoadXML(); |
1628 | | |
1629 | | // Check for overviews. |
1630 | 0 | poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename); |
1631 | |
|
1632 | 0 | return poDS.release(); |
1633 | 0 | } |
1634 | | |
1635 | | /************************************************************************/ |
1636 | | /* Create() */ |
1637 | | /************************************************************************/ |
1638 | | |
1639 | | GDALDataset *EHdrDataset::Create(const char *pszFilename, int nXSize, |
1640 | | int nYSize, int nBandsIn, GDALDataType eType, |
1641 | | char **papszParamList) |
1642 | | |
1643 | 0 | { |
1644 | | // Verify input options. |
1645 | 0 | if (nBandsIn <= 0) |
1646 | 0 | { |
1647 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1648 | 0 | "EHdr driver does not support %d bands.", nBandsIn); |
1649 | 0 | return nullptr; |
1650 | 0 | } |
1651 | | |
1652 | 0 | if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_Float32 && |
1653 | 0 | eType != GDT_UInt16 && eType != GDT_Int16 && eType != GDT_Int32 && |
1654 | 0 | eType != GDT_UInt32) |
1655 | 0 | { |
1656 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
1657 | 0 | "Attempt to create ESRI .hdr labelled dataset with an illegal" |
1658 | 0 | "data type (%s).", |
1659 | 0 | GDALGetDataTypeName(eType)); |
1660 | |
|
1661 | 0 | return nullptr; |
1662 | 0 | } |
1663 | | |
1664 | | // Try to create the file. |
1665 | 0 | VSILFILE *fp = VSIFOpenL(pszFilename, "wb"); |
1666 | |
|
1667 | 0 | if (fp == nullptr) |
1668 | 0 | { |
1669 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
1670 | 0 | "Attempt to create file `%s' failed.", pszFilename); |
1671 | 0 | return nullptr; |
1672 | 0 | } |
1673 | | |
1674 | | // Just write out a couple of bytes to establish the binary |
1675 | | // file, and then close it. |
1676 | 0 | bool bOK = VSIFWriteL(reinterpret_cast<void *>(const_cast<char *>("\0\0")), |
1677 | 0 | 2, 1, fp) == 1; |
1678 | 0 | if (VSIFCloseL(fp) != 0) |
1679 | 0 | bOK = false; |
1680 | 0 | fp = nullptr; |
1681 | 0 | if (!bOK) |
1682 | 0 | return nullptr; |
1683 | | |
1684 | | // Create the hdr filename. |
1685 | 0 | char *const pszHdrFilename = |
1686 | 0 | CPLStrdup(CPLResetExtensionSafe(pszFilename, "hdr").c_str()); |
1687 | | |
1688 | | // Open the file. |
1689 | 0 | fp = VSIFOpenL(pszHdrFilename, "wt"); |
1690 | 0 | if (fp == nullptr) |
1691 | 0 | { |
1692 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
1693 | 0 | "Attempt to create file `%s' failed.", pszHdrFilename); |
1694 | 0 | CPLFree(pszHdrFilename); |
1695 | 0 | return nullptr; |
1696 | 0 | } |
1697 | | |
1698 | | // Decide how many bits the file should have. |
1699 | 0 | int nBits = GDALGetDataTypeSize(eType); |
1700 | |
|
1701 | 0 | if (CSLFetchNameValue(papszParamList, "NBITS") != nullptr) |
1702 | 0 | nBits = atoi(CSLFetchNameValue(papszParamList, "NBITS")); |
1703 | |
|
1704 | 0 | const int nRowBytes = (nBits * nXSize + 7) / 8; |
1705 | | |
1706 | | // Check for signed byte. |
1707 | 0 | const char *pszPixelType = CSLFetchNameValue(papszParamList, "PIXELTYPE"); |
1708 | 0 | if (pszPixelType == nullptr) |
1709 | 0 | pszPixelType = ""; |
1710 | | |
1711 | | // Write out the raw definition for the dataset as a whole. |
1712 | 0 | bOK &= VSIFPrintfL(fp, "BYTEORDER I\n") >= 0; |
1713 | 0 | bOK &= VSIFPrintfL(fp, "LAYOUT BIL\n") >= 0; |
1714 | 0 | bOK &= VSIFPrintfL(fp, "NROWS %d\n", nYSize) >= 0; |
1715 | 0 | bOK &= VSIFPrintfL(fp, "NCOLS %d\n", nXSize) >= 0; |
1716 | 0 | bOK &= VSIFPrintfL(fp, "NBANDS %d\n", nBandsIn) >= 0; |
1717 | 0 | bOK &= VSIFPrintfL(fp, "NBITS %d\n", nBits) >= 0; |
1718 | 0 | bOK &= VSIFPrintfL(fp, "BANDROWBYTES %d\n", nRowBytes) >= 0; |
1719 | 0 | bOK &= VSIFPrintfL(fp, "TOTALROWBYTES %d\n", nRowBytes * nBandsIn) >= 0; |
1720 | |
|
1721 | 0 | if (eType == GDT_Float32) |
1722 | 0 | bOK &= VSIFPrintfL(fp, "PIXELTYPE FLOAT\n") >= 0; |
1723 | 0 | else if (eType == GDT_Int8 || eType == GDT_Int16 || eType == GDT_Int32) |
1724 | 0 | bOK &= VSIFPrintfL(fp, "PIXELTYPE SIGNEDINT\n") >= 0; |
1725 | 0 | else if (eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE")) |
1726 | 0 | bOK &= VSIFPrintfL(fp, "PIXELTYPE SIGNEDINT\n") >= 0; |
1727 | 0 | else |
1728 | 0 | bOK &= VSIFPrintfL(fp, "PIXELTYPE UNSIGNEDINT\n") >= 0; |
1729 | |
|
1730 | 0 | if (VSIFCloseL(fp) != 0) |
1731 | 0 | bOK = false; |
1732 | |
|
1733 | 0 | CPLFree(pszHdrFilename); |
1734 | |
|
1735 | 0 | if (!bOK) |
1736 | 0 | return nullptr; |
1737 | | |
1738 | 0 | GDALOpenInfo oOpenInfo(pszFilename, GA_Update); |
1739 | 0 | return Open(&oOpenInfo, false); |
1740 | 0 | } |
1741 | | |
1742 | | /************************************************************************/ |
1743 | | /* CreateCopy() */ |
1744 | | /************************************************************************/ |
1745 | | |
1746 | | GDALDataset *EHdrDataset::CreateCopy(const char *pszFilename, |
1747 | | GDALDataset *poSrcDS, int bStrict, |
1748 | | char **papszOptions, |
1749 | | GDALProgressFunc pfnProgress, |
1750 | | void *pProgressData) |
1751 | | |
1752 | 0 | { |
1753 | 0 | const int nBands = poSrcDS->GetRasterCount(); |
1754 | 0 | if (nBands == 0) |
1755 | 0 | { |
1756 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
1757 | 0 | "EHdr driver does not support source dataset without any " |
1758 | 0 | "bands."); |
1759 | 0 | return nullptr; |
1760 | 0 | } |
1761 | | |
1762 | 0 | char **papszAdjustedOptions = CSLDuplicate(papszOptions); |
1763 | | |
1764 | | // Ensure we pass on NBITS and PIXELTYPE structure information. |
1765 | 0 | auto poSrcBand = poSrcDS->GetRasterBand(1); |
1766 | 0 | if (poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != nullptr && |
1767 | 0 | CSLFetchNameValue(papszOptions, "NBITS") == nullptr) |
1768 | 0 | { |
1769 | 0 | papszAdjustedOptions = CSLSetNameValue( |
1770 | 0 | papszAdjustedOptions, "NBITS", |
1771 | 0 | poSrcBand->GetMetadataItem("NBITS", "IMAGE_STRUCTURE")); |
1772 | 0 | } |
1773 | |
|
1774 | 0 | if (poSrcBand->GetRasterDataType() == GDT_Byte && |
1775 | 0 | CSLFetchNameValue(papszOptions, "PIXELTYPE") == nullptr) |
1776 | 0 | { |
1777 | 0 | poSrcBand->EnablePixelTypeSignedByteWarning(false); |
1778 | 0 | const char *pszPixelType = |
1779 | 0 | poSrcBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE"); |
1780 | 0 | poSrcBand->EnablePixelTypeSignedByteWarning(true); |
1781 | 0 | if (pszPixelType != nullptr) |
1782 | 0 | { |
1783 | 0 | papszAdjustedOptions = CSLSetNameValue(papszAdjustedOptions, |
1784 | 0 | "PIXELTYPE", pszPixelType); |
1785 | 0 | } |
1786 | 0 | } |
1787 | | |
1788 | | // Proceed with normal copying using the default createcopy operators. |
1789 | 0 | GDALDriver *poDriver = |
1790 | 0 | reinterpret_cast<GDALDriver *>(GDALGetDriverByName("EHdr")); |
1791 | |
|
1792 | 0 | GDALDataset *poOutDS = poDriver->DefaultCreateCopy( |
1793 | 0 | pszFilename, poSrcDS, bStrict, papszAdjustedOptions, pfnProgress, |
1794 | 0 | pProgressData); |
1795 | 0 | CSLDestroy(papszAdjustedOptions); |
1796 | |
|
1797 | 0 | if (poOutDS != nullptr) |
1798 | 0 | poOutDS->FlushCache(false); |
1799 | |
|
1800 | 0 | return poOutDS; |
1801 | 0 | } |
1802 | | |
1803 | | /************************************************************************/ |
1804 | | /* GetNoDataValue() */ |
1805 | | /************************************************************************/ |
1806 | | |
1807 | | double EHdrRasterBand::GetNoDataValue(int *pbSuccess) |
1808 | 0 | { |
1809 | 0 | if (pbSuccess) |
1810 | 0 | *pbSuccess = bNoDataSet; |
1811 | |
|
1812 | 0 | if (bNoDataSet) |
1813 | 0 | return dfNoData; |
1814 | | |
1815 | 0 | return RawRasterBand::GetNoDataValue(pbSuccess); |
1816 | 0 | } |
1817 | | |
1818 | | /************************************************************************/ |
1819 | | /* GetMinimum() */ |
1820 | | /************************************************************************/ |
1821 | | |
1822 | | double EHdrRasterBand::GetMinimum(int *pbSuccess) |
1823 | 0 | { |
1824 | 0 | if (pbSuccess != nullptr) |
1825 | 0 | *pbSuccess = (minmaxmeanstddev & HAS_MIN_FLAG) != 0; |
1826 | |
|
1827 | 0 | if (minmaxmeanstddev & HAS_MIN_FLAG) |
1828 | 0 | return dfMin; |
1829 | | |
1830 | 0 | return RawRasterBand::GetMinimum(pbSuccess); |
1831 | 0 | } |
1832 | | |
1833 | | /************************************************************************/ |
1834 | | /* GetMaximum() */ |
1835 | | /************************************************************************/ |
1836 | | |
1837 | | double EHdrRasterBand::GetMaximum(int *pbSuccess) |
1838 | 0 | { |
1839 | 0 | if (pbSuccess != nullptr) |
1840 | 0 | *pbSuccess = (minmaxmeanstddev & HAS_MAX_FLAG) != 0; |
1841 | |
|
1842 | 0 | if (minmaxmeanstddev & HAS_MAX_FLAG) |
1843 | 0 | return dfMax; |
1844 | | |
1845 | 0 | return RawRasterBand::GetMaximum(pbSuccess); |
1846 | 0 | } |
1847 | | |
1848 | | /************************************************************************/ |
1849 | | /* GetStatistics() */ |
1850 | | /************************************************************************/ |
1851 | | |
1852 | | CPLErr EHdrRasterBand::GetStatistics(int bApproxOK, int bForce, double *pdfMin, |
1853 | | double *pdfMax, double *pdfMean, |
1854 | | double *pdfStdDev) |
1855 | 0 | { |
1856 | 0 | if (!(GetMetadataItem("STATISTICS_APPROXIMATE") && !bApproxOK)) |
1857 | 0 | { |
1858 | 0 | if ((minmaxmeanstddev & HAS_ALL_FLAGS) == HAS_ALL_FLAGS) |
1859 | 0 | { |
1860 | 0 | if (pdfMin) |
1861 | 0 | *pdfMin = dfMin; |
1862 | 0 | if (pdfMax) |
1863 | 0 | *pdfMax = dfMax; |
1864 | 0 | if (pdfMean) |
1865 | 0 | *pdfMean = dfMean; |
1866 | 0 | if (pdfStdDev) |
1867 | 0 | *pdfStdDev = dfStdDev; |
1868 | 0 | return CE_None; |
1869 | 0 | } |
1870 | 0 | } |
1871 | | |
1872 | 0 | const CPLErr eErr = RawRasterBand::GetStatistics( |
1873 | 0 | bApproxOK, bForce, &dfMin, &dfMax, &dfMean, &dfStdDev); |
1874 | 0 | if (eErr != CE_None) |
1875 | 0 | return eErr; |
1876 | | |
1877 | 0 | EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS); |
1878 | |
|
1879 | 0 | minmaxmeanstddev = HAS_ALL_FLAGS; |
1880 | |
|
1881 | 0 | if (!bApproxOK && poEDS->RewriteSTX() != CE_None) |
1882 | 0 | RawRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); |
1883 | |
|
1884 | 0 | if (pdfMin) |
1885 | 0 | *pdfMin = dfMin; |
1886 | 0 | if (pdfMax) |
1887 | 0 | *pdfMax = dfMax; |
1888 | 0 | if (pdfMean) |
1889 | 0 | *pdfMean = dfMean; |
1890 | 0 | if (pdfStdDev) |
1891 | 0 | *pdfStdDev = dfStdDev; |
1892 | |
|
1893 | 0 | return CE_None; |
1894 | 0 | } |
1895 | | |
1896 | | /************************************************************************/ |
1897 | | /* SetStatistics() */ |
1898 | | /************************************************************************/ |
1899 | | |
1900 | | CPLErr EHdrRasterBand::SetStatistics(double dfMinIn, double dfMaxIn, |
1901 | | double dfMeanIn, double dfStdDevIn) |
1902 | 0 | { |
1903 | | // Avoid churn if nothing is changing. |
1904 | 0 | if (dfMin == dfMinIn && dfMax == dfMaxIn && dfMean == dfMeanIn && |
1905 | 0 | dfStdDev == dfStdDevIn) |
1906 | 0 | return CE_None; |
1907 | | |
1908 | 0 | dfMin = dfMinIn; |
1909 | 0 | dfMax = dfMaxIn; |
1910 | 0 | dfMean = dfMeanIn; |
1911 | 0 | dfStdDev = dfStdDevIn; |
1912 | | |
1913 | | // marks stats valid |
1914 | 0 | minmaxmeanstddev = HAS_ALL_FLAGS; |
1915 | |
|
1916 | 0 | EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS); |
1917 | |
|
1918 | 0 | if (GetMetadataItem("STATISTICS_APPROXIMATE") == nullptr) |
1919 | 0 | { |
1920 | 0 | if (GetMetadataItem("STATISTICS_MINIMUM")) |
1921 | 0 | { |
1922 | 0 | SetMetadataItem("STATISTICS_MINIMUM", nullptr); |
1923 | 0 | SetMetadataItem("STATISTICS_MAXIMUM", nullptr); |
1924 | 0 | SetMetadataItem("STATISTICS_MEAN", nullptr); |
1925 | 0 | SetMetadataItem("STATISTICS_STDDEV", nullptr); |
1926 | 0 | } |
1927 | 0 | return poEDS->RewriteSTX(); |
1928 | 0 | } |
1929 | | |
1930 | 0 | return RawRasterBand::SetStatistics(dfMinIn, dfMaxIn, dfMeanIn, dfStdDevIn); |
1931 | 0 | } |
1932 | | |
1933 | | /************************************************************************/ |
1934 | | /* GetColorTable() */ |
1935 | | /************************************************************************/ |
1936 | | |
1937 | | GDALColorTable *EHdrRasterBand::GetColorTable() |
1938 | 0 | { |
1939 | 0 | return m_poColorTable.get(); |
1940 | 0 | } |
1941 | | |
1942 | | /************************************************************************/ |
1943 | | /* SetColorTable() */ |
1944 | | /************************************************************************/ |
1945 | | |
1946 | | CPLErr EHdrRasterBand::SetColorTable(GDALColorTable *poNewCT) |
1947 | 0 | { |
1948 | 0 | if (poNewCT == nullptr) |
1949 | 0 | m_poColorTable.reset(); |
1950 | 0 | else |
1951 | 0 | m_poColorTable.reset(poNewCT->Clone()); |
1952 | |
|
1953 | 0 | reinterpret_cast<EHdrDataset *>(poDS)->bCLRDirty = true; |
1954 | |
|
1955 | 0 | return CE_None; |
1956 | 0 | } |
1957 | | |
1958 | | /************************************************************************/ |
1959 | | /* GetDefaultRAT() */ |
1960 | | /************************************************************************/ |
1961 | | |
1962 | | GDALRasterAttributeTable *EHdrRasterBand::GetDefaultRAT() |
1963 | 0 | { |
1964 | 0 | return m_poRAT.get(); |
1965 | 0 | } |
1966 | | |
1967 | | /************************************************************************/ |
1968 | | /* SetDefaultRAT() */ |
1969 | | /************************************************************************/ |
1970 | | |
1971 | | CPLErr EHdrRasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT) |
1972 | 0 | { |
1973 | 0 | if (poRAT) |
1974 | 0 | { |
1975 | 0 | if (!(poRAT->GetColumnCount() == 4 && |
1976 | 0 | poRAT->GetTypeOfCol(0) == GFT_Integer && |
1977 | 0 | poRAT->GetTypeOfCol(1) == GFT_Integer && |
1978 | 0 | poRAT->GetTypeOfCol(2) == GFT_Integer && |
1979 | 0 | poRAT->GetTypeOfCol(3) == GFT_Integer && |
1980 | 0 | poRAT->GetUsageOfCol(0) == GFU_Generic && |
1981 | 0 | poRAT->GetUsageOfCol(1) == GFU_Red && |
1982 | 0 | poRAT->GetUsageOfCol(2) == GFU_Green && |
1983 | 0 | poRAT->GetUsageOfCol(3) == GFU_Blue)) |
1984 | 0 | { |
1985 | 0 | CPLError(CE_Warning, CPLE_NotSupported, |
1986 | 0 | "Unsupported type of RAT: " |
1987 | 0 | "only value,R,G,B ones are supported"); |
1988 | 0 | return CE_Failure; |
1989 | 0 | } |
1990 | 0 | } |
1991 | | |
1992 | 0 | if (poRAT == nullptr) |
1993 | 0 | m_poRAT.reset(); |
1994 | 0 | else |
1995 | 0 | m_poRAT.reset(poRAT->Clone()); |
1996 | |
|
1997 | 0 | reinterpret_cast<EHdrDataset *>(poDS)->bCLRDirty = true; |
1998 | |
|
1999 | 0 | return CE_None; |
2000 | 0 | } |
2001 | | |
2002 | | /************************************************************************/ |
2003 | | /* GDALRegister_EHdr() */ |
2004 | | /************************************************************************/ |
2005 | | |
2006 | | void GDALRegister_EHdr() |
2007 | | |
2008 | 2 | { |
2009 | 2 | if (GDALGetDriverByName("EHdr") != nullptr) |
2010 | 0 | return; |
2011 | | |
2012 | 2 | GDALDriver *poDriver = new GDALDriver(); |
2013 | | |
2014 | 2 | poDriver->SetDescription("EHdr"); |
2015 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
2016 | 2 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ESRI .hdr Labelled"); |
2017 | 2 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/ehdr.html"); |
2018 | 2 | poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "bil"); |
2019 | 2 | poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, |
2020 | 2 | "Byte Int8 Int16 UInt16 Int32 UInt32 Float32"); |
2021 | | |
2022 | 2 | poDriver->SetMetadataItem( |
2023 | 2 | GDAL_DMD_CREATIONOPTIONLIST, |
2024 | 2 | "<CreationOptionList>" |
2025 | 2 | " <Option name='NBITS' type='int' description='Special pixel bits " |
2026 | 2 | "(1-7)'/>" |
2027 | 2 | " <Option name='PIXELTYPE' type='string' description='By setting " |
2028 | 2 | "this to SIGNEDBYTE, a new Byte file can be forced to be written as " |
2029 | 2 | "signed byte'/>" |
2030 | 2 | "</CreationOptionList>"); |
2031 | | |
2032 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES"); |
2033 | 2 | poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, "GeoTransform SRS NoData " |
2034 | 2 | "RasterValues"); |
2035 | | |
2036 | 2 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
2037 | 2 | poDriver->pfnOpen = EHdrDataset::Open; |
2038 | 2 | poDriver->pfnCreate = EHdrDataset::Create; |
2039 | 2 | poDriver->pfnCreateCopy = EHdrDataset::CreateCopy; |
2040 | | |
2041 | 2 | GetGDALDriverManager()->RegisterDriver(poDriver); |
2042 | 2 | } |