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