1 /******************************************************************************
2 *
3 * Project: GDAL
4 * Purpose: Generic Binary format driver (.hdr but not ESRI .hdr!)
5 * Author: Frank Warmerdam, warmerdam@pobox.com
6 *
7 ******************************************************************************
8 * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
9 * Copyright (c) 2008-2011, 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 <cstdlib>
36
37 CPL_CVSID("$Id: genbindataset.cpp 18c08feb1540d58a5076265e837d49ace106f302 2021-03-08 23:30:56 +0100 Even Rouault $")
38
39 /* ==================================================================== */
40 /* Table relating USGS and ESRI state plane zones. */
41 /* ==================================================================== */
42 constexpr int anUsgsEsriZones[] =
43 {
44 101, 3101,
45 102, 3126,
46 201, 3151,
47 202, 3176,
48 203, 3201,
49 301, 3226,
50 302, 3251,
51 401, 3276,
52 402, 3301,
53 403, 3326,
54 404, 3351,
55 405, 3376,
56 406, 3401,
57 407, 3426,
58 501, 3451,
59 502, 3476,
60 503, 3501,
61 600, 3526,
62 700, 3551,
63 901, 3601,
64 902, 3626,
65 903, 3576,
66 1001, 3651,
67 1002, 3676,
68 1101, 3701,
69 1102, 3726,
70 1103, 3751,
71 1201, 3776,
72 1202, 3801,
73 1301, 3826,
74 1302, 3851,
75 1401, 3876,
76 1402, 3901,
77 1501, 3926,
78 1502, 3951,
79 1601, 3976,
80 1602, 4001,
81 1701, 4026,
82 1702, 4051,
83 1703, 6426,
84 1801, 4076,
85 1802, 4101,
86 1900, 4126,
87 2001, 4151,
88 2002, 4176,
89 2101, 4201,
90 2102, 4226,
91 2103, 4251,
92 2111, 6351,
93 2112, 6376,
94 2113, 6401,
95 2201, 4276,
96 2202, 4301,
97 2203, 4326,
98 2301, 4351,
99 2302, 4376,
100 2401, 4401,
101 2402, 4426,
102 2403, 4451,
103 2500, 0,
104 2501, 4476,
105 2502, 4501,
106 2503, 4526,
107 2600, 0,
108 2601, 4551,
109 2602, 4576,
110 2701, 4601,
111 2702, 4626,
112 2703, 4651,
113 2800, 4676,
114 2900, 4701,
115 3001, 4726,
116 3002, 4751,
117 3003, 4776,
118 3101, 4801,
119 3102, 4826,
120 3103, 4851,
121 3104, 4876,
122 3200, 4901,
123 3301, 4926,
124 3302, 4951,
125 3401, 4976,
126 3402, 5001,
127 3501, 5026,
128 3502, 5051,
129 3601, 5076,
130 3602, 5101,
131 3701, 5126,
132 3702, 5151,
133 3800, 5176,
134 3900, 0,
135 3901, 5201,
136 3902, 5226,
137 4001, 5251,
138 4002, 5276,
139 4100, 5301,
140 4201, 5326,
141 4202, 5351,
142 4203, 5376,
143 4204, 5401,
144 4205, 5426,
145 4301, 5451,
146 4302, 5476,
147 4303, 5501,
148 4400, 5526,
149 4501, 5551,
150 4502, 5576,
151 4601, 5601,
152 4602, 5626,
153 4701, 5651,
154 4702, 5676,
155 4801, 5701,
156 4802, 5726,
157 4803, 5751,
158 4901, 5776,
159 4902, 5801,
160 4903, 5826,
161 4904, 5851,
162 5001, 6101,
163 5002, 6126,
164 5003, 6151,
165 5004, 6176,
166 5005, 6201,
167 5006, 6226,
168 5007, 6251,
169 5008, 6276,
170 5009, 6301,
171 5010, 6326,
172 5101, 5876,
173 5102, 5901,
174 5103, 5926,
175 5104, 5951,
176 5105, 5976,
177 5201, 6001,
178 5200, 6026,
179 5200, 6076,
180 5201, 6051,
181 5202, 6051,
182 5300, 0,
183 5400, 0
184 };
185
186 /************************************************************************/
187 /* ==================================================================== */
188 /* GenBinDataset */
189 /* ==================================================================== */
190 /************************************************************************/
191
192 class GenBinDataset final: public RawDataset
193 {
194 friend class GenBinBitRasterBand;
195
196 VSILFILE *fpImage; // image data file.
197
198 bool bGotTransform;
199 double adfGeoTransform[6];
200 char *pszProjection;
201
202 char **papszHDR;
203
204 void ParseCoordinateSystem( char ** );
205
206 CPL_DISALLOW_COPY_ASSIGN(GenBinDataset)
207
208 public:
209 GenBinDataset();
210 ~GenBinDataset() override;
211
212 CPLErr GetGeoTransform( double * padfTransform ) override;
213 const char *_GetProjectionRef(void) override;
GetSpatialRef() const214 const OGRSpatialReference* GetSpatialRef() const override {
215 return GetSpatialRefFromOldGetProjectionRef();
216 }
217
218 char **GetFileList() override;
219
220 static GDALDataset *Open( GDALOpenInfo * );
221 };
222
223 /************************************************************************/
224 /* ==================================================================== */
225 /* GenBinBitRasterBand */
226 /* ==================================================================== */
227 /************************************************************************/
228
229 class GenBinBitRasterBand final: public GDALPamRasterBand
230 {
231 int nBits;
232
233 CPL_DISALLOW_COPY_ASSIGN(GenBinBitRasterBand)
234
235 public:
236 GenBinBitRasterBand( GenBinDataset *poDS, int nBits );
~GenBinBitRasterBand()237 ~GenBinBitRasterBand() override {}
238
239 CPLErr IReadBlock( int, int, void * ) override;
240 };
241
242 /************************************************************************/
243 /* GenBinBitRasterBand() */
244 /************************************************************************/
245
GenBinBitRasterBand(GenBinDataset * poDSIn,int nBitsIn)246 GenBinBitRasterBand::GenBinBitRasterBand( GenBinDataset *poDSIn, int nBitsIn ) :
247 nBits(nBitsIn)
248 {
249 SetMetadataItem( "NBITS",
250 CPLString().Printf("%d",nBitsIn),
251 "IMAGE_STRUCTURE" );
252
253 poDS = poDSIn;
254 nBand = 1;
255
256 eDataType = GDT_Byte;
257
258 nBlockXSize = poDSIn->nRasterXSize;
259 nBlockYSize = 1;
260 }
261
262 /************************************************************************/
263 /* IReadBlock() */
264 /************************************************************************/
265
IReadBlock(int,int nBlockYOff,void * pImage)266 CPLErr GenBinBitRasterBand::IReadBlock( int /* nBlockXOff */,
267 int nBlockYOff,
268 void * pImage )
269
270 {
271 GenBinDataset *poGDS = reinterpret_cast<GenBinDataset *>( poDS );
272
273 /* -------------------------------------------------------------------- */
274 /* Establish desired position. */
275 /* -------------------------------------------------------------------- */
276 const vsi_l_offset nLineStart
277 = (static_cast<vsi_l_offset>( nBlockXSize ) * nBlockYOff * nBits) / 8;
278 int iBitOffset = static_cast<int>(
279 ( static_cast<vsi_l_offset>( nBlockXSize ) * nBlockYOff * nBits) % 8 );
280 const unsigned int nLineBytes = static_cast<unsigned int>(
281 ( static_cast<vsi_l_offset>( nBlockXSize ) * (nBlockYOff+1) * nBits + 7)
282 / 8 - nLineStart);
283
284 /* -------------------------------------------------------------------- */
285 /* Read data into buffer. */
286 /* -------------------------------------------------------------------- */
287 GByte *pabyBuffer = static_cast<GByte *>( CPLCalloc( nLineBytes, 1 ) );
288
289 if( VSIFSeekL( poGDS->fpImage, nLineStart, SEEK_SET ) != 0
290 || VSIFReadL( pabyBuffer, 1, nLineBytes, poGDS->fpImage) != nLineBytes )
291 {
292 CPLError( CE_Failure, CPLE_FileIO,
293 "Failed to read %u bytes at offset %lu.\n%s",
294 nLineBytes, static_cast<unsigned long>(nLineStart),
295 VSIStrerror( errno ) );
296 CPLFree( pabyBuffer );
297 return CE_Failure;
298 }
299
300 /* -------------------------------------------------------------------- */
301 /* Copy data, promoting to 8bit. */
302 /* -------------------------------------------------------------------- */
303 GByte *pafImage = reinterpret_cast<GByte *>( pImage );
304 if( nBits == 1 )
305 {
306 for( int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits )
307 {
308 if( pabyBuffer[iBitOffset>>3] & (0x80 >>(iBitOffset & 7)) )
309 pafImage[iX] = 1;
310 else
311 pafImage[iX] = 0;
312 }
313 }
314 else if( nBits == 2 )
315 {
316 for( int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits )
317 {
318 pafImage[iX] =
319 (pabyBuffer[iBitOffset>>3]) >> (6-(iBitOffset&0x7)) & 0x3;
320 }
321 }
322 else if( nBits == 4 )
323 {
324 for( int iX = 0; iX < nBlockXSize; iX++, iBitOffset += nBits )
325 {
326 if( iBitOffset == 0 )
327 pafImage[iX] = (pabyBuffer[iBitOffset>>3]) >> 4;
328 else
329 pafImage[iX] = (pabyBuffer[iBitOffset>>3]) & 0xf;
330 }
331 }
332 else {
333 CPLAssert( false );
334 }
335
336 CPLFree( pabyBuffer );
337
338 return CE_None;
339 }
340
341 /************************************************************************/
342 /* ==================================================================== */
343 /* GenBinDataset */
344 /* ==================================================================== */
345 /************************************************************************/
346
347 /************************************************************************/
348 /* GenBinDataset() */
349 /************************************************************************/
350
GenBinDataset()351 GenBinDataset::GenBinDataset() :
352 fpImage(nullptr),
353 bGotTransform(false),
354 pszProjection(CPLStrdup("")),
355 papszHDR(nullptr)
356 {
357 adfGeoTransform[0] = 0.0;
358 adfGeoTransform[1] = 1.0;
359 adfGeoTransform[2] = 0.0;
360 adfGeoTransform[3] = 0.0;
361 adfGeoTransform[4] = 0.0;
362 adfGeoTransform[5] = 1.0;
363 }
364
365 /************************************************************************/
366 /* ~GenBinDataset() */
367 /************************************************************************/
368
~GenBinDataset()369 GenBinDataset::~GenBinDataset()
370
371 {
372 FlushCache();
373
374 if( fpImage != nullptr )
375 CPL_IGNORE_RET_VAL(VSIFCloseL( fpImage ));
376
377 CPLFree( pszProjection );
378 CSLDestroy( papszHDR );
379 }
380
381 /************************************************************************/
382 /* GetProjectionRef() */
383 /************************************************************************/
384
_GetProjectionRef()385 const char *GenBinDataset::_GetProjectionRef()
386
387 {
388 if (pszProjection && strlen(pszProjection) > 0)
389 return pszProjection;
390
391 return GDALPamDataset::_GetProjectionRef();
392 }
393
394 /************************************************************************/
395 /* GetGeoTransform() */
396 /************************************************************************/
397
GetGeoTransform(double * padfTransform)398 CPLErr GenBinDataset::GetGeoTransform( double * padfTransform )
399
400 {
401 if( bGotTransform )
402 {
403 memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 );
404 return CE_None;
405 }
406
407 return GDALPamDataset::GetGeoTransform( padfTransform );
408 }
409
410 /************************************************************************/
411 /* GetFileList() */
412 /************************************************************************/
413
GetFileList()414 char **GenBinDataset::GetFileList()
415
416 {
417 const CPLString osPath = CPLGetPath( GetDescription() );
418 const CPLString osName = CPLGetBasename( GetDescription() );
419
420 // Main data file, etc.
421 char **papszFileList = GDALPamDataset::GetFileList();
422
423 // Header file.
424 const CPLString osFilename = CPLFormCIFilename( osPath, osName, "hdr" );
425 papszFileList = CSLAddString( papszFileList, osFilename );
426
427 return papszFileList;
428 }
429
430 /************************************************************************/
431 /* ParseCoordinateSystem() */
432 /************************************************************************/
433
ParseCoordinateSystem(char ** papszHdr)434 void GenBinDataset::ParseCoordinateSystem( char **papszHdr )
435
436 {
437 const char *pszProjName = CSLFetchNameValue( papszHdr, "PROJECTION_NAME" );
438 if( pszProjName == nullptr )
439 return;
440
441 /* -------------------------------------------------------------------- */
442 /* Translate zone and parameters into numeric form. */
443 /* -------------------------------------------------------------------- */
444 int nZone = 0;
445 if( CSLFetchNameValue( papszHdr, "PROJECTION_ZONE" ) )
446 nZone = atoi(CSLFetchNameValue( papszHdr, "PROJECTION_ZONE" ));
447
448 #if 0
449 // TODO(schwehr): Why was this being done but not used?
450 double adfProjParams[15] = { 0.0 };
451 if( CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) )
452 {
453 char **papszTokens = CSLTokenizeString(
454 CSLFetchNameValue( papszHdr, "PROJECTION_PARAMETERS" ) );
455
456 for( int i = 0; i < 15 && papszTokens[i] != NULL; i++ )
457 adfProjParams[i] = CPLAtofM( papszTokens[i] );
458
459 CSLDestroy( papszTokens );
460 }
461 #endif
462
463 /* -------------------------------------------------------------------- */
464 /* Handle projections. */
465 /* -------------------------------------------------------------------- */
466 const char *pszDatumName = CSLFetchNameValue( papszHdr, "DATUM_NAME" );
467 OGRSpatialReference oSRS;
468
469 if( EQUAL(pszProjName,"UTM") && nZone != 0 )
470 {
471 // Just getting that the negative zone for southern hemisphere is used.
472 oSRS.SetUTM( std::abs(nZone), nZone > 0 );
473 }
474
475 else if( EQUAL(pszProjName,"State Plane") && nZone != 0 )
476 {
477 const int nPairs = sizeof(anUsgsEsriZones) / (2 * sizeof(int));
478
479 for( int i = 0; i < nPairs; i++ )
480 {
481 if( anUsgsEsriZones[i*2+1] == nZone )
482 {
483 nZone = anUsgsEsriZones[i*2];
484 break;
485 }
486 }
487
488 const char *pszUnits = CSLFetchNameValueDef( papszHdr, "MAP_UNITS", "" );
489 double dfUnits = 0.0;
490 if( EQUAL(pszUnits,"feet") )
491 dfUnits = CPLAtofM(SRS_UL_US_FOOT_CONV);
492 else if( STARTS_WITH_CI(pszUnits, "MET") )
493 dfUnits = 1.0;
494 else
495 pszUnits = nullptr;
496
497 oSRS.SetStatePlane( std::abs(nZone),
498 pszDatumName==nullptr || !EQUAL(pszDatumName,"NAD27"),
499 pszUnits, dfUnits );
500 }
501
502 /* -------------------------------------------------------------------- */
503 /* Setup the geographic coordinate system. */
504 /* -------------------------------------------------------------------- */
505 if( oSRS.GetAttrNode( "GEOGCS" ) == nullptr )
506 {
507 const char* pszSpheroidName =
508 CSLFetchNameValue( papszHdr, "SPHEROID_NAME" );
509 const char* pszSemiMajor =
510 CSLFetchNameValue( papszHdr, "SEMI_MAJOR_AXIS");
511 const char* pszSemiMinor =
512 CSLFetchNameValue( papszHdr, "SEMI_MINOR_AXIS");
513 if( pszDatumName != nullptr
514 && oSRS.SetWellKnownGeogCS( pszDatumName ) == OGRERR_NONE )
515 {
516 // good
517 }
518 else if( pszSpheroidName && pszSemiMajor && pszSemiMinor )
519 {
520 const double dfSemiMajor = CPLAtofM(pszSemiMajor);
521 const double dfSemiMinor = CPLAtofM(pszSemiMinor);
522
523 oSRS.SetGeogCS( pszSpheroidName,
524 pszSpheroidName,
525 pszSpheroidName,
526 dfSemiMajor,
527 (dfSemiMajor == 0.0 ||
528 dfSemiMajor == dfSemiMinor) ? 0.0 :
529 1.0 / (1.0 - dfSemiMinor/dfSemiMajor) );
530 }
531 else // fallback default.
532 oSRS.SetWellKnownGeogCS( "WGS84" );
533 }
534
535 /* -------------------------------------------------------------------- */
536 /* Convert to WKT. */
537 /* -------------------------------------------------------------------- */
538 CPLFree( pszProjection );
539 pszProjection = nullptr;
540
541 oSRS.exportToWkt( &pszProjection );
542 }
543
544 /************************************************************************/
545 /* Open() */
546 /************************************************************************/
547
Open(GDALOpenInfo * poOpenInfo)548 GDALDataset *GenBinDataset::Open( GDALOpenInfo * poOpenInfo )
549
550 {
551 /* -------------------------------------------------------------------- */
552 /* We assume the user is pointing to the binary (i.e. .bil) file. */
553 /* -------------------------------------------------------------------- */
554 if( poOpenInfo->nHeaderBytes < 2 || poOpenInfo->fpL == nullptr )
555 return nullptr;
556
557 /* -------------------------------------------------------------------- */
558 /* Now we need to tear apart the filename to form a .HDR */
559 /* filename. */
560 /* -------------------------------------------------------------------- */
561 const CPLString osPath = CPLGetPath( poOpenInfo->pszFilename );
562 const CPLString osName = CPLGetBasename( poOpenInfo->pszFilename );
563 CPLString osHDRFilename;
564
565 char** papszSiblingFiles = poOpenInfo->GetSiblingFiles();
566 if( papszSiblingFiles )
567 {
568 const int iFile = CSLFindString(
569 papszSiblingFiles, CPLFormFilename( nullptr, osName, "hdr" ) );
570 if( iFile < 0 ) // return if there is no corresponding .hdr file
571 return nullptr;
572
573 osHDRFilename =
574 CPLFormFilename( osPath, papszSiblingFiles[iFile], nullptr );
575 }
576 else
577 {
578 osHDRFilename = CPLFormCIFilename( osPath, osName, "hdr" );
579 }
580
581 const bool bSelectedHDR = EQUAL( osHDRFilename, poOpenInfo->pszFilename );
582
583 /* -------------------------------------------------------------------- */
584 /* Do we have a .hdr file? */
585 /* -------------------------------------------------------------------- */
586 VSILFILE *fp = VSIFOpenL( osHDRFilename, "r" );
587 if( fp == nullptr )
588 {
589 return nullptr;
590 }
591
592 /* -------------------------------------------------------------------- */
593 /* Read a chunk to skim for expected keywords. */
594 /* -------------------------------------------------------------------- */
595 char achHeader[1000] = { '\0' };
596
597 const int nRead =
598 static_cast<int>(VSIFReadL( achHeader, 1, sizeof(achHeader) - 1, fp ));
599 achHeader[nRead] = '\0';
600 CPL_IGNORE_RET_VAL(VSIFSeekL( fp, 0, SEEK_SET ));
601
602 if( strstr( achHeader, "BANDS:" ) == nullptr
603 || strstr( achHeader, "ROWS:" ) == nullptr
604 || strstr( achHeader, "COLS:" ) == nullptr )
605 {
606 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
607 return nullptr;
608 }
609
610 /* -------------------------------------------------------------------- */
611 /* Has the user selected the .hdr file to open? */
612 /* -------------------------------------------------------------------- */
613 if( bSelectedHDR )
614 {
615 CPLError(
616 CE_Failure, CPLE_AppDefined,
617 "The selected file is an Generic Binary header file, but to "
618 "open Generic Binary datasets, the data file should be selected "
619 "instead of the .hdr file. Please try again selecting"
620 "the raw data file corresponding to the header file: %s",
621 poOpenInfo->pszFilename );
622 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
623 return nullptr;
624 }
625
626 /* -------------------------------------------------------------------- */
627 /* Read the .hdr file. */
628 /* -------------------------------------------------------------------- */
629 char **papszHdr = nullptr;
630 const char *pszLine = CPLReadLineL( fp );
631
632 while( pszLine != nullptr )
633 {
634 if( EQUAL(pszLine,"PROJECTION_PARAMETERS:") )
635 {
636 CPLString osPP = pszLine;
637
638 pszLine = CPLReadLineL(fp);
639 while( pszLine != nullptr
640 && (*pszLine == '\t' || *pszLine == ' ') )
641 {
642 osPP += pszLine;
643 pszLine = CPLReadLineL(fp);
644 }
645 papszHdr = CSLAddString( papszHdr, osPP );
646 }
647 else
648 {
649 char *pszName = nullptr;
650 const char* pszKey = CPLParseNameValue( pszLine, &pszName );
651 if( pszKey && pszName )
652 {
653 CPLString osValue = pszKey;
654 osValue.Trim();
655
656 papszHdr = CSLSetNameValue( papszHdr, pszName, osValue );
657 }
658 CPLFree( pszName );
659
660 pszLine = CPLReadLineL( fp );
661 }
662 }
663
664 CPL_IGNORE_RET_VAL(VSIFCloseL( fp ));
665
666 if( CSLFetchNameValue( papszHdr, "COLS" ) == nullptr
667 || CSLFetchNameValue( papszHdr, "ROWS" ) == nullptr
668 || CSLFetchNameValue( papszHdr, "BANDS" ) == nullptr )
669 {
670 CSLDestroy( papszHdr );
671 return nullptr;
672 }
673
674 /* -------------------------------------------------------------------- */
675 /* Create a corresponding GDALDataset. */
676 /* -------------------------------------------------------------------- */
677 GenBinDataset *poDS = new GenBinDataset();
678
679 /* -------------------------------------------------------------------- */
680 /* Capture some information from the file that is of interest. */
681 /* -------------------------------------------------------------------- */
682 const int nBands = atoi(CSLFetchNameValue( papszHdr, "BANDS" ));
683
684 poDS->nRasterXSize = atoi(CSLFetchNameValue( papszHdr, "COLS" ));
685 poDS->nRasterYSize = atoi(CSLFetchNameValue( papszHdr, "ROWS" ));
686 poDS->papszHDR = papszHdr;
687
688 if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
689 !GDALCheckBandCount(nBands, FALSE))
690 {
691 delete poDS;
692 return nullptr;
693 }
694
695 poDS->fpImage = poOpenInfo->fpL;
696 poOpenInfo->fpL = nullptr;
697 poDS->eAccess = poOpenInfo->eAccess;
698
699 /* -------------------------------------------------------------------- */
700 /* Figure out the data type. */
701 /* -------------------------------------------------------------------- */
702 const char *pszDataType = CSLFetchNameValue( papszHdr, "DATATYPE" );
703 GDALDataType eDataType = GDT_Unknown;
704 int nBits = -1; // Only needed for partial byte types
705
706 if( pszDataType == nullptr )
707 eDataType = GDT_Byte;
708 else if( EQUAL(pszDataType,"U16") )
709 eDataType = GDT_UInt16;
710 else if( EQUAL(pszDataType,"S16") )
711 eDataType = GDT_Int16;
712 else if( EQUAL(pszDataType,"F32") )
713 eDataType = GDT_Float32;
714 else if( EQUAL(pszDataType,"F64") )
715 eDataType = GDT_Float64;
716 else if( EQUAL(pszDataType,"U8") )
717 eDataType = GDT_Byte;
718 else if( EQUAL(pszDataType,"U1")
719 || EQUAL(pszDataType,"U2")
720 || EQUAL(pszDataType,"U4") )
721 {
722 nBits = atoi(pszDataType+1);
723 eDataType = GDT_Byte;
724 if( nBands != 1 )
725 {
726 CPLError( CE_Failure, CPLE_OpenFailed,
727 "Only one band is supported for U1/U2/U4 data type" );
728 delete poDS;
729 return nullptr;
730 }
731 }
732 else
733 {
734 eDataType = GDT_Byte;
735 CPLError( CE_Warning, CPLE_AppDefined,
736 "DATATYPE=%s not recognised, assuming Byte.",
737 pszDataType );
738 }
739
740 /* -------------------------------------------------------------------- */
741 /* Do we need byte swapping? */
742 /* -------------------------------------------------------------------- */
743 const char *pszBYTE_ORDER = CSLFetchNameValue(papszHdr,"BYTE_ORDER");
744 bool bNative = true;
745
746 if( pszBYTE_ORDER != nullptr )
747 {
748 #ifdef CPL_LSB
749 bNative = STARTS_WITH_CI(pszBYTE_ORDER, "LSB");
750 #else
751 bNative = !STARTS_WITH_CI(pszBYTE_ORDER, "LSB");
752 #endif
753 }
754
755 /* -------------------------------------------------------------------- */
756 /* Work out interleaving info. */
757 /* -------------------------------------------------------------------- */
758 const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
759 int nPixelOffset = 0;
760 int nLineOffset = 0;
761 vsi_l_offset nBandOffset = 0;
762 bool bIntOverflow = false;
763
764 const char *pszInterleaving = CSLFetchNameValue(papszHdr, "INTERLEAVING");
765 if( pszInterleaving == nullptr )
766 pszInterleaving = "BIL";
767
768 if( EQUAL(pszInterleaving,"BSQ") || EQUAL(pszInterleaving,"NA") )
769 {
770 nPixelOffset = nItemSize;
771 if( poDS->nRasterXSize > INT_MAX / nItemSize )
772 bIntOverflow = true;
773 else
774 {
775 nLineOffset = nItemSize * poDS->nRasterXSize;
776 nBandOffset =
777 nLineOffset * static_cast<vsi_l_offset>(poDS->nRasterYSize);
778 }
779 }
780 else if( EQUAL(pszInterleaving,"BIP") )
781 {
782 nPixelOffset = nItemSize * nBands;
783 if( nPixelOffset == 0 || poDS->nRasterXSize > INT_MAX / nPixelOffset )
784 bIntOverflow = true;
785 else
786 {
787 nLineOffset = nPixelOffset * poDS->nRasterXSize;
788 nBandOffset = nItemSize;
789 }
790 }
791 else
792 {
793 if( !EQUAL(pszInterleaving,"BIL") )
794 CPLError( CE_Warning, CPLE_AppDefined,
795 "INTERLEAVING:%s not recognised, assume BIL.",
796 pszInterleaving );
797
798 nPixelOffset = nItemSize;
799 if( nPixelOffset == 0 || nBands == 0 ||
800 poDS->nRasterXSize > INT_MAX / (nPixelOffset * nBands) )
801 bIntOverflow = true;
802 else
803 {
804 nLineOffset = nPixelOffset * nBands * poDS->nRasterXSize;
805 nBandOffset = nItemSize * static_cast<vsi_l_offset>(poDS->nRasterXSize);
806 }
807 }
808
809 if (bIntOverflow)
810 {
811 delete poDS;
812 CPLError( CE_Failure, CPLE_AppDefined,
813 "Int overflow occurred." );
814 return nullptr;
815 }
816
817 if( nBits < 0 &&
818 !RAWDatasetCheckMemoryUsage(
819 poDS->nRasterXSize, poDS->nRasterYSize, nBands,
820 nItemSize,
821 nPixelOffset, nLineOffset, 0, nBandOffset,
822 poDS->fpImage) )
823 {
824 delete poDS;
825 return nullptr;
826 }
827
828 poDS->SetDescription( poOpenInfo->pszFilename );
829 poDS->PamInitialize();
830
831 /* -------------------------------------------------------------------- */
832 /* Create band information objects. */
833 /* -------------------------------------------------------------------- */
834 poDS->nBands = nBands;
835 for( int i = 0; i < poDS->nBands; i++ )
836 {
837 if( nBits != -1 )
838 {
839 poDS->SetBand( i+1, new GenBinBitRasterBand( poDS, nBits ) );
840 }
841 else
842 {
843 poDS->SetBand(
844 i+1,
845 new RawRasterBand( poDS, i+1, poDS->fpImage,
846 nBandOffset * i, nPixelOffset, nLineOffset,
847 eDataType, bNative,
848 RawRasterBand::OwnFP::NO ) );
849 }
850 }
851
852 /* -------------------------------------------------------------------- */
853 /* Get geotransform. */
854 /* -------------------------------------------------------------------- */
855 if( poDS->nRasterXSize > 1 &&
856 poDS->nRasterYSize > 1 &&
857 CSLFetchNameValue(papszHdr,"UL_X_COORDINATE") != nullptr
858 && CSLFetchNameValue(papszHdr,"UL_Y_COORDINATE") != nullptr
859 && CSLFetchNameValue(papszHdr,"LR_X_COORDINATE") != nullptr
860 && CSLFetchNameValue(papszHdr,"LR_Y_COORDINATE") != nullptr )
861 {
862 const double dfULX =
863 CPLAtofM(CSLFetchNameValue(papszHdr,"UL_X_COORDINATE"));
864 const double dfULY =
865 CPLAtofM(CSLFetchNameValue(papszHdr,"UL_Y_COORDINATE"));
866 const double dfLRX =
867 CPLAtofM(CSLFetchNameValue(papszHdr,"LR_X_COORDINATE"));
868 const double dfLRY =
869 CPLAtofM(CSLFetchNameValue(papszHdr,"LR_Y_COORDINATE"));
870
871 poDS->adfGeoTransform[1] = (dfLRX - dfULX) / (poDS->nRasterXSize-1);
872 poDS->adfGeoTransform[2] = 0.0;
873 poDS->adfGeoTransform[4] = 0.0;
874 poDS->adfGeoTransform[5] = (dfLRY - dfULY) / (poDS->nRasterYSize-1);
875
876 poDS->adfGeoTransform[0] = dfULX - poDS->adfGeoTransform[1] * 0.5;
877 poDS->adfGeoTransform[3] = dfULY - poDS->adfGeoTransform[5] * 0.5;
878
879 poDS->bGotTransform = true;
880 }
881
882 /* -------------------------------------------------------------------- */
883 /* Try and parse the coordinate system. */
884 /* -------------------------------------------------------------------- */
885 poDS->ParseCoordinateSystem( papszHdr );
886
887 /* -------------------------------------------------------------------- */
888 /* Initialize any PAM information. */
889 /* -------------------------------------------------------------------- */
890 poDS->TryLoadXML();
891
892 /* -------------------------------------------------------------------- */
893 /* Check for overviews. */
894 /* -------------------------------------------------------------------- */
895 poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
896
897 return poDS;
898 }
899
900 /************************************************************************/
901 /* GDALRegister_GenBin() */
902 /************************************************************************/
903
GDALRegister_GenBin()904 void GDALRegister_GenBin()
905
906 {
907 if( GDALGetDriverByName( "GenBin" ) != nullptr )
908 return;
909
910 GDALDriver *poDriver = new GDALDriver();
911
912 poDriver->SetDescription( "GenBin" );
913 poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
914 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
915 "Generic Binary (.hdr Labelled)" );
916 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
917 "drivers/raster/genbin.html" );
918 poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
919
920 poDriver->pfnOpen = GenBinDataset::Open;
921
922 GetGDALDriverManager()->RegisterDriver( poDriver );
923 }
924