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