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 : FILE *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 : const char *EIRDataset::GetKeyValue( const char *pszKey,
127 0 : 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 : && 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 9621 : int EIRDataset::Identify( GDALOpenInfo * poOpenInfo )
232 :
233 : {
234 9621 : if( poOpenInfo->nHeaderBytes < 100 )
235 9316 : return FALSE;
236 :
237 305 : if( strstr((const char *) poOpenInfo->pabyHeader,
238 : "IMAGINE_RAW_FILE" ) == NULL )
239 304 : return FALSE;
240 :
241 1 : return TRUE;
242 : }
243 :
244 : /************************************************************************/
245 : /* Open() */
246 : /************************************************************************/
247 :
248 1376 : GDALDataset *EIRDataset::Open( GDALOpenInfo * poOpenInfo )
249 :
250 : {
251 : int i;
252 : FILE *fp;
253 : const char * pszLine;
254 :
255 :
256 1376 : if( !Identify( poOpenInfo ) )
257 1375 : 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 )) )
308 : {
309 : char **papszTokens;
310 :
311 9 : nLineCount++;
312 :
313 9 : if ( (nLineCount == 1) && !EQUAL(pszLine,"IMAGINE_RAW_FILE") ) {
314 1 : 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 2 : else if( EQUAL(papszTokens[0],"DATATYPE") )
356 : {
357 2 : if ( EQUAL(papszTokens[1], "U1")
358 : || EQUAL(papszTokens[1], "U2")
359 : || EQUAL(papszTokens[1], "U4")
360 : || EQUAL(papszTokens[1], "U8") ) {
361 1 : nBits = 8;
362 1 : eDataType = GDT_Byte;
363 : }
364 0 : else if( EQUAL(papszTokens[1], "U16") ) {
365 0 : nBits = 16;
366 0 : eDataType = GDT_UInt16;
367 : }
368 0 : else if( EQUAL(papszTokens[1], "U32") ) {
369 0 : nBits = 32;
370 0 : eDataType = GDT_UInt32;
371 : }
372 0 : else if( EQUAL(papszTokens[1], "S16") ) {
373 0 : nBits = 16;
374 0 : eDataType = GDT_Int16;
375 : }
376 0 : else if( EQUAL(papszTokens[1], "S32") ) {
377 0 : nBits = 32;
378 0 : eDataType = GDT_Int32;
379 : }
380 0 : else if( EQUAL(papszTokens[1], "F32") ) {
381 0 : nBits = 32;
382 0 : eDataType = GDT_Float32;
383 : }
384 0 : else if( EQUAL(papszTokens[1], "F64") ) {
385 0 : nBits = 64;
386 0 : eDataType = GDT_Float64;
387 : }
388 : else {
389 : CPLError( CE_Failure, CPLE_NotSupported,
390 : "EIR driver does not support DATATYPE %s.",
391 0 : papszTokens[1] );
392 0 : CSLDestroy( papszTokens );
393 0 : CSLDestroy( papszHDR );
394 0 : VSIFCloseL( fp );
395 0 : return NULL;
396 : }
397 : }
398 1 : else if( EQUAL(papszTokens[0],"BYTE_ORDER") )
399 : {
400 : // M for MSB, L for LSB
401 0 : chByteOrder = toupper(papszTokens[1][0]);
402 : }
403 1 : else if( EQUAL(papszTokens[0],"DATA_OFFSET") )
404 : {
405 1 : nSkipBytes = atoi(papszTokens[1]); // TBD: is this mapping right?
406 : }
407 :
408 7 : CSLDestroy( papszTokens );
409 : }
410 :
411 1 : VSIFCloseL( fp );
412 :
413 :
414 : /* -------------------------------------------------------------------- */
415 : /* Did we get the required keywords? If not we return with */
416 : /* this never having been considered to be a match. This isn't */
417 : /* an error! */
418 : /* -------------------------------------------------------------------- */
419 1 : if( nRows == -1 || nCols == -1 )
420 : {
421 0 : CSLDestroy( papszHDR );
422 0 : return NULL;
423 : }
424 :
425 1 : if (!GDALCheckDatasetDimensions(nCols, nRows) ||
426 : !GDALCheckBandCount(nBands, FALSE))
427 : {
428 0 : CSLDestroy( papszHDR );
429 0 : return NULL;
430 : }
431 :
432 : /* -------------------------------------------------------------------- */
433 : /* Confirm the requested access is supported. */
434 : /* -------------------------------------------------------------------- */
435 1 : if( poOpenInfo->eAccess == GA_Update )
436 : {
437 0 : CSLDestroy( papszHDR );
438 : CPLError( CE_Failure, CPLE_NotSupported,
439 : "The EIR driver does not support update access to existing"
440 0 : " datasets.\n" );
441 0 : return NULL;
442 : }
443 : /* -------------------------------------------------------------------- */
444 : /* Create a corresponding GDALDataset. */
445 : /* -------------------------------------------------------------------- */
446 : EIRDataset *poDS;
447 :
448 1 : poDS = new EIRDataset();
449 :
450 : /* -------------------------------------------------------------------- */
451 : /* Capture some information from the file that is of interest. */
452 : /* -------------------------------------------------------------------- */
453 1 : poDS->nRasterXSize = nCols;
454 1 : poDS->nRasterYSize = nRows;
455 1 : poDS->papszHDR = papszHDR;
456 :
457 :
458 : /* -------------------------------------------------------------------- */
459 : /* Open target binary file. */
460 : /* -------------------------------------------------------------------- */
461 2 : poDS->fpImage = VSIFOpenL( osRasterFilename.c_str(), "rb" );
462 1 : if( poDS->fpImage == NULL )
463 : {
464 : CPLError( CE_Failure, CPLE_OpenFailed,
465 : "Failed to open %s.\n%s",
466 0 : osRasterFilename.c_str(), VSIStrerror( errno ) );
467 0 : delete poDS;
468 0 : return NULL;
469 : }
470 : poDS->papszExtraFiles =
471 : CSLAddString( poDS->papszExtraFiles,
472 1 : osRasterFilename );
473 :
474 1 : poDS->eAccess = poOpenInfo->eAccess;
475 :
476 :
477 : /* -------------------------------------------------------------------- */
478 : /* Compute the line offset. */
479 : /* -------------------------------------------------------------------- */
480 1 : int nItemSize = GDALGetDataTypeSize(eDataType)/8;
481 : int nPixelOffset, nLineOffset;
482 : vsi_l_offset nBandOffset;
483 :
484 1 : if( EQUAL(szLayout,"BIP") )
485 : {
486 0 : nPixelOffset = nItemSize * nBands;
487 0 : nLineOffset = nPixelOffset * nCols;
488 0 : nBandOffset = (vsi_l_offset)nItemSize;
489 : }
490 1 : else if( EQUAL(szLayout,"BSQ") )
491 : {
492 0 : nPixelOffset = nItemSize;
493 0 : nLineOffset = nPixelOffset * nCols;
494 0 : nBandOffset = (vsi_l_offset)nLineOffset * nRows;
495 : }
496 : else /* assume BIL */
497 : {
498 1 : nPixelOffset = nItemSize;
499 1 : nLineOffset = nItemSize * nBands * nCols;
500 1 : nBandOffset = (vsi_l_offset)nItemSize * nCols;
501 : }
502 :
503 1 : poDS->SetDescription( poOpenInfo->pszFilename );
504 1 : poDS->PamInitialize();
505 :
506 : /* -------------------------------------------------------------------- */
507 : /* Create band information objects. */
508 : /* -------------------------------------------------------------------- */
509 1 : poDS->nBands = nBands;
510 2 : for( i = 0; i < poDS->nBands; i++ )
511 : {
512 : RawRasterBand *poBand;
513 :
514 : poBand =
515 : new RawRasterBand( poDS, i+1, poDS->fpImage,
516 : nSkipBytes + nBandOffset * i,
517 : nPixelOffset, nLineOffset, eDataType,
518 : #ifdef CPL_LSB
519 : chByteOrder == 'I' || chByteOrder == 'L',
520 : #else
521 : chByteOrder == 'M',
522 : #endif
523 1 : nBits);
524 :
525 :
526 1 : poDS->SetBand( i+1, poBand );
527 : }
528 :
529 :
530 : /* -------------------------------------------------------------------- */
531 : /* look for a worldfile */
532 : /* -------------------------------------------------------------------- */
533 :
534 1 : if( !poDS->bGotTransform )
535 : poDS->bGotTransform =
536 : GDALReadWorldFile( poOpenInfo->pszFilename, 0,
537 1 : poDS->adfGeoTransform );
538 :
539 1 : if( !poDS->bGotTransform )
540 : poDS->bGotTransform =
541 : GDALReadWorldFile( poOpenInfo->pszFilename, "wld",
542 1 : poDS->adfGeoTransform );
543 :
544 : /* -------------------------------------------------------------------- */
545 : /* Initialize any PAM information. */
546 : /* -------------------------------------------------------------------- */
547 1 : poDS->TryLoadXML();
548 :
549 : /* -------------------------------------------------------------------- */
550 : /* Check for overviews. */
551 : /* -------------------------------------------------------------------- */
552 1 : poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
553 :
554 1 : return( poDS );
555 : }
556 :
557 :
558 : /************************************************************************/
559 : /* GDALRegister_EIR() */
560 : /************************************************************************/
561 :
562 409 : void GDALRegister_EIR()
563 :
564 : {
565 : GDALDriver *poDriver;
566 :
567 409 : if( GDALGetDriverByName( "EIR" ) == NULL )
568 : {
569 392 : poDriver = new GDALDriver();
570 :
571 392 : poDriver->SetDescription( "EIR" );
572 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
573 392 : "Erdas Imagine Raw" );
574 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
575 392 : "frmt_various.html#EIR" );
576 :
577 392 : poDriver->pfnOpen = EIRDataset::Open;
578 392 : poDriver->pfnIdentify = EIRDataset::Identify;
579 :
580 392 : GetGDALDriverManager()->RegisterDriver( poDriver );
581 : }
582 409 : }
|