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