/src/gdal/frmts/raw/genbindataset.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: GDAL |
4 | | * Purpose: Generic Binary format driver (.hdr but not ESRI .hdr!) |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com> |
9 | | * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_string.h" |
15 | | #include "gdal_frmts.h" |
16 | | #include "gdal_priv.h" |
17 | | #include "ogr_spatialref.h" |
18 | | #include "rawdataset.h" |
19 | | |
20 | | #include <algorithm> |
21 | | #include <cstdlib> |
22 | | |
23 | | #include "usgs_esri_zones.h" |
24 | | |
25 | | /************************************************************************/ |
26 | | /* ==================================================================== */ |
27 | | /* GenBinDataset */ |
28 | | /* ==================================================================== */ |
29 | | /************************************************************************/ |
30 | | |
31 | | class GenBinDataset final : public RawDataset |
32 | | { |
33 | | friend class GenBinBitRasterBand; |
34 | | |
35 | | VSILFILE *fpImage; // image data file. |
36 | | |
37 | | bool bGotTransform{}; |
38 | | GDALGeoTransform m_gt{}; |
39 | | OGRSpatialReference m_oSRS{}; |
40 | | |
41 | | char **papszHDR; |
42 | | |
43 | | void ParseCoordinateSystem(char **); |
44 | | |
45 | | CPL_DISALLOW_COPY_ASSIGN(GenBinDataset) |
46 | | |
47 | | CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override; |
48 | | |
49 | | public: |
50 | | GenBinDataset(); |
51 | | ~GenBinDataset() override; |
52 | | |
53 | | CPLErr GetGeoTransform(GDALGeoTransform >) const override; |
54 | | |
55 | | const OGRSpatialReference *GetSpatialRef() const override |
56 | 10.2k | { |
57 | 10.2k | return m_oSRS.IsEmpty() ? RawDataset::GetSpatialRef() : &m_oSRS; |
58 | 10.2k | } |
59 | | |
60 | | char **GetFileList() override; |
61 | | |
62 | | static GDALDataset *Open(GDALOpenInfo *); |
63 | | }; |
64 | | |
65 | | /************************************************************************/ |
66 | | /* ==================================================================== */ |
67 | | /* GenBinBitRasterBand */ |
68 | | /* ==================================================================== */ |
69 | | /************************************************************************/ |
70 | | |
71 | | class GenBinBitRasterBand final : public GDALPamRasterBand |
72 | | { |
73 | | int nBits; |
74 | | |
75 | | CPL_DISALLOW_COPY_ASSIGN(GenBinBitRasterBand) |
76 | | |
77 | | public: |
78 | | GenBinBitRasterBand(GenBinDataset *poDS, int nBits); |
79 | | |
80 | | ~GenBinBitRasterBand() override |
81 | 0 | { |
82 | 0 | } |
83 | | |
84 | | CPLErr IReadBlock(int, int, void *) override; |
85 | | }; |
86 | | |
87 | | /************************************************************************/ |
88 | | /* GenBinBitRasterBand() */ |
89 | | /************************************************************************/ |
90 | | |
91 | | GenBinBitRasterBand::GenBinBitRasterBand(GenBinDataset *poDSIn, int nBitsIn) |
92 | 2.09k | : nBits(nBitsIn) |
93 | 2.09k | { |
94 | 2.09k | SetMetadataItem(GDALMD_NBITS, CPLString().Printf("%d", nBitsIn), |
95 | 2.09k | GDAL_MDD_IMAGE_STRUCTURE); |
96 | | |
97 | 2.09k | poDS = poDSIn; |
98 | 2.09k | nBand = 1; |
99 | | |
100 | 2.09k | eDataType = GDT_UInt8; |
101 | | |
102 | 2.09k | nBlockXSize = poDSIn->nRasterXSize; |
103 | 2.09k | nBlockYSize = 1; |
104 | 2.09k | } |
105 | | |
106 | | /************************************************************************/ |
107 | | /* IReadBlock() */ |
108 | | /************************************************************************/ |
109 | | |
110 | | CPLErr GenBinBitRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff, |
111 | | void *pImage) |
112 | | |
113 | 41.9k | { |
114 | 41.9k | GenBinDataset *poGDS = cpl::down_cast<GenBinDataset *>(poDS); |
115 | | |
116 | | /* -------------------------------------------------------------------- */ |
117 | | /* Establish desired position. */ |
118 | | /* -------------------------------------------------------------------- */ |
119 | 41.9k | const vsi_l_offset nLineStart = |
120 | 41.9k | (static_cast<vsi_l_offset>(nBlockXSize) * nBlockYOff * nBits) / 8; |
121 | 41.9k | int iBitOffset = static_cast<int>( |
122 | 41.9k | (static_cast<vsi_l_offset>(nBlockXSize) * nBlockYOff * nBits) % 8); |
123 | 41.9k | const unsigned int nLineBytes = static_cast<unsigned int>( |
124 | 41.9k | (static_cast<vsi_l_offset>(nBlockXSize) * (nBlockYOff + 1) * nBits + |
125 | 41.9k | 7) / |
126 | 41.9k | 8 - |
127 | 41.9k | nLineStart); |
128 | | |
129 | | /* -------------------------------------------------------------------- */ |
130 | | /* Read data into buffer. */ |
131 | | /* -------------------------------------------------------------------- */ |
132 | 41.9k | GByte *pabyBuffer = static_cast<GByte *>(CPLCalloc(nLineBytes, 1)); |
133 | | |
134 | 41.9k | if (VSIFSeekL(poGDS->fpImage, nLineStart, SEEK_SET) != 0 || |
135 | 41.9k | VSIFReadL(pabyBuffer, 1, nLineBytes, poGDS->fpImage) != nLineBytes) |
136 | 138 | { |
137 | 138 | CPLError(CE_Failure, CPLE_FileIO, |
138 | 138 | "Failed to read %u bytes at offset %lu.\n%s", nLineBytes, |
139 | 138 | static_cast<unsigned long>(nLineStart), VSIStrerror(errno)); |
140 | 138 | CPLFree(pabyBuffer); |
141 | 138 | return CE_Failure; |
142 | 138 | } |
143 | | |
144 | | /* -------------------------------------------------------------------- */ |
145 | | /* Copy data, promoting to 8bit. */ |
146 | | /* -------------------------------------------------------------------- */ |
147 | 41.7k | GByte *pafImage = reinterpret_cast<GByte *>(pImage); |
148 | 41.7k | if (nBits == 1) |
149 | 21.8k | { |
150 | 1.22M | for (int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits) |
151 | 1.20M | { |
152 | 1.20M | if (pabyBuffer[iBitOffset >> 3] & (0x80 >> (iBitOffset & 7))) |
153 | 577k | pafImage[iX] = 1; |
154 | 628k | else |
155 | 628k | pafImage[iX] = 0; |
156 | 1.20M | } |
157 | 21.8k | } |
158 | 19.9k | else if (nBits == 2) |
159 | 2.92k | { |
160 | 267k | for (int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits) |
161 | 264k | { |
162 | 264k | pafImage[iX] = |
163 | 264k | (pabyBuffer[iBitOffset >> 3]) >> (6 - (iBitOffset & 0x7)) & 0x3; |
164 | 264k | } |
165 | 2.92k | } |
166 | 17.0k | else if (nBits == 4) |
167 | 17.0k | { |
168 | 299k | for (int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits) |
169 | 282k | { |
170 | 282k | if (iBitOffset == 0) |
171 | 10.1k | pafImage[iX] = (pabyBuffer[iBitOffset >> 3]) >> 4; |
172 | 271k | else |
173 | 271k | pafImage[iX] = (pabyBuffer[iBitOffset >> 3]) & 0xf; |
174 | 282k | } |
175 | 17.0k | } |
176 | 0 | else |
177 | 0 | { |
178 | 0 | CPLAssert(false); |
179 | 0 | } |
180 | | |
181 | 41.7k | CPLFree(pabyBuffer); |
182 | | |
183 | 41.7k | return CE_None; |
184 | 41.9k | } |
185 | | |
186 | | /************************************************************************/ |
187 | | /* ==================================================================== */ |
188 | | /* GenBinDataset */ |
189 | | /* ==================================================================== */ |
190 | | /************************************************************************/ |
191 | | |
192 | | /************************************************************************/ |
193 | | /* GenBinDataset() */ |
194 | | /************************************************************************/ |
195 | | |
196 | | GenBinDataset::GenBinDataset() |
197 | 53.3k | : fpImage(nullptr), bGotTransform(false), papszHDR(nullptr) |
198 | 53.3k | { |
199 | 53.3k | m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
200 | 53.3k | } |
201 | | |
202 | | /************************************************************************/ |
203 | | /* ~GenBinDataset() */ |
204 | | /************************************************************************/ |
205 | | |
206 | | GenBinDataset::~GenBinDataset() |
207 | | |
208 | 53.3k | { |
209 | 53.3k | GenBinDataset::Close(); |
210 | 53.3k | } |
211 | | |
212 | | /************************************************************************/ |
213 | | /* Close() */ |
214 | | /************************************************************************/ |
215 | | |
216 | | CPLErr GenBinDataset::Close(GDALProgressFunc, void *) |
217 | 105k | { |
218 | 105k | CPLErr eErr = CE_None; |
219 | 105k | if (nOpenFlags != OPEN_FLAGS_CLOSED) |
220 | 53.3k | { |
221 | 53.3k | if (GenBinDataset::FlushCache(true) != CE_None) |
222 | 0 | eErr = CE_Failure; |
223 | | |
224 | 53.3k | if (fpImage) |
225 | 52.8k | { |
226 | 52.8k | if (VSIFCloseL(fpImage) != 0) |
227 | 0 | { |
228 | 0 | CPLError(CE_Failure, CPLE_FileIO, "I/O error"); |
229 | 0 | eErr = CE_Failure; |
230 | 0 | } |
231 | 52.8k | } |
232 | | |
233 | 53.3k | CSLDestroy(papszHDR); |
234 | | |
235 | 53.3k | if (GDALPamDataset::Close() != CE_None) |
236 | 0 | eErr = CE_Failure; |
237 | 53.3k | } |
238 | 105k | return eErr; |
239 | 105k | } |
240 | | |
241 | | /************************************************************************/ |
242 | | /* GetGeoTransform() */ |
243 | | /************************************************************************/ |
244 | | |
245 | | CPLErr GenBinDataset::GetGeoTransform(GDALGeoTransform >) const |
246 | | |
247 | 10.2k | { |
248 | 10.2k | if (bGotTransform) |
249 | 207 | { |
250 | 207 | gt = m_gt; |
251 | 207 | return CE_None; |
252 | 207 | } |
253 | | |
254 | 10.0k | return GDALPamDataset::GetGeoTransform(gt); |
255 | 10.2k | } |
256 | | |
257 | | /************************************************************************/ |
258 | | /* GetFileList() */ |
259 | | /************************************************************************/ |
260 | | |
261 | | char **GenBinDataset::GetFileList() |
262 | | |
263 | 45.1k | { |
264 | 45.1k | const CPLString osPath = CPLGetPathSafe(GetDescription()); |
265 | 45.1k | const CPLString osName = CPLGetBasenameSafe(GetDescription()); |
266 | | |
267 | | // Main data file, etc. |
268 | 45.1k | char **papszFileList = GDALPamDataset::GetFileList(); |
269 | | |
270 | | // Header file. |
271 | 45.1k | const CPLString osFilename = CPLFormCIFilenameSafe(osPath, osName, "hdr"); |
272 | 45.1k | papszFileList = CSLAddString(papszFileList, osFilename); |
273 | | |
274 | 45.1k | return papszFileList; |
275 | 45.1k | } |
276 | | |
277 | | /************************************************************************/ |
278 | | /* ParseCoordinateSystem() */ |
279 | | /************************************************************************/ |
280 | | |
281 | | void GenBinDataset::ParseCoordinateSystem(char **papszHdr) |
282 | | |
283 | 52.5k | { |
284 | 52.5k | const char *pszProjName = CSLFetchNameValue(papszHdr, "PROJECTION_NAME"); |
285 | 52.5k | if (pszProjName == nullptr) |
286 | 29.7k | return; |
287 | | |
288 | | /* -------------------------------------------------------------------- */ |
289 | | /* Translate zone and parameters into numeric form. */ |
290 | | /* -------------------------------------------------------------------- */ |
291 | 22.8k | int nZone = 0; |
292 | 22.8k | if (const char *pszProjectionZone = |
293 | 22.8k | CSLFetchNameValue(papszHdr, "PROJECTION_ZONE")) |
294 | 9.99k | nZone = atoi(pszProjectionZone); |
295 | | |
296 | | #if 0 |
297 | | // TODO(schwehr): Why was this being done but not used? |
298 | | double adfProjParams[15] = { 0.0 }; |
299 | | if( CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) ) |
300 | | { |
301 | | char **papszTokens = CSLTokenizeString( |
302 | | CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) ); |
303 | | |
304 | | for( int i = 0; i < 15 && papszTokens[i] != NULL; i++ ) |
305 | | adfProjParams[i] = CPLAtofM( papszTokens[i] ); |
306 | | |
307 | | CSLDestroy( papszTokens ); |
308 | | } |
309 | | #endif |
310 | | |
311 | | /* -------------------------------------------------------------------- */ |
312 | | /* Handle projections. */ |
313 | | /* -------------------------------------------------------------------- */ |
314 | 22.8k | const char *pszDatumName = CSLFetchNameValue(papszHdr, "DATUM_NAME"); |
315 | | |
316 | 22.8k | if (EQUAL(pszProjName, "UTM") && nZone != 0 && nZone > INT_MIN) |
317 | 2.07k | { |
318 | | // Just getting that the negative zone for southern hemisphere is used. |
319 | 2.07k | m_oSRS.SetUTM(std::abs(nZone), nZone > 0); |
320 | 2.07k | } |
321 | | |
322 | 20.8k | else if (EQUAL(pszProjName, "State Plane") && nZone != 0 && nZone > INT_MIN) |
323 | 3.96k | { |
324 | 3.96k | const int nPairs = sizeof(anUsgsEsriZones) / (2 * sizeof(int)); |
325 | | |
326 | 537k | for (int i = 0; i < nPairs; i++) |
327 | 533k | { |
328 | 533k | if (anUsgsEsriZones[i * 2 + 1] == nZone) |
329 | 397 | { |
330 | 397 | nZone = anUsgsEsriZones[i * 2]; |
331 | 397 | break; |
332 | 397 | } |
333 | 533k | } |
334 | | |
335 | 3.96k | const char *pszUnits = CSLFetchNameValueDef(papszHdr, "MAP_UNITS", ""); |
336 | 3.96k | double dfUnits = 0.0; |
337 | 3.96k | if (EQUAL(pszUnits, "feet")) |
338 | 449 | dfUnits = CPLAtofM(SRS_UL_US_FOOT_CONV); |
339 | 3.51k | else if (STARTS_WITH_CI(pszUnits, "MET")) |
340 | 118 | dfUnits = 1.0; |
341 | 3.40k | else |
342 | 3.40k | pszUnits = nullptr; |
343 | | |
344 | 3.96k | m_oSRS.SetStatePlane(std::abs(nZone), |
345 | 3.96k | pszDatumName == nullptr || |
346 | 2.22k | !EQUAL(pszDatumName, "NAD27"), |
347 | 3.96k | pszUnits, dfUnits); |
348 | 3.96k | } |
349 | | |
350 | | /* -------------------------------------------------------------------- */ |
351 | | /* Setup the geographic coordinate system. */ |
352 | | /* -------------------------------------------------------------------- */ |
353 | 22.8k | if (m_oSRS.GetAttrNode("GEOGCS") == nullptr) |
354 | 21.3k | { |
355 | 21.3k | const char *pszSpheroidName = |
356 | 21.3k | CSLFetchNameValue(papszHdr, "SPHEROID_NAME"); |
357 | 21.3k | const char *pszSemiMajor = |
358 | 21.3k | CSLFetchNameValue(papszHdr, "SEMI_MAJOR_AXIS"); |
359 | 21.3k | const char *pszSemiMinor = |
360 | 21.3k | CSLFetchNameValue(papszHdr, "SEMI_MINOR_AXIS"); |
361 | 21.3k | if (pszDatumName != nullptr && |
362 | 8.38k | m_oSRS.SetWellKnownGeogCS(pszDatumName) == OGRERR_NONE) |
363 | 1.58k | { |
364 | | // good |
365 | 1.58k | } |
366 | 19.7k | else if (pszSpheroidName && pszSemiMajor && pszSemiMinor) |
367 | 12.0k | { |
368 | 12.0k | const double dfSemiMajor = CPLAtofM(pszSemiMajor); |
369 | 12.0k | const double dfSemiMinor = CPLAtofM(pszSemiMinor); |
370 | | |
371 | 12.0k | m_oSRS.SetGeogCS(pszSpheroidName, pszSpheroidName, pszSpheroidName, |
372 | 12.0k | dfSemiMajor, |
373 | 12.0k | (dfSemiMajor == 0.0 || dfSemiMajor == dfSemiMinor) |
374 | 12.0k | ? 0.0 |
375 | 12.0k | : 1.0 / (1.0 - dfSemiMinor / dfSemiMajor)); |
376 | 12.0k | } |
377 | 7.70k | else // fallback default. |
378 | 7.70k | m_oSRS.SetWellKnownGeogCS("WGS84"); |
379 | 21.3k | } |
380 | 22.8k | } |
381 | | |
382 | | /************************************************************************/ |
383 | | /* Open() */ |
384 | | /************************************************************************/ |
385 | | |
386 | | GDALDataset *GenBinDataset::Open(GDALOpenInfo *poOpenInfo) |
387 | | |
388 | 547k | { |
389 | | /* -------------------------------------------------------------------- */ |
390 | | /* We assume the user is pointing to the binary (i.e. .bil) file. */ |
391 | | /* -------------------------------------------------------------------- */ |
392 | 547k | if (poOpenInfo->nHeaderBytes < 2 || poOpenInfo->fpL == nullptr || |
393 | 124k | (!poOpenInfo->IsSingleAllowedDriver("GenBin") && |
394 | 124k | poOpenInfo->IsExtensionEqualToCI("zarr"))) |
395 | 423k | { |
396 | 423k | return nullptr; |
397 | 423k | } |
398 | | |
399 | | /* -------------------------------------------------------------------- */ |
400 | | /* Now we need to tear apart the filename to form a .HDR */ |
401 | | /* filename. */ |
402 | | /* -------------------------------------------------------------------- */ |
403 | 124k | const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename); |
404 | 124k | const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename); |
405 | 124k | CPLString osHDRFilename; |
406 | | |
407 | 124k | CSLConstList papszSiblingFiles = poOpenInfo->GetSiblingFiles(); |
408 | 124k | if (papszSiblingFiles) |
409 | 115k | { |
410 | 115k | const int iFile = |
411 | 115k | CSLFindString(papszSiblingFiles, |
412 | 115k | CPLFormFilenameSafe(nullptr, osName, "hdr").c_str()); |
413 | 115k | if (iFile < 0) // return if there is no corresponding .hdr file |
414 | 32.6k | return nullptr; |
415 | | |
416 | 82.7k | osHDRFilename = |
417 | 82.7k | CPLFormFilenameSafe(osPath, papszSiblingFiles[iFile], nullptr); |
418 | 82.7k | } |
419 | 8.70k | else |
420 | 8.70k | { |
421 | 8.70k | osHDRFilename = CPLFormCIFilenameSafe(osPath, osName, "hdr"); |
422 | 8.70k | } |
423 | | |
424 | 91.4k | const bool bSelectedHDR = EQUAL(osHDRFilename, poOpenInfo->pszFilename); |
425 | | |
426 | | /* -------------------------------------------------------------------- */ |
427 | | /* Do we have a .hdr file? */ |
428 | | /* -------------------------------------------------------------------- */ |
429 | 91.4k | VSILFILE *fp = VSIFOpenL(osHDRFilename, "r"); |
430 | 91.4k | if (fp == nullptr) |
431 | 8.71k | { |
432 | 8.71k | return nullptr; |
433 | 8.71k | } |
434 | | |
435 | | /* -------------------------------------------------------------------- */ |
436 | | /* Read a chunk to skim for expected keywords. */ |
437 | | /* -------------------------------------------------------------------- */ |
438 | 82.7k | char achHeader[1000] = {'\0'}; |
439 | | |
440 | 82.7k | const int nRead = |
441 | 82.7k | static_cast<int>(VSIFReadL(achHeader, 1, sizeof(achHeader) - 1, fp)); |
442 | 82.7k | achHeader[nRead] = '\0'; |
443 | 82.7k | CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_SET)); |
444 | | |
445 | 82.7k | if (strstr(achHeader, "BANDS:") == nullptr || |
446 | 53.8k | strstr(achHeader, "ROWS:") == nullptr || |
447 | 53.5k | strstr(achHeader, "COLS:") == nullptr) |
448 | 29.2k | { |
449 | 29.2k | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
450 | 29.2k | return nullptr; |
451 | 29.2k | } |
452 | | |
453 | | /* -------------------------------------------------------------------- */ |
454 | | /* Has the user selected the .hdr file to open? */ |
455 | | /* -------------------------------------------------------------------- */ |
456 | 53.5k | if (bSelectedHDR) |
457 | 0 | { |
458 | 0 | CPLError( |
459 | 0 | CE_Failure, CPLE_AppDefined, |
460 | 0 | "The selected file is an Generic Binary header file, but to " |
461 | 0 | "open Generic Binary datasets, the data file should be selected " |
462 | 0 | "instead of the .hdr file. Please try again selecting" |
463 | 0 | "the raw data file corresponding to the header file: %s", |
464 | 0 | poOpenInfo->pszFilename); |
465 | 0 | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
466 | 0 | return nullptr; |
467 | 0 | } |
468 | | |
469 | | /* -------------------------------------------------------------------- */ |
470 | | /* Read the .hdr file. */ |
471 | | /* -------------------------------------------------------------------- */ |
472 | 53.5k | char **papszHdr = nullptr; |
473 | 53.5k | const char *pszLine = CPLReadLineL(fp); |
474 | | |
475 | 12.6M | while (pszLine != nullptr) |
476 | 12.5M | { |
477 | 12.5M | if (EQUAL(pszLine, "PROJECTION_PARAMETERS:")) |
478 | 9.88k | { |
479 | 9.88k | CPLString osPP = pszLine; |
480 | | |
481 | 9.88k | pszLine = CPLReadLineL(fp); |
482 | 44.3k | while (pszLine != nullptr && (*pszLine == '\t' || *pszLine == ' ')) |
483 | 34.4k | { |
484 | 34.4k | osPP += pszLine; |
485 | 34.4k | pszLine = CPLReadLineL(fp); |
486 | 34.4k | } |
487 | 9.88k | papszHdr = CSLAddString(papszHdr, osPP); |
488 | 9.88k | } |
489 | 12.5M | else |
490 | 12.5M | { |
491 | 12.5M | char *pszName = nullptr; |
492 | 12.5M | const char *pszKey = CPLParseNameValue(pszLine, &pszName); |
493 | 12.5M | if (pszKey && pszName) |
494 | 6.32M | { |
495 | 6.32M | CPLString osValue = pszKey; |
496 | 6.32M | osValue.Trim(); |
497 | | |
498 | 6.32M | papszHdr = CSLSetNameValue(papszHdr, pszName, osValue); |
499 | 6.32M | } |
500 | 12.5M | CPLFree(pszName); |
501 | | |
502 | 12.5M | pszLine = CPLReadLineL(fp); |
503 | 12.5M | } |
504 | 12.5M | } |
505 | | |
506 | 53.5k | CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); |
507 | | |
508 | 53.5k | if (CSLFetchNameValue(papszHdr, "COLS") == nullptr || |
509 | 53.4k | CSLFetchNameValue(papszHdr, "ROWS") == nullptr || |
510 | 53.3k | CSLFetchNameValue(papszHdr, "BANDS") == nullptr) |
511 | 179 | { |
512 | 179 | CSLDestroy(papszHdr); |
513 | 179 | return nullptr; |
514 | 179 | } |
515 | | |
516 | | /* -------------------------------------------------------------------- */ |
517 | | /* Create a corresponding GDALDataset. */ |
518 | | /* -------------------------------------------------------------------- */ |
519 | 53.3k | auto poDS = std::make_unique<GenBinDataset>(); |
520 | | |
521 | | /* -------------------------------------------------------------------- */ |
522 | | /* Capture some information from the file that is of interest. */ |
523 | | /* -------------------------------------------------------------------- */ |
524 | 53.3k | const int nBands = atoi(CSLFetchNameValue(papszHdr, "BANDS")); |
525 | | |
526 | 53.3k | poDS->nRasterXSize = atoi(CSLFetchNameValue(papszHdr, "COLS")); |
527 | 53.3k | poDS->nRasterYSize = atoi(CSLFetchNameValue(papszHdr, "ROWS")); |
528 | 53.3k | poDS->papszHDR = papszHdr; |
529 | | |
530 | 53.3k | if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) || |
531 | 53.0k | !GDALCheckBandCount(nBands, FALSE)) |
532 | 477 | { |
533 | 477 | return nullptr; |
534 | 477 | } |
535 | | |
536 | 52.8k | std::swap(poDS->fpImage, poOpenInfo->fpL); |
537 | 52.8k | poDS->eAccess = poOpenInfo->eAccess; |
538 | | |
539 | | /* -------------------------------------------------------------------- */ |
540 | | /* Figure out the data type. */ |
541 | | /* -------------------------------------------------------------------- */ |
542 | 52.8k | const char *pszDataType = CSLFetchNameValue(papszHdr, "DATATYPE"); |
543 | 52.8k | GDALDataType eDataType = GDT_UInt8; |
544 | 52.8k | int nBits = -1; // Only needed for partial byte types |
545 | | |
546 | 52.8k | if (pszDataType == nullptr) |
547 | 36.4k | { |
548 | | // nothing to do |
549 | 36.4k | } |
550 | 16.3k | else if (EQUAL(pszDataType, "U16")) |
551 | 898 | eDataType = GDT_UInt16; |
552 | 15.4k | else if (EQUAL(pszDataType, "S16")) |
553 | 1.05k | eDataType = GDT_Int16; |
554 | 14.4k | else if (EQUAL(pszDataType, "F32")) |
555 | 829 | eDataType = GDT_Float32; |
556 | 13.5k | else if (EQUAL(pszDataType, "F64")) |
557 | 1.44k | eDataType = GDT_Float64; |
558 | 12.1k | else if (EQUAL(pszDataType, "U8")) |
559 | 3.67k | { |
560 | | // nothing to do |
561 | 3.67k | } |
562 | 8.45k | else if (EQUAL(pszDataType, "U1") || EQUAL(pszDataType, "U2") || |
563 | 6.89k | EQUAL(pszDataType, "U4")) |
564 | 2.11k | { |
565 | 2.11k | nBits = atoi(pszDataType + 1); |
566 | 2.11k | if (nBands != 1) |
567 | 17 | { |
568 | 17 | CPLError(CE_Failure, CPLE_OpenFailed, |
569 | 17 | "Only one band is supported for U1/U2/U4 data type"); |
570 | 17 | return nullptr; |
571 | 17 | } |
572 | 2.11k | } |
573 | 6.34k | else |
574 | 6.34k | { |
575 | 6.34k | CPLError(CE_Warning, CPLE_AppDefined, |
576 | 6.34k | "DATATYPE=%s not recognised, assuming Byte.", pszDataType); |
577 | 6.34k | } |
578 | | |
579 | | /* -------------------------------------------------------------------- */ |
580 | | /* Do we need byte swapping? */ |
581 | | /* -------------------------------------------------------------------- */ |
582 | | |
583 | 52.8k | RawRasterBand::ByteOrder eByteOrder = RawRasterBand::NATIVE_BYTE_ORDER; |
584 | | |
585 | 52.8k | const char *pszByteOrder = CSLFetchNameValue(papszHdr, "BYTE_ORDER"); |
586 | 52.8k | if (pszByteOrder) |
587 | 11.6k | { |
588 | 11.6k | eByteOrder = EQUAL(pszByteOrder, "LSB") |
589 | 11.6k | ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN |
590 | 11.6k | : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN; |
591 | 11.6k | } |
592 | | |
593 | | /* -------------------------------------------------------------------- */ |
594 | | /* Work out interleaving info. */ |
595 | | /* -------------------------------------------------------------------- */ |
596 | 52.8k | const int nItemSize = GDALGetDataTypeSizeBytes(eDataType); |
597 | 52.8k | int nPixelOffset = 0; |
598 | 52.8k | int nLineOffset = 0; |
599 | 52.8k | vsi_l_offset nBandOffset = 0; |
600 | 52.8k | bool bIntOverflow = false; |
601 | | |
602 | 52.8k | const char *pszInterleaving = CSLFetchNameValue(papszHdr, "INTERLEAVING"); |
603 | 52.8k | if (pszInterleaving == nullptr) |
604 | 42.2k | pszInterleaving = "BIL"; |
605 | | |
606 | 52.8k | if (EQUAL(pszInterleaving, "BSQ") || EQUAL(pszInterleaving, "NA")) |
607 | 2.55k | { |
608 | 2.55k | nPixelOffset = nItemSize; |
609 | 2.55k | if (nItemSize <= 0 || poDS->nRasterXSize > INT_MAX / nItemSize) |
610 | 10 | bIntOverflow = true; |
611 | 2.54k | else |
612 | 2.54k | { |
613 | 2.54k | nLineOffset = nItemSize * poDS->nRasterXSize; |
614 | 2.54k | nBandOffset = |
615 | 2.54k | nLineOffset * static_cast<vsi_l_offset>(poDS->nRasterYSize); |
616 | 2.54k | } |
617 | 2.55k | } |
618 | 50.2k | else if (EQUAL(pszInterleaving, "BIP")) |
619 | 5.88k | { |
620 | 5.88k | nPixelOffset = nItemSize * nBands; |
621 | 5.88k | if (nPixelOffset == 0 || poDS->nRasterXSize > INT_MAX / nPixelOffset) |
622 | 10 | bIntOverflow = true; |
623 | 5.87k | else |
624 | 5.87k | { |
625 | 5.87k | nLineOffset = nPixelOffset * poDS->nRasterXSize; |
626 | 5.87k | nBandOffset = nItemSize; |
627 | 5.87k | } |
628 | 5.88k | } |
629 | 44.3k | else |
630 | 44.3k | { |
631 | 44.3k | if (!EQUAL(pszInterleaving, "BIL")) |
632 | 2.12k | CPLError(CE_Warning, CPLE_AppDefined, |
633 | 2.12k | "INTERLEAVING:%s not recognised, assume BIL.", |
634 | 2.12k | pszInterleaving); |
635 | | |
636 | 44.3k | nPixelOffset = nItemSize; |
637 | 44.3k | if (nPixelOffset == 0 || nBands == 0 || |
638 | 44.3k | poDS->nRasterXSize > INT_MAX / (nPixelOffset * nBands)) |
639 | 16 | bIntOverflow = true; |
640 | 44.3k | else |
641 | 44.3k | { |
642 | 44.3k | nLineOffset = nPixelOffset * nBands * poDS->nRasterXSize; |
643 | 44.3k | nBandOffset = |
644 | 44.3k | nItemSize * static_cast<vsi_l_offset>(poDS->nRasterXSize); |
645 | 44.3k | } |
646 | 44.3k | } |
647 | | |
648 | 52.8k | if (bIntOverflow) |
649 | 36 | { |
650 | 36 | CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred."); |
651 | 36 | return nullptr; |
652 | 36 | } |
653 | | |
654 | 52.7k | if (nBits < 0 && |
655 | 50.7k | !RAWDatasetCheckMemoryUsage(poDS->nRasterXSize, poDS->nRasterYSize, |
656 | 50.7k | nBands, nItemSize, nPixelOffset, |
657 | 50.7k | nLineOffset, 0, nBandOffset, poDS->fpImage)) |
658 | 209 | { |
659 | 209 | return nullptr; |
660 | 209 | } |
661 | | |
662 | 52.5k | poDS->SetDescription(poOpenInfo->pszFilename); |
663 | 52.5k | poDS->PamInitialize(); |
664 | | |
665 | | /* -------------------------------------------------------------------- */ |
666 | | /* Create band information objects. */ |
667 | | /* -------------------------------------------------------------------- */ |
668 | 265k | for (int i = 0; i < nBands; i++) |
669 | 213k | { |
670 | 213k | if (nBits != -1) |
671 | 2.09k | { |
672 | 2.09k | poDS->SetBand(i + 1, new GenBinBitRasterBand(poDS.get(), nBits)); |
673 | 2.09k | } |
674 | 211k | else |
675 | 211k | { |
676 | 211k | auto poBand = RawRasterBand::Create( |
677 | 211k | poDS.get(), i + 1, poDS->fpImage, nBandOffset * i, nPixelOffset, |
678 | 211k | nLineOffset, eDataType, eByteOrder, RawRasterBand::OwnFP::NO); |
679 | 211k | if (!poBand) |
680 | 0 | return nullptr; |
681 | 211k | poDS->SetBand(i + 1, std::move(poBand)); |
682 | 211k | } |
683 | 213k | } |
684 | | |
685 | | /* -------------------------------------------------------------------- */ |
686 | | /* Get geotransform. */ |
687 | | /* -------------------------------------------------------------------- */ |
688 | 52.5k | if (poDS->nRasterXSize > 1 && poDS->nRasterYSize > 1 && |
689 | 29.7k | CSLFetchNameValue(papszHdr, "UL_X_COORDINATE") != nullptr && |
690 | 7.22k | CSLFetchNameValue(papszHdr, "UL_Y_COORDINATE") != nullptr && |
691 | 6.16k | CSLFetchNameValue(papszHdr, "LR_X_COORDINATE") != nullptr && |
692 | 5.42k | CSLFetchNameValue(papszHdr, "LR_Y_COORDINATE") != nullptr) |
693 | 4.96k | { |
694 | 4.96k | const double dfULX = |
695 | 4.96k | CPLAtofM(CSLFetchNameValue(papszHdr, "UL_X_COORDINATE")); |
696 | 4.96k | const double dfULY = |
697 | 4.96k | CPLAtofM(CSLFetchNameValue(papszHdr, "UL_Y_COORDINATE")); |
698 | 4.96k | const double dfLRX = |
699 | 4.96k | CPLAtofM(CSLFetchNameValue(papszHdr, "LR_X_COORDINATE")); |
700 | 4.96k | const double dfLRY = |
701 | 4.96k | CPLAtofM(CSLFetchNameValue(papszHdr, "LR_Y_COORDINATE")); |
702 | | |
703 | 4.96k | poDS->m_gt.xscale = (dfLRX - dfULX) / (poDS->nRasterXSize - 1); |
704 | 4.96k | poDS->m_gt.xrot = 0.0; |
705 | 4.96k | poDS->m_gt.yrot = 0.0; |
706 | 4.96k | poDS->m_gt.yscale = (dfLRY - dfULY) / (poDS->nRasterYSize - 1); |
707 | | |
708 | 4.96k | poDS->m_gt.xorig = dfULX - poDS->m_gt.xscale * 0.5; |
709 | 4.96k | poDS->m_gt.yorig = dfULY - poDS->m_gt.yscale * 0.5; |
710 | | |
711 | 4.96k | poDS->bGotTransform = true; |
712 | 4.96k | } |
713 | | |
714 | | /* -------------------------------------------------------------------- */ |
715 | | /* Try and parse the coordinate system. */ |
716 | | /* -------------------------------------------------------------------- */ |
717 | 52.5k | poDS->ParseCoordinateSystem(papszHdr); |
718 | | |
719 | | /* -------------------------------------------------------------------- */ |
720 | | /* Initialize any PAM information. */ |
721 | | /* -------------------------------------------------------------------- */ |
722 | 52.5k | poDS->TryLoadXML(); |
723 | | |
724 | | /* -------------------------------------------------------------------- */ |
725 | | /* Check for overviews. */ |
726 | | /* -------------------------------------------------------------------- */ |
727 | 52.5k | poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename); |
728 | | |
729 | 52.5k | return poDS.release(); |
730 | 52.5k | } |
731 | | |
732 | | /************************************************************************/ |
733 | | /* GDALRegister_GenBin() */ |
734 | | /************************************************************************/ |
735 | | |
736 | | void GDALRegister_GenBin() |
737 | | |
738 | 24 | { |
739 | 24 | if (GDALGetDriverByName("GenBin") != nullptr) |
740 | 0 | return; |
741 | | |
742 | 24 | GDALDriver *poDriver = new GDALDriver(); |
743 | | |
744 | 24 | poDriver->SetDescription("GenBin"); |
745 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
746 | 24 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, |
747 | 24 | "Generic Binary (.hdr Labelled)"); |
748 | 24 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/genbin.html"); |
749 | 24 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
750 | | |
751 | 24 | poDriver->pfnOpen = GenBinDataset::Open; |
752 | | |
753 | 24 | GetGDALDriverManager()->RegisterDriver(poDriver); |
754 | 24 | } |