Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Erdas EIR Raw Driver
4 : * Purpose: Implementation of EIRDataset
5 : * Author: Adam Milling, amilling@alumni.uwaterloo.ca
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2009-2010, 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 <limits>
20 :
21 : /************************************************************************/
22 : /* ==================================================================== */
23 : /* EIRDataset */
24 : /* ==================================================================== */
25 : /************************************************************************/
26 :
27 2 : class EIRDataset final : public RawDataset
28 : {
29 : friend class RawRasterBand;
30 :
31 : VSILFILE *fpImage = nullptr; // image data file
32 : bool bGotTransform = false;
33 : double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
34 : bool bHDRDirty = false;
35 : CPLStringList aosHDR{};
36 : char **papszExtraFiles = nullptr;
37 :
38 : void ResetKeyValue(const char *pszKey, const char *pszValue);
39 : #ifdef unused
40 : const char *GetKeyValue(const char *pszKey, const char *pszDefault = "");
41 : #endif
42 :
43 : CPL_DISALLOW_COPY_ASSIGN(EIRDataset)
44 :
45 : CPLErr Close() override;
46 :
47 : public:
48 : EIRDataset();
49 : ~EIRDataset() override;
50 :
51 : CPLErr GetGeoTransform(double *padfTransform) override;
52 :
53 : char **GetFileList() override;
54 :
55 : static int Identify(GDALOpenInfo *);
56 : static GDALDataset *Open(GDALOpenInfo *);
57 : };
58 :
59 : /************************************************************************/
60 : /* ==================================================================== */
61 : /* EIRDataset */
62 : /* ==================================================================== */
63 : /************************************************************************/
64 :
65 : /************************************************************************/
66 : /* EIRDataset() */
67 : /************************************************************************/
68 :
69 : EIRDataset::EIRDataset() = default;
70 :
71 : /************************************************************************/
72 : /* ~EIRDataset() */
73 : /************************************************************************/
74 :
75 4 : EIRDataset::~EIRDataset()
76 :
77 : {
78 2 : EIRDataset::Close();
79 4 : }
80 :
81 : /************************************************************************/
82 : /* Close() */
83 : /************************************************************************/
84 :
85 4 : CPLErr EIRDataset::Close()
86 : {
87 4 : CPLErr eErr = CE_None;
88 4 : if (nOpenFlags != OPEN_FLAGS_CLOSED)
89 : {
90 2 : if (EIRDataset::FlushCache(true) != CE_None)
91 0 : eErr = CE_Failure;
92 :
93 2 : if (nBands > 0 && GetAccess() == GA_Update)
94 : {
95 : RawRasterBand *poBand =
96 0 : reinterpret_cast<RawRasterBand *>(GetRasterBand(1));
97 :
98 0 : int bNoDataSet = FALSE;
99 0 : const double dfNoData = poBand->GetNoDataValue(&bNoDataSet);
100 0 : if (bNoDataSet)
101 : {
102 0 : ResetKeyValue("NODATA", CPLString().Printf("%.8g", dfNoData));
103 : }
104 : }
105 :
106 2 : if (fpImage)
107 : {
108 2 : if (VSIFCloseL(fpImage) != 0)
109 0 : eErr = CE_Failure;
110 : }
111 :
112 2 : CSLDestroy(papszExtraFiles);
113 :
114 2 : if (GDALPamDataset::Close() != CE_None)
115 0 : eErr = CE_Failure;
116 : }
117 4 : return eErr;
118 : }
119 :
120 : #ifdef unused
121 : /************************************************************************/
122 : /* GetKeyValue() */
123 : /************************************************************************/
124 :
125 : const char *EIRDataset::GetKeyValue(const char *pszKey, const char *pszDefault)
126 :
127 : {
128 : const char *const *papszHDR = aosHDR.List();
129 : for (int i = 0; papszHDR[i] != nullptr; i++)
130 : {
131 : if (EQUALN(pszKey, papszHDR[i], strlen(pszKey)) &&
132 : isspace((unsigned char)papszHDR[i][strlen(pszKey)]))
133 : {
134 : const char *pszValue = papszHDR[i] + strlen(pszKey);
135 : while (isspace(static_cast<unsigned char>(*pszValue)))
136 : pszValue++;
137 :
138 : return pszValue;
139 : }
140 : }
141 :
142 : return pszDefault;
143 : }
144 : #endif
145 :
146 : /************************************************************************/
147 : /* ResetKeyValue() */
148 : /* */
149 : /* Replace or add the keyword with the indicated value in the */
150 : /* papszHDR list. */
151 : /************************************************************************/
152 :
153 0 : void EIRDataset::ResetKeyValue(const char *pszKey, const char *pszValue)
154 :
155 : {
156 0 : if (strlen(pszValue) > 65)
157 : {
158 0 : CPLAssert(strlen(pszValue) <= 65);
159 0 : return;
160 : }
161 :
162 0 : char szNewLine[82] = {'\0'};
163 0 : snprintf(szNewLine, sizeof(szNewLine), "%-15s%s", pszKey, pszValue);
164 :
165 0 : char **papszHDR = aosHDR.List();
166 0 : for (int i = aosHDR.size() - 1; i >= 0; i--)
167 : {
168 0 : if (EQUALN(papszHDR[i], szNewLine, strlen(pszKey) + 1))
169 : {
170 0 : if (strcmp(papszHDR[i], szNewLine) != 0)
171 : {
172 0 : CPLFree(papszHDR[i]);
173 0 : papszHDR[i] = CPLStrdup(szNewLine);
174 0 : bHDRDirty = true;
175 : }
176 0 : return;
177 : }
178 : }
179 :
180 0 : bHDRDirty = true;
181 0 : aosHDR.AddString(szNewLine);
182 : }
183 :
184 : /************************************************************************/
185 : /* GetGeoTransform() */
186 : /************************************************************************/
187 :
188 0 : CPLErr EIRDataset::GetGeoTransform(double *padfTransform)
189 :
190 : {
191 0 : if (bGotTransform)
192 : {
193 0 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
194 0 : return CE_None;
195 : }
196 :
197 0 : return GDALPamDataset::GetGeoTransform(padfTransform);
198 : }
199 :
200 : /************************************************************************/
201 : /* GetFileList() */
202 : /************************************************************************/
203 :
204 1 : char **EIRDataset::GetFileList()
205 :
206 : {
207 : // Main data file, etc.
208 1 : char **papszFileList = GDALPamDataset::GetFileList();
209 :
210 : // Header file.
211 1 : papszFileList = CSLInsertStrings(papszFileList, -1, papszExtraFiles);
212 :
213 1 : return papszFileList;
214 : }
215 :
216 : /************************************************************************/
217 : /* Identify() */
218 : /************************************************************************/
219 :
220 51570 : int EIRDataset::Identify(GDALOpenInfo *poOpenInfo)
221 :
222 : {
223 51570 : if (poOpenInfo->nHeaderBytes < 100)
224 48185 : return FALSE;
225 :
226 3385 : if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
227 : "IMAGINE_RAW_FILE") == nullptr)
228 3381 : return FALSE;
229 :
230 4 : return TRUE;
231 : }
232 :
233 : /************************************************************************/
234 : /* Open() */
235 : /************************************************************************/
236 :
237 2 : GDALDataset *EIRDataset::Open(GDALOpenInfo *poOpenInfo)
238 :
239 : {
240 2 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
241 0 : return nullptr;
242 :
243 : /* header example and description
244 :
245 : IMAGINE_RAW_FILE // must be on first line, by itself
246 : WIDTH 581 // number of columns in the image
247 : HEIGHT 695 // number of rows in the image
248 : NUM_LAYERS 3 // number of spectral bands in the image; default 1
249 : PIXEL_FILES raw8_3n_ui_sanjack.bl // raster file
250 : // default: same name with no extension
251 : FORMAT BIL // BIL BIP BSQ; default BIL
252 : DATATYPE U8 // U1 U2 U4 U8 U16 U32 S16 S32 F32 F64; default U8
253 : BYTE_ORDER // LSB MSB; required for U16 U32 S16 S32 F32 F64
254 : DATA_OFFSET // start of image data in raster file; default 0 bytes
255 : END_RAW_FILE // end RAW file - stop reading
256 :
257 : For a true color image with three bands (R, G, B) stored using 8 bits
258 : for each pixel in each band, DATA_TYPE equals U8 and NUM_LAYERS equals
259 : 3 for a total of 24 bits per pixel.
260 :
261 : Note that the current version of ERDAS Raw Raster Reader/Writer does
262 : not support the LAYER_SKIP_BYTES, RECORD_SKIP_BYTES, TILE_WIDTH and
263 : TILE_HEIGHT directives. Since the reader does not read the PIXEL_FILES
264 : directive, the reader always assumes that the raw binary file is the
265 : dataset, and the name of this file is the name of the header without the
266 : extension. Currently, the reader does not support multiple raw binary
267 : files in one dataset or a single file with both the header and the raw
268 : binary data at the same time.
269 : */
270 :
271 2 : int nRows = -1;
272 2 : int nCols = -1;
273 2 : int nBands = 1;
274 2 : int nSkipBytes = 0;
275 2 : int nLineCount = 0;
276 2 : GDALDataType eDataType = GDT_Byte;
277 2 : int nBits = 8;
278 2 : char chByteOrder = 'M';
279 2 : char szLayout[10] = "BIL";
280 4 : CPLStringList aosHDR;
281 :
282 : // default raster file: same name with no extension
283 4 : const CPLString osPath = CPLGetPath(poOpenInfo->pszFilename);
284 4 : const CPLString osName = CPLGetBasename(poOpenInfo->pszFilename);
285 4 : CPLString osRasterFilename = CPLFormCIFilename(osPath, osName, "");
286 :
287 : // parse the header file
288 2 : const char *pszLine = nullptr;
289 2 : VSIRewindL(poOpenInfo->fpL);
290 18 : while ((pszLine = CPLReadLineL(poOpenInfo->fpL)) != nullptr)
291 : {
292 18 : nLineCount++;
293 :
294 18 : if ((nLineCount == 1) && !EQUAL(pszLine, "IMAGINE_RAW_FILE"))
295 : {
296 0 : return nullptr;
297 : }
298 :
299 18 : if ((nLineCount > 50) || EQUAL(pszLine, "END_RAW_FILE"))
300 : {
301 : break;
302 : }
303 :
304 16 : if (strlen(pszLine) > 1000)
305 0 : break;
306 :
307 16 : aosHDR.AddString(pszLine);
308 :
309 : const CPLStringList aosTokens(
310 16 : CSLTokenizeStringComplex(pszLine, " \t", TRUE, FALSE));
311 16 : if (aosTokens.size() < 2)
312 : {
313 2 : continue;
314 : }
315 :
316 14 : if (EQUAL(aosTokens[0], "WIDTH"))
317 : {
318 2 : nCols = atoi(aosTokens[1]);
319 : }
320 12 : else if (EQUAL(aosTokens[0], "HEIGHT"))
321 : {
322 2 : nRows = atoi(aosTokens[1]);
323 : }
324 10 : else if (EQUAL(aosTokens[0], "NUM_LAYERS"))
325 : {
326 2 : nBands = atoi(aosTokens[1]);
327 : }
328 8 : else if (EQUAL(aosTokens[0], "PIXEL_FILES"))
329 : {
330 2 : osRasterFilename = CPLFormCIFilename(osPath, aosTokens[1], "");
331 : }
332 6 : else if (EQUAL(aosTokens[0], "FORMAT"))
333 : {
334 2 : snprintf(szLayout, sizeof(szLayout), "%s", aosTokens[1]);
335 : }
336 6 : else if (EQUAL(aosTokens[0], "DATATYPE") ||
337 2 : EQUAL(aosTokens[0], "DATA_TYPE"))
338 : {
339 4 : if (EQUAL(aosTokens[1], "U1") || EQUAL(aosTokens[1], "U2") ||
340 4 : EQUAL(aosTokens[1], "U4") || EQUAL(aosTokens[1], "U8"))
341 : {
342 2 : nBits = 8;
343 2 : eDataType = GDT_Byte;
344 : }
345 0 : else if (EQUAL(aosTokens[1], "U16"))
346 : {
347 0 : nBits = 16;
348 0 : eDataType = GDT_UInt16;
349 : }
350 0 : else if (EQUAL(aosTokens[1], "U32"))
351 : {
352 0 : nBits = 32;
353 0 : eDataType = GDT_UInt32;
354 : }
355 0 : else if (EQUAL(aosTokens[1], "S16"))
356 : {
357 0 : nBits = 16;
358 0 : eDataType = GDT_Int16;
359 : }
360 0 : else if (EQUAL(aosTokens[1], "S32"))
361 : {
362 0 : nBits = 32;
363 0 : eDataType = GDT_Int32;
364 : }
365 0 : else if (EQUAL(aosTokens[1], "F32"))
366 : {
367 0 : nBits = 32;
368 0 : eDataType = GDT_Float32;
369 : }
370 0 : else if (EQUAL(aosTokens[1], "F64"))
371 : {
372 0 : nBits = 64;
373 0 : eDataType = GDT_Float64;
374 : }
375 : else
376 : {
377 0 : CPLError(CE_Failure, CPLE_NotSupported,
378 : "EIR driver does not support DATATYPE %s.",
379 : aosTokens[1]);
380 0 : return nullptr;
381 : }
382 : }
383 2 : else if (EQUAL(aosTokens[0], "BYTE_ORDER"))
384 : {
385 : // M for MSB, L for LSB
386 0 : chByteOrder = static_cast<char>(
387 0 : toupper(static_cast<unsigned char>(aosTokens[1][0])));
388 : }
389 2 : else if (EQUAL(aosTokens[0], "DATA_OFFSET"))
390 : {
391 2 : nSkipBytes = atoi(aosTokens[1]); // TBD: is this mapping right?
392 2 : if (nSkipBytes < 0)
393 : {
394 0 : return nullptr;
395 : }
396 : }
397 : }
398 2 : CPL_IGNORE_RET_VAL(nBits);
399 :
400 : /* -------------------------------------------------------------------- */
401 : /* Did we get the required keywords? If not we return with */
402 : /* this never having been considered to be a match. This isn't */
403 : /* an error! */
404 : /* -------------------------------------------------------------------- */
405 2 : if (nRows <= 0 || nCols <= 0 || nBands <= 0)
406 : {
407 0 : return nullptr;
408 : }
409 :
410 4 : if (!GDALCheckDatasetDimensions(nCols, nRows) ||
411 2 : !GDALCheckBandCount(nBands, FALSE))
412 : {
413 0 : return nullptr;
414 : }
415 :
416 : /* -------------------------------------------------------------------- */
417 : /* Confirm the requested access is supported. */
418 : /* -------------------------------------------------------------------- */
419 2 : if (poOpenInfo->eAccess == GA_Update)
420 : {
421 0 : CPLError(CE_Failure, CPLE_NotSupported,
422 : "The EIR driver does not support update access to existing"
423 : " datasets.");
424 0 : return nullptr;
425 : }
426 : /* -------------------------------------------------------------------- */
427 : /* Create a corresponding GDALDataset. */
428 : /* -------------------------------------------------------------------- */
429 4 : auto poDS = std::make_unique<EIRDataset>();
430 :
431 : /* -------------------------------------------------------------------- */
432 : /* Capture some information from the file that is of interest. */
433 : /* -------------------------------------------------------------------- */
434 2 : poDS->nRasterXSize = nCols;
435 2 : poDS->nRasterYSize = nRows;
436 2 : poDS->aosHDR = std::move(aosHDR);
437 :
438 : /* -------------------------------------------------------------------- */
439 : /* Open target binary file. */
440 : /* -------------------------------------------------------------------- */
441 2 : poDS->fpImage = VSIFOpenL(osRasterFilename.c_str(), "rb");
442 2 : if (poDS->fpImage == nullptr)
443 : {
444 0 : CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s: %s",
445 0 : osRasterFilename.c_str(), VSIStrerror(errno));
446 0 : return nullptr;
447 : }
448 4 : poDS->papszExtraFiles =
449 2 : CSLAddString(poDS->papszExtraFiles, osRasterFilename);
450 :
451 2 : poDS->eAccess = poOpenInfo->eAccess;
452 :
453 : /* -------------------------------------------------------------------- */
454 : /* Compute the line offset. */
455 : /* -------------------------------------------------------------------- */
456 2 : const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
457 2 : int nPixelOffset = 0;
458 2 : int nLineOffset = 0;
459 2 : vsi_l_offset nBandOffset = 0;
460 :
461 2 : if (EQUAL(szLayout, "BIP"))
462 : {
463 0 : nPixelOffset = nItemSize * nBands;
464 0 : if (nPixelOffset > INT_MAX / nCols)
465 : {
466 0 : return nullptr;
467 : }
468 0 : nLineOffset = nPixelOffset * nCols;
469 0 : nBandOffset = static_cast<vsi_l_offset>(nItemSize);
470 : }
471 2 : else if (EQUAL(szLayout, "BSQ"))
472 : {
473 0 : nPixelOffset = nItemSize;
474 0 : if (nPixelOffset > INT_MAX / nCols)
475 : {
476 0 : return nullptr;
477 : }
478 0 : nLineOffset = nPixelOffset * nCols;
479 0 : nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
480 : }
481 : else /* assume BIL */
482 : {
483 2 : nPixelOffset = nItemSize;
484 2 : if (nItemSize > INT_MAX / nBands ||
485 2 : nItemSize * nBands > INT_MAX / nCols)
486 : {
487 0 : return nullptr;
488 : }
489 2 : nLineOffset = nItemSize * nBands * nCols;
490 2 : nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nCols;
491 : }
492 :
493 2 : if (poDS->nBands > 1)
494 : {
495 0 : if (nBandOffset >
496 0 : std::numeric_limits<vsi_l_offset>::max() / (poDS->nBands - 1) ||
497 0 : static_cast<vsi_l_offset>(nSkipBytes) >
498 0 : std::numeric_limits<vsi_l_offset>::max() -
499 0 : nBandOffset * (poDS->nBands - 1))
500 : {
501 0 : return nullptr;
502 : }
503 : }
504 :
505 2 : if (!RAWDatasetCheckMemoryUsage(
506 2 : poDS->nRasterXSize, poDS->nRasterYSize, nBands, nItemSize,
507 2 : nPixelOffset, nLineOffset, nSkipBytes, nBandOffset, poDS->fpImage))
508 : {
509 0 : return nullptr;
510 : }
511 :
512 2 : poDS->SetDescription(poOpenInfo->pszFilename);
513 2 : poDS->PamInitialize();
514 :
515 : /* -------------------------------------------------------------------- */
516 : /* Create band information objects. */
517 : /* -------------------------------------------------------------------- */
518 4 : for (int i = 0; i < nBands; i++)
519 : {
520 : auto poBand = RawRasterBand::Create(
521 4 : poDS.get(), i + 1, poDS->fpImage, nSkipBytes + nBandOffset * i,
522 : nPixelOffset, nLineOffset, eDataType,
523 : chByteOrder == 'I' || chByteOrder == 'L'
524 2 : ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
525 : : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
526 2 : RawRasterBand::OwnFP::NO);
527 2 : if (!poBand)
528 0 : return nullptr;
529 2 : poDS->SetBand(i + 1, std::move(poBand));
530 : }
531 :
532 : /* -------------------------------------------------------------------- */
533 : /* look for a worldfile */
534 : /* -------------------------------------------------------------------- */
535 :
536 2 : if (!poDS->bGotTransform)
537 2 : poDS->bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
538 2 : poOpenInfo->pszFilename, nullptr, poDS->adfGeoTransform));
539 :
540 2 : if (!poDS->bGotTransform)
541 2 : poDS->bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
542 2 : poOpenInfo->pszFilename, "wld", poDS->adfGeoTransform));
543 :
544 : /* -------------------------------------------------------------------- */
545 : /* Initialize any PAM information. */
546 : /* -------------------------------------------------------------------- */
547 2 : poDS->TryLoadXML();
548 :
549 : /* -------------------------------------------------------------------- */
550 : /* Check for overviews. */
551 : /* -------------------------------------------------------------------- */
552 2 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
553 :
554 2 : return poDS.release();
555 : }
556 :
557 : /************************************************************************/
558 : /* GDALRegister_EIR() */
559 : /************************************************************************/
560 :
561 1595 : void GDALRegister_EIR()
562 :
563 : {
564 1595 : if (GDALGetDriverByName("EIR") != nullptr)
565 302 : return;
566 :
567 1293 : GDALDriver *poDriver = new GDALDriver();
568 :
569 1293 : poDriver->SetDescription("EIR");
570 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
571 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Erdas Imagine Raw");
572 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/eir.html");
573 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
574 :
575 1293 : poDriver->pfnOpen = EIRDataset::Open;
576 1293 : poDriver->pfnIdentify = EIRDataset::Identify;
577 :
578 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
579 : }
|