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