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