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