EVOLUTION-MANAGER
Edit File: ehdrdataset.cpp
/****************************************************************************** * * Project: ESRI .hdr Driver * Purpose: Implementation of EHdrDataset * Author: Frank Warmerdam, warmerdam@pobox.com * ****************************************************************************** * Copyright (c) 1999, Frank Warmerdam <warmerdam@pobox.com> * Copyright (c) 2007-2013, Even Rouault <even dot rouault at mines-paris dot org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "cpl_port.h" #include "ehdrdataset.h" #include "rawdataset.h" #include <cctype> #include <cerrno> #include <climits> #include <cmath> #include <cstddef> #include <cstdio> #include <cstdlib> #include <cstring> #if HAVE_FCNTL_H # include <fcntl.h> #endif #include <limits> #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_progress.h" #include "cpl_string.h" #include "cpl_vsi.h" #include "gdal.h" #include "gdal_frmts.h" #include "gdal_pam.h" #include "gdal_priv.h" #include "ogr_core.h" #include "ogr_spatialref.h" CPL_CVSID("$Id: ehdrdataset.cpp 37531 2017-03-01 04:56:18Z goatbar $"); static const int HAS_MIN_FLAG = 0x1; static const int HAS_MAX_FLAG = 0x2; static const int HAS_MEAN_FLAG = 0x4; static const int HAS_STDDEV_FLAG = 0x8; static const int HAS_ALL_FLAGS = HAS_MIN_FLAG | HAS_MAX_FLAG | HAS_MEAN_FLAG | HAS_STDDEV_FLAG; /************************************************************************/ /* EHdrRasterBand() */ /************************************************************************/ EHdrRasterBand::EHdrRasterBand( GDALDataset *poDSIn, int nBandIn, VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn, int nPixelOffsetIn, int nLineOffsetIn, GDALDataType eDataTypeIn, int bNativeOrderIn, int nBitsIn) : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn, nLineOffsetIn, eDataTypeIn, bNativeOrderIn, TRUE), nBits(nBitsIn), nStartBit(0), nPixelOffsetBits(0), nLineOffsetBits(0), bNoDataSet(FALSE), dfNoData(0.0), dfMin(0.0), dfMax(0.0), dfMean(0.0), dfStdDev(0.0), minmaxmeanstddev(0) { EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS); if (nBits < 8) { int nSkipBytes = atoi(poEDS->GetKeyValue("SKIPBYTES")); if( nSkipBytes < 0 || nSkipBytes > std::numeric_limits<int>::max() / 8 ) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid SKIPBYTES: %d", nSkipBytes); nStartBit = 0; } else { nStartBit = nSkipBytes * 8; } if (nBand >= 2) { GIntBig nBandRowBytes = CPLAtoGIntBig(poEDS->GetKeyValue("BANDROWBYTES")); if( nBandRowBytes < 0 ) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid BANDROWBYTES: " CPL_FRMT_GIB, nBandRowBytes); nBandRowBytes = 0; } vsi_l_offset nRowBytes = 0; if (nBandRowBytes == 0) nRowBytes = (static_cast<vsi_l_offset>(nBits) * poDS->GetRasterXSize() + 7) / 8; else nRowBytes = static_cast<vsi_l_offset>(nBandRowBytes); nStartBit += nRowBytes * (nBand - 1) * 8; } nPixelOffsetBits = nBits; GIntBig nTotalRowBytes = CPLAtoGIntBig(poEDS->GetKeyValue("TOTALROWBYTES")); if( nTotalRowBytes < 0 ) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid TOTALROWBYTES: " CPL_FRMT_GIB, nTotalRowBytes); nTotalRowBytes = 0; } if( nTotalRowBytes > 0 ) nLineOffsetBits = static_cast<vsi_l_offset>(nTotalRowBytes * 8); else nLineOffsetBits = static_cast<vsi_l_offset>(nPixelOffsetBits) * poDS->GetRasterXSize(); nBlockXSize = poDS->GetRasterXSize(); nBlockYSize = 1; SetMetadataItem("NBITS", CPLString().Printf("%d", nBits), "IMAGE_STRUCTURE"); } if( eDataType == GDT_Byte && EQUAL(poEDS->GetKeyValue("PIXELTYPE", ""), "SIGNEDINT") ) SetMetadataItem("PIXELTYPE", "SIGNEDBYTE", "IMAGE_STRUCTURE"); } /************************************************************************/ /* IReadBlock() */ /************************************************************************/ CPLErr EHdrRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff, void * pImage ) { if (nBits >= 8) return RawRasterBand::IReadBlock(nBlockXOff, nBlockYOff, pImage); // Establish desired position. const vsi_l_offset nLineBytesBig = (static_cast<vsi_l_offset>(nPixelOffsetBits) * nBlockXSize + 7) / 8; if( nLineBytesBig > static_cast<vsi_l_offset>(std::numeric_limits<int>::max()) ) return CE_Failure; const unsigned int nLineBytes = static_cast<unsigned int>(nLineBytesBig); const vsi_l_offset nLineStart = (nStartBit + nLineOffsetBits * nBlockYOff) / 8; int iBitOffset = static_cast<int>((nStartBit + nLineOffsetBits * nBlockYOff) % 8); // Read data into buffer. GByte *pabyBuffer = static_cast<GByte *>(VSI_MALLOC_VERBOSE(nLineBytes)); if( pabyBuffer == NULL ) return CE_Failure; if( VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0 || VSIFReadL(pabyBuffer, 1, nLineBytes, GetFPL()) != nLineBytes ) { CPLError(CE_Failure, CPLE_FileIO, "Failed to read %u bytes at offset %lu.\n%s", nLineBytes, static_cast<unsigned long>(nLineStart), VSIStrerror(errno)); CPLFree(pabyBuffer); return CE_Failure; } // Copy data, promoting to 8bit. for( int iX = 0, iPixel = 0; iX < nBlockXSize; iX++ ) { int nOutWord = 0; for( int iBit = 0; iBit < nBits; iBit++ ) { if( pabyBuffer[iBitOffset >> 3] & (0x80 >>(iBitOffset & 7)) ) nOutWord |= (1 << (nBits - 1 - iBit)); iBitOffset++; } iBitOffset = iBitOffset + nPixelOffsetBits - nBits; reinterpret_cast<GByte *>(pImage)[iPixel++] = static_cast<GByte>(nOutWord); } CPLFree(pabyBuffer); return CE_None; } /************************************************************************/ /* IWriteBlock() */ /************************************************************************/ CPLErr EHdrRasterBand::IWriteBlock( int nBlockXOff, int nBlockYOff, void * pImage ) { if (nBits >= 8) return RawRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage); // Establish desired position. const vsi_l_offset nLineBytesBig = (static_cast<vsi_l_offset>(nPixelOffsetBits) * nBlockXSize + 7) / 8; if( nLineBytesBig > static_cast<vsi_l_offset>(std::numeric_limits<int>::max()) ) return CE_Failure; const unsigned int nLineBytes = static_cast<unsigned int>(nLineBytesBig); const vsi_l_offset nLineStart = (nStartBit + nLineOffsetBits * nBlockYOff) / 8; int iBitOffset = static_cast<int>((nStartBit + nLineOffsetBits * nBlockYOff) % 8); // Read data into buffer. GByte *pabyBuffer = static_cast<GByte *>(VSI_CALLOC_VERBOSE(nLineBytes, 1)); if( pabyBuffer == NULL ) return CE_Failure; if( VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0 ) { CPLError(CE_Failure, CPLE_FileIO, "Failed to read %u bytes at offset %lu.\n%s", nLineBytes, static_cast<unsigned long>(nLineStart), VSIStrerror(errno)); CPLFree(pabyBuffer); return CE_Failure; } CPL_IGNORE_RET_VAL(VSIFReadL(pabyBuffer, nLineBytes, 1, GetFPL())); // Copy data, promoting to 8bit. for( int iX = 0, iPixel = 0; iX < nBlockXSize; iX++ ) { const int nOutWord = reinterpret_cast<GByte *>(pImage)[iPixel++]; for( int iBit = 0; iBit < nBits; iBit++ ) { if( nOutWord & (1 << (nBits - 1 - iBit)) ) pabyBuffer[iBitOffset >> 3] |= (0x80 >> (iBitOffset & 7)); else pabyBuffer[iBitOffset >> 3] &= ~((0x80 >> (iBitOffset & 7))); iBitOffset++; } iBitOffset = iBitOffset + nPixelOffsetBits - nBits; } // Write the data back out. if( VSIFSeekL(GetFPL(), nLineStart, SEEK_SET) != 0 || VSIFWriteL(pabyBuffer, 1, nLineBytes, GetFPL()) != nLineBytes ) { CPLError(CE_Failure, CPLE_FileIO, "Failed to write %u bytes at offset %lu.\n%s", nLineBytes, static_cast<unsigned long>(nLineStart), VSIStrerror(errno)); return CE_Failure; } CPLFree(pabyBuffer); return CE_None; } /************************************************************************/ /* IRasterIO() */ /************************************************************************/ CPLErr EHdrRasterBand::IRasterIO( GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg* psExtraArg ) { // Defer to RawRasterBand if (nBits >= 8) return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg); // Force use of IReadBlock() and IWriteBlock() return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace, psExtraArg); } /************************************************************************/ /* OSR_GDS() */ /************************************************************************/ static const char*OSR_GDS( char* pszResult, int nResultLen, char **papszNV, const char * pszField, const char *pszDefaultValue ) { if( papszNV == NULL || papszNV[0] == NULL ) return pszDefaultValue; int iLine = 0; // Used after for. for( ; papszNV[iLine] != NULL && !EQUALN(papszNV[iLine], pszField, strlen(pszField)); iLine++ ) {} if( papszNV[iLine] == NULL ) return pszDefaultValue; char **papszTokens = CSLTokenizeString(papszNV[iLine]); if( CSLCount(papszTokens) > 1 ) strncpy(pszResult, papszTokens[1], nResultLen); else strncpy(pszResult, pszDefaultValue, nResultLen); pszResult[nResultLen - 1] = '\0'; CSLDestroy(papszTokens); return pszResult; } /************************************************************************/ /* ==================================================================== */ /* EHdrDataset */ /* ==================================================================== */ /************************************************************************/ /************************************************************************/ /* EHdrDataset() */ /************************************************************************/ EHdrDataset::EHdrDataset() : fpImage(NULL), osHeaderExt("hdr"), bGotTransform(false), pszProjection(CPLStrdup("")), bHDRDirty(false), papszHDR(NULL), bCLRDirty(false) { adfGeoTransform[0] = 0.0; adfGeoTransform[1] = 1.0; adfGeoTransform[2] = 0.0; adfGeoTransform[3] = 0.0; adfGeoTransform[4] = 0.0; adfGeoTransform[5] = 1.0; } /************************************************************************/ /* ~EHdrDataset() */ /************************************************************************/ EHdrDataset::~EHdrDataset() { FlushCache(); if( nBands > 0 && GetAccess() == GA_Update ) { int bNoDataSet; RawRasterBand *poBand = reinterpret_cast<RawRasterBand *>(GetRasterBand(1)); const double dfNoData = poBand->GetNoDataValue(&bNoDataSet); if( bNoDataSet ) { ResetKeyValue("NODATA", CPLString().Printf("%.8g", dfNoData)); } if( bCLRDirty ) RewriteColorTable(poBand->GetColorTable()); if( bHDRDirty ) RewriteHDR(); } if( fpImage != NULL ) { if( VSIFCloseL(fpImage) != 0 ) { CPLError(CE_Failure, CPLE_FileIO, "I/O error"); } } CPLFree(pszProjection); CSLDestroy(papszHDR); } /************************************************************************/ /* GetKeyValue() */ /************************************************************************/ const char *EHdrDataset::GetKeyValue( const char *pszKey, const char *pszDefault ) { for( int i = 0; papszHDR[i] != NULL; i++ ) { if( EQUALN(pszKey,papszHDR[i],strlen(pszKey)) && isspace((unsigned char)papszHDR[i][strlen(pszKey)]) ) { const char *pszValue = papszHDR[i] + strlen(pszKey); while( isspace((unsigned char)*pszValue) ) pszValue++; return pszValue; } } return pszDefault; } /************************************************************************/ /* ResetKeyValue() */ /* */ /* Replace or add the keyword with the indicated value in the */ /* papszHDR list. */ /************************************************************************/ void EHdrDataset::ResetKeyValue( const char *pszKey, const char *pszValue ) { if( strlen(pszValue) > 65 ) { CPLAssert(strlen(pszValue) <= 65); return; } char szNewLine[82] = { '\0' }; snprintf(szNewLine, sizeof(szNewLine), "%-15s%s", pszKey, pszValue); for( int i = CSLCount(papszHDR)-1; i >= 0; i-- ) { if( EQUALN(papszHDR[i], szNewLine, strlen(pszKey) + 1) ) { if( strcmp(papszHDR[i],szNewLine) != 0 ) { CPLFree(papszHDR[i]); papszHDR[i] = CPLStrdup(szNewLine); bHDRDirty = true; } return; } } bHDRDirty = true; papszHDR = CSLAddString(papszHDR, szNewLine); } /************************************************************************/ /* RewriteColorTable() */ /************************************************************************/ void EHdrDataset::RewriteColorTable( GDALColorTable *poTable ) { CPLString osCLRFilename = CPLResetExtension(GetDescription(), "clr"); if( poTable ) { VSILFILE *fp = VSIFOpenL(osCLRFilename, "wt"); if( fp != NULL ) { for( int iColor = 0; iColor < poTable->GetColorEntryCount(); iColor++ ) { GDALColorEntry sEntry; poTable->GetColorEntryAsRGB(iColor, &sEntry); // I wish we had a way to mark transparency. CPLString oLine; oLine.Printf("%3d %3d %3d %3d\n", iColor, sEntry.c1, sEntry.c2, sEntry.c3); if( VSIFWriteL( reinterpret_cast<void *>( const_cast<char *>( oLine.c_str() ) ), strlen(oLine), 1, fp ) != 1 ) { CPLError(CE_Failure, CPLE_FileIO, "Error while write color table"); CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); return; } } if( VSIFCloseL(fp) != 0 ) { CPLError(CE_Failure, CPLE_FileIO, "Error while write color table"); } } else { CPLError(CE_Failure, CPLE_OpenFailed, "Unable to create color file %s.", osCLRFilename.c_str()); } } else { VSIUnlink(osCLRFilename); } } /************************************************************************/ /* GetProjectionRef() */ /************************************************************************/ const char *EHdrDataset::GetProjectionRef() { if (pszProjection && strlen(pszProjection) > 0) return pszProjection; return GDALPamDataset::GetProjectionRef(); } /************************************************************************/ /* SetProjection() */ /************************************************************************/ CPLErr EHdrDataset::SetProjection( const char *pszSRS ) { // Reset coordinate system on the dataset. CPLFree(pszProjection); pszProjection = CPLStrdup(pszSRS); if( strlen(pszSRS) == 0 ) return CE_None; // Convert to ESRI WKT. OGRSpatialReference oSRS(pszSRS); oSRS.morphToESRI(); char *pszESRI_SRS = NULL; oSRS.exportToWkt(&pszESRI_SRS); // Write to .prj file. CPLString osPrjFilename = CPLResetExtension(GetDescription(), "prj"); VSILFILE *fp = VSIFOpenL(osPrjFilename.c_str(), "wt"); if( fp != NULL ) { size_t nCount = VSIFWriteL(pszESRI_SRS, strlen(pszESRI_SRS), 1, fp); nCount += VSIFWriteL("\n", 1, 1, fp); if( VSIFCloseL(fp) != 0 || nCount != 2 ) { CPLFree(pszESRI_SRS); return CE_Failure; } } CPLFree(pszESRI_SRS); return CE_None; } /************************************************************************/ /* GetGeoTransform() */ /************************************************************************/ CPLErr EHdrDataset::GetGeoTransform( double * padfTransform ) { if( bGotTransform ) { memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6); return CE_None; } return GDALPamDataset::GetGeoTransform(padfTransform); } /************************************************************************/ /* SetGeoTransform() */ /************************************************************************/ CPLErr EHdrDataset::SetGeoTransform( double *padfGeoTransform ) { // We only support non-rotated images with info in the .HDR file. if( padfGeoTransform[2] != 0.0 || padfGeoTransform[4] != 0.0 ) { return GDALPamDataset::SetGeoTransform(padfGeoTransform); } // Record new geotransform. bGotTransform = true; memcpy(adfGeoTransform, padfGeoTransform, sizeof(double) * 6); // Strip out all old geotransform keywords from HDR records. for( int i = CSLCount(papszHDR) - 1; i >= 0; i-- ) { if( STARTS_WITH_CI(papszHDR[i], "ul") || STARTS_WITH_CI(papszHDR[i] + 1, "ll") || STARTS_WITH_CI(papszHDR[i], "cell") || STARTS_WITH_CI(papszHDR[i] + 1, "dim") ) { papszHDR = CSLRemoveStrings(papszHDR, i, 1, NULL); } } // Set the transformation information. CPLString oValue; oValue.Printf("%.15g", adfGeoTransform[0] + adfGeoTransform[1] * 0.5); ResetKeyValue("ULXMAP", oValue); oValue.Printf("%.15g", adfGeoTransform[3] + adfGeoTransform[5] * 0.5); ResetKeyValue("ULYMAP", oValue); oValue.Printf("%.15g", adfGeoTransform[1]); ResetKeyValue("XDIM", oValue); oValue.Printf("%.15g", fabs(adfGeoTransform[5])); ResetKeyValue("YDIM", oValue); return CE_None; } /************************************************************************/ /* RewriteHDR() */ /************************************************************************/ CPLErr EHdrDataset::RewriteHDR() { const CPLString osPath = CPLGetPath(GetDescription()); const CPLString osName = CPLGetBasename(GetDescription()); const CPLString osHDRFilename = CPLFormCIFilename(osPath, osName, osHeaderExt); // Write .hdr file. VSILFILE *fp = VSIFOpenL(osHDRFilename, "wt"); if( fp == NULL ) { CPLError(CE_Failure, CPLE_OpenFailed, "Failed to rewrite .hdr file %s.", osHDRFilename.c_str()); return CE_Failure; } for( int i = 0; papszHDR[i] != NULL; i++ ) { size_t nCount = VSIFWriteL(papszHDR[i], strlen(papszHDR[i]), 1, fp); nCount += VSIFWriteL("\n", 1, 1, fp); if( nCount != 2 ) { CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); return CE_Failure; } } bHDRDirty = false; if( VSIFCloseL(fp) != 0 ) return CE_Failure; return CE_None; } /************************************************************************/ /* RewriteSTX() */ /************************************************************************/ CPLErr EHdrDataset::RewriteSTX() { const CPLString osPath = CPLGetPath(GetDescription()); const CPLString osName = CPLGetBasename(GetDescription()); const CPLString osSTXFilename = CPLFormCIFilename(osPath, osName, "stx"); VSILFILE *fp = VSIFOpenL(osSTXFilename, "wt"); if( fp == NULL ) { CPLDebug("EHDR", "Failed to rewrite .stx file %s.", osSTXFilename.c_str()); return CE_Failure; } bool bOK = true; for ( int i = 0; bOK && i < nBands; ++i ) { EHdrRasterBand* poBand = reinterpret_cast<EHdrRasterBand *>(papoBands[i]); bOK &= VSIFPrintfL(fp, "%d %.10f %.10f ", i + 1, poBand->dfMin, poBand->dfMax) >= 0; if ( poBand->minmaxmeanstddev & HAS_MEAN_FLAG ) bOK &= VSIFPrintfL(fp, "%.10f ", poBand->dfMean) >= 0; else bOK &= VSIFPrintfL(fp, "# ") >= 0; if ( poBand->minmaxmeanstddev & HAS_STDDEV_FLAG ) bOK &= VSIFPrintfL(fp, "%.10f\n", poBand->dfStdDev) >= 0; else bOK &= VSIFPrintfL(fp, "#\n") >= 0; } if( VSIFCloseL(fp) != 0 ) bOK = false; return bOK ? CE_None : CE_Failure; } /************************************************************************/ /* ReadSTX() */ /************************************************************************/ CPLErr EHdrDataset::ReadSTX() { const CPLString osPath = CPLGetPath(GetDescription()); const CPLString osName = CPLGetBasename(GetDescription()); const CPLString osSTXFilename = CPLFormCIFilename(osPath, osName, "stx"); VSILFILE *fp = VSIFOpenL(osSTXFilename, "rt"); if (fp == NULL) return CE_None; const char *pszLine = NULL; while( (pszLine = CPLReadLineL(fp)) != NULL ) { char **papszTokens = CSLTokenizeStringComplex(pszLine, " \t", TRUE, FALSE); const int nTokens = CSLCount(papszTokens); if( nTokens >= 5 ) { const int i = atoi(papszTokens[0]); if (i > 0 && i <= nBands) { EHdrRasterBand *poBand = reinterpret_cast<EHdrRasterBand *>(papoBands[i - 1]); poBand->dfMin = CPLAtof(papszTokens[1]); poBand->dfMax = CPLAtof(papszTokens[2]); int bNoDataSet = FALSE; const double dfNoData = poBand->GetNoDataValue(&bNoDataSet); if (bNoDataSet && dfNoData == poBand->dfMin) { // Triggered by // /vsicurl/http://eros.usgs.gov/archive/nslrsda/GeoTowns/HongKong/srtm/n22e113.zip/n22e113.bil CPLDebug( "EHDr", "Ignoring .stx file where min == nodata. " "The nodata value should not be taken into account " "in minimum value computation."); CSLDestroy(papszTokens); papszTokens = NULL; break; } poBand->minmaxmeanstddev = HAS_MIN_FLAG | HAS_MAX_FLAG; // Reads optional mean and stddev. if (!EQUAL(papszTokens[3], "#") ) { poBand->dfMean = CPLAtof(papszTokens[3]); poBand->minmaxmeanstddev |= HAS_MEAN_FLAG; } if ( !EQUAL(papszTokens[4], "#") ) { poBand->dfStdDev = CPLAtof(papszTokens[4]); poBand->minmaxmeanstddev |= HAS_STDDEV_FLAG; } if( nTokens >= 6 && !EQUAL(papszTokens[5], "#") ) poBand->SetMetadataItem("STRETCHMIN", papszTokens[5], "RENDERING_HINTS"); if( nTokens >= 7 && !EQUAL(papszTokens[6], "#") ) poBand->SetMetadataItem("STRETCHMAX", papszTokens[6], "RENDERING_HINTS"); } } CSLDestroy(papszTokens); } CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); return CE_None; } /************************************************************************/ /* GetImageRepFilename() */ /************************************************************************/ // Check for IMAGE.REP (Spatiocarte Defense 1.0) or name_of_image.rep // if it's a GIS-GeoSPOT image. // For the specification of SPDF (in French), see // http://eden.ign.fr/download/pub/doc/emabgi/spdf10.pdf/download CPLString EHdrDataset::GetImageRepFilename(const char *pszFilename) { const CPLString osPath = CPLGetPath(pszFilename); const CPLString osName = CPLGetBasename(pszFilename); const CPLString osREPFilename = CPLFormCIFilename(osPath, osName, "rep"); VSIStatBufL sStatBuf; if( VSIStatExL( osREPFilename.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 ) return osREPFilename; if (EQUAL(CPLGetFilename(pszFilename), "imspatio.bil") || EQUAL(CPLGetFilename(pszFilename), "haspatio.bil")) { CPLString osImageRepFilename(CPLFormCIFilename(osPath, "image", "rep")); if( VSIStatExL( osImageRepFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 ) return osImageRepFilename; // Try in the upper directories if not found in the BIL image directory. CPLString dirName(CPLGetDirname(osPath)); if (CPLIsFilenameRelative(osPath.c_str())) { char *cwd = CPLGetCurrentDir(); if (cwd) { dirName = CPLFormFilename(cwd, dirName.c_str(), NULL); CPLFree(cwd); } } while( dirName[0] != 0 && EQUAL(dirName, ".") == FALSE && EQUAL(dirName, "/") == FALSE ) { osImageRepFilename = CPLFormCIFilename(dirName.c_str(), "image", "rep"); if( VSIStatExL( osImageRepFilename.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 ) return osImageRepFilename; // Don't try to recurse above the 'image' subdirectory. if( EQUAL(dirName, "image") ) { break; } dirName = CPLString(CPLGetDirname(dirName)); } } return CPLString(); } /************************************************************************/ /* GetFileList() */ /************************************************************************/ char **EHdrDataset::GetFileList() { const CPLString osPath = CPLGetPath(GetDescription()); const CPLString osName = CPLGetBasename(GetDescription()); // Main data file, etc. char **papszFileList = GDALPamDataset::GetFileList(); // Header file. CPLString osFilename = CPLFormCIFilename(osPath, osName, osHeaderExt); papszFileList = CSLAddString(papszFileList, osFilename); // Statistics file osFilename = CPLFormCIFilename(osPath, osName, "stx"); VSIStatBufL sStatBuf; if( VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 ) papszFileList = CSLAddString(papszFileList, osFilename); // color table file. osFilename = CPLFormCIFilename(osPath, osName, "clr"); if( VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 ) papszFileList = CSLAddString(papszFileList, osFilename); // projections file. osFilename = CPLFormCIFilename(osPath, osName, "prj"); if( VSIStatExL(osFilename, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0 ) papszFileList = CSLAddString(papszFileList, osFilename); const CPLString imageRepFilename = GetImageRepFilename(GetDescription()); if (!imageRepFilename.empty()) papszFileList = CSLAddString(papszFileList, imageRepFilename.c_str()); return papszFileList; } /************************************************************************/ /* Open() */ /************************************************************************/ GDALDataset *EHdrDataset::Open( GDALOpenInfo * poOpenInfo ) { // Assume the caller is pointing to the binary (i.e. .bil) file. if( poOpenInfo->nHeaderBytes < 2 ) return NULL; // Tear apart the filename to form a .HDR filename. const CPLString osPath = CPLGetPath(poOpenInfo->pszFilename); const CPLString osName = CPLGetBasename(poOpenInfo->pszFilename); const char *pszHeaderExt = "hdr"; if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "SRC") && osName.size() == 7 && (osName[0] == 'e' || osName[0] == 'E' || osName[0] == 'w' || osName[0] == 'W') && (osName[4] == 'n' || osName[4] == 'N' || osName[4] == 's' || osName[4] == 'S') ) { // It is a GTOPO30 or SRTM30 source file, whose header extension is .sch // see http://dds.cr.usgs.gov/srtm/version1/SRTM30/GTOPO30_Documentation pszHeaderExt = "sch"; } char **papszSiblingFiles = poOpenInfo->GetSiblingFiles(); CPLString osHDRFilename; if( papszSiblingFiles ) { const int iFile = CSLFindString( papszSiblingFiles, CPLFormFilename(NULL, osName, pszHeaderExt)); if( iFile < 0 ) // Return if there is no corresponding .hdr file. return NULL; osHDRFilename = CPLFormFilename(osPath, papszSiblingFiles[iFile], NULL); } else { osHDRFilename = CPLFormCIFilename(osPath, osName, pszHeaderExt); } const bool bSelectedHDR = EQUAL(osHDRFilename, poOpenInfo->pszFilename); // Do we have a .hdr file? VSILFILE *fp = VSIFOpenL(osHDRFilename, "r"); if( fp == NULL ) { return NULL; } // Is this file an ESRI header file? Read a few lines of text // searching for something starting with nrows or ncols. int nRows = -1; int nCols = -1; int nBands = 1; int nSkipBytes = 0; double dfULXMap = 0.5; double dfULYMap = 0.5; double dfYLLCorner = -123.456; int bCenter = TRUE; double dfXDim = 1.0; double dfYDim = 1.0; double dfNoData = 0.0; int nLineCount = 0; int bNoDataSet = FALSE; GDALDataType eDataType = GDT_Byte; int nBits = -1; char chByteOrder = 'M'; char chPixelType = 'N'; // Not defined. char szLayout[10] = "BIL"; char **papszHDR = NULL; int bHasInternalProjection = FALSE; int bHasMin = FALSE; int bHasMax = FALSE; double dfMin = 0; double dfMax = 0; const char *pszLine = NULL; while( (pszLine = CPLReadLineL(fp)) != NULL ) { nLineCount++; if( nLineCount > 50 || strlen(pszLine) > 1000 ) break; papszHDR = CSLAddString(papszHDR, pszLine); char **papszTokens = CSLTokenizeStringComplex(pszLine, " \t", TRUE, FALSE); if( CSLCount(papszTokens) < 2 ) { CSLDestroy(papszTokens); continue; } if( EQUAL(papszTokens[0], "ncols") ) { nCols = atoi(papszTokens[1]); } else if( EQUAL(papszTokens[0], "nrows") ) { nRows = atoi(papszTokens[1]); } else if( EQUAL(papszTokens[0], "skipbytes") ) { nSkipBytes = atoi(papszTokens[1]); } else if( EQUAL(papszTokens[0], "ulxmap") || EQUAL(papszTokens[0], "xllcorner") || EQUAL(papszTokens[0], "xllcenter") ) { dfULXMap = CPLAtofM(papszTokens[1]); if( EQUAL(papszTokens[0], "xllcorner") ) bCenter = FALSE; } else if( EQUAL(papszTokens[0], "ulymap") ) { dfULYMap = CPLAtofM(papszTokens[1]); } else if( EQUAL(papszTokens[0], "yllcorner") || EQUAL(papszTokens[0], "yllcenter") ) { dfYLLCorner = CPLAtofM(papszTokens[1]); if( EQUAL(papszTokens[0], "yllcorner") ) bCenter = FALSE; } else if( EQUAL(papszTokens[0], "xdim") ) { dfXDim = CPLAtofM(papszTokens[1]); } else if( EQUAL(papszTokens[0], "ydim") ) { dfYDim = CPLAtofM(papszTokens[1]); } else if( EQUAL(papszTokens[0], "cellsize") ) { dfXDim = CPLAtofM(papszTokens[1]); dfYDim = dfXDim; } else if( EQUAL(papszTokens[0], "nbands") ) { nBands = atoi(papszTokens[1]); } else if( EQUAL(papszTokens[0], "layout") ) { snprintf( szLayout, sizeof(szLayout), "%s", papszTokens[1] ); } else if( EQUAL(papszTokens[0], "NODATA_value") || EQUAL(papszTokens[0], "NODATA") ) { dfNoData = CPLAtofM(papszTokens[1]); bNoDataSet = TRUE; } else if( EQUAL(papszTokens[0], "NBITS") ) { nBits = atoi(papszTokens[1]); } else if( EQUAL(papszTokens[0], "PIXELTYPE") ) { chPixelType = static_cast<char>(toupper(papszTokens[1][0])); } else if( EQUAL(papszTokens[0], "byteorder") ) { chByteOrder = static_cast<char>(toupper(papszTokens[1][0])); } // http://www.worldclim.org/futdown.htm have the projection extensions else if( EQUAL(papszTokens[0], "Projection") ) { bHasInternalProjection = TRUE; } else if( EQUAL(papszTokens[0], "MinValue") || EQUAL(papszTokens[0], "MIN_VALUE") ) { dfMin = CPLAtofM(papszTokens[1]); bHasMin = TRUE; } else if( EQUAL(papszTokens[0], "MaxValue") || EQUAL(papszTokens[0], "MAX_VALUE") ) { dfMax = CPLAtofM(papszTokens[1]); bHasMax = TRUE; } CSLDestroy(papszTokens); } CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); // Did we get the required keywords? If not, return with this never having // been considered to be a match. This isn't an error! if( nRows == -1 || nCols == -1 ) { CSLDestroy(papszHDR); return NULL; } if (!GDALCheckDatasetDimensions(nCols, nRows) || !GDALCheckBandCount(nBands, FALSE)) { CSLDestroy(papszHDR); return NULL; } // Has the caller selected the .hdr file to open? if( bSelectedHDR ) { CPLError(CE_Failure, CPLE_AppDefined, "The selected file is an ESRI BIL header file, but to " "open ESRI BIL datasets, the data file should be selected " "instead of the .hdr file. Please try again selecting " "the data file (often with the extension .bil) corresponding " "to the header file: %s", poOpenInfo->pszFilename); CSLDestroy(papszHDR); return NULL; } // If we aren't sure of the file type, check the data file size. If it is 4 // bytes or more per pixel then we assume it is floating point data. if( nBits == -1 && chPixelType == 'N' ) { VSIStatBufL sStatBuf; if( VSIStatL(poOpenInfo->pszFilename, &sStatBuf) == 0 ) { const size_t nBytes = static_cast<size_t>(sStatBuf.st_size/nCols/nRows/nBands); if( nBytes > 0 && nBytes != 3 ) nBits = static_cast<int>(nBytes * 8); if( nBytes == 4 ) chPixelType = 'F'; } } // If the extension is FLT it is likely a floating point file. if( chPixelType == 'N' ) { if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "FLT") ) chPixelType = 'F'; } // If we have a negative nodata value, assume that the // pixel type is signed. This is necessary for datasets from // http://www.worldclim.org/futdown.htm if( bNoDataSet && dfNoData < 0 && chPixelType == 'N' ) { chPixelType = 'S'; } EHdrDataset *poDS = new EHdrDataset(); poDS->osHeaderExt = pszHeaderExt; poDS->nRasterXSize = nCols; poDS->nRasterYSize = nRows; poDS->papszHDR = papszHDR; // Open target binary file. if( poOpenInfo->eAccess == GA_ReadOnly ) poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb"); else poDS->fpImage = VSIFOpenL(poOpenInfo->pszFilename, "r+b"); if( poDS->fpImage == NULL ) { CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s with write permission.\n%s", osName.c_str(), VSIStrerror(errno)); delete poDS; return NULL; } poDS->eAccess = poOpenInfo->eAccess; // Figure out the data type. if( nBits == 16 ) { if ( chPixelType == 'S' ) eDataType = GDT_Int16; else eDataType = GDT_UInt16; // Default } else if( nBits == 32 ) { if( chPixelType == 'S' ) eDataType = GDT_Int32; else if( chPixelType == 'F' ) eDataType = GDT_Float32; else eDataType = GDT_UInt32; // Default } else if( nBits == 8 ) { eDataType = GDT_Byte; nBits = 8; } else if( nBits < 8 && nBits >= 1 ) { eDataType = GDT_Byte; } else if( nBits == -1 ) { if( chPixelType == 'F' ) { eDataType = GDT_Float32; nBits = 32; } else { eDataType = GDT_Byte; nBits = 8; } } else { CPLError(CE_Failure, CPLE_NotSupported, "EHdr driver does not support %d NBITS value.", nBits); delete poDS; poDS = NULL; return NULL; } // Compute the line offset. const int nItemSize = GDALGetDataTypeSizeBytes(eDataType); CPLAssert(nItemSize != 0); CPLAssert(nBands != 0); int nPixelOffset = 0; int nLineOffset = 0; vsi_l_offset nBandOffset = 0; if( EQUAL(szLayout, "BIP") ) { if (nCols > std::numeric_limits<int>::max() / (nItemSize * nBands)) { delete poDS; poDS = NULL; CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred."); return NULL; } nPixelOffset = nItemSize * nBands; nLineOffset = nPixelOffset * nCols; nBandOffset = static_cast<vsi_l_offset>(nItemSize); } else if( EQUAL(szLayout, "BSQ") ) { if (nCols > std::numeric_limits<int>::max() / nItemSize) { delete poDS; poDS = NULL; CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred."); return NULL; } nPixelOffset = nItemSize; nLineOffset = nPixelOffset * nCols; nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows; } else { // Assume BIL. if (nCols > std::numeric_limits<int>::max() / (nItemSize * nBands)) { delete poDS; poDS = NULL; CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred."); return NULL; } nPixelOffset = nItemSize; nLineOffset = nItemSize * nBands * nCols; nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nCols; } poDS->SetDescription(poOpenInfo->pszFilename); poDS->PamInitialize(); // Create band information objects. poDS->nBands = nBands; CPLErrorReset(); for( int i = 0; i < poDS->nBands; i++ ) { EHdrRasterBand *poBand = new EHdrRasterBand( poDS, i + 1, poDS->fpImage, nSkipBytes + nBandOffset * i, nPixelOffset, nLineOffset, eDataType, #ifdef CPL_LSB chByteOrder == 'I' || chByteOrder == 'L', #else chByteOrder == 'M', #endif nBits); poBand->bNoDataSet = bNoDataSet; poBand->dfNoData = dfNoData; if( bHasMin && bHasMax ) { poBand->dfMin = dfMin; poBand->dfMax = dfMax; poBand->minmaxmeanstddev = HAS_MIN_FLAG | HAS_MAX_FLAG; } poDS->SetBand(i + 1, poBand); if( CPLGetLastErrorType() != CE_None ) { poDS->nBands = i + 1; delete poDS; return NULL; } } // If we didn't get bounds in the .hdr, look for a worldfile. if( dfYLLCorner != -123.456 ) { if( bCenter ) dfULYMap = dfYLLCorner + (nRows-1) * dfYDim; else dfULYMap = dfYLLCorner + nRows * dfYDim; } if( dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0 ) { poDS->bGotTransform = true; if( bCenter ) { poDS->adfGeoTransform[0] = dfULXMap - dfXDim * 0.5; poDS->adfGeoTransform[1] = dfXDim; poDS->adfGeoTransform[2] = 0.0; poDS->adfGeoTransform[3] = dfULYMap + dfYDim * 0.5; poDS->adfGeoTransform[4] = 0.0; poDS->adfGeoTransform[5] = - dfYDim; } else { poDS->adfGeoTransform[0] = dfULXMap; poDS->adfGeoTransform[1] = dfXDim; poDS->adfGeoTransform[2] = 0.0; poDS->adfGeoTransform[3] = dfULYMap; poDS->adfGeoTransform[4] = 0.0; poDS->adfGeoTransform[5] = - dfYDim; } } if( !poDS->bGotTransform ) poDS->bGotTransform = CPL_TO_BOOL( GDALReadWorldFile(poOpenInfo->pszFilename, NULL, poDS->adfGeoTransform)); if( !poDS->bGotTransform ) poDS->bGotTransform = CPL_TO_BOOL( GDALReadWorldFile(poOpenInfo->pszFilename, "wld", poDS->adfGeoTransform)); // Check for a .prj file. const char *pszPrjFilename = CPLFormCIFilename(osPath, osName, "prj"); fp = VSIFOpenL(pszPrjFilename, "r"); // .hdr files from http://www.worldclim.org/futdown.htm have the projection // info in the .hdr file itself. if (fp == NULL && bHasInternalProjection) { pszPrjFilename = osHDRFilename; fp = VSIFOpenL(pszPrjFilename, "r"); } if( fp != NULL ) { CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); fp = NULL; char **papszLines = CSLLoad(pszPrjFilename); OGRSpatialReference oSRS; if( oSRS.importFromESRI(papszLines) == OGRERR_NONE ) { // If geographic values are in seconds, we must transform. // Is there a code for minutes too? char szResult[80] = { '\0' }; if( oSRS.IsGeographic() && EQUAL(OSR_GDS(szResult, sizeof(szResult), papszLines, "Units", ""), "DS") ) { poDS->adfGeoTransform[0] /= 3600.0; poDS->adfGeoTransform[1] /= 3600.0; poDS->adfGeoTransform[2] /= 3600.0; poDS->adfGeoTransform[3] /= 3600.0; poDS->adfGeoTransform[4] /= 3600.0; poDS->adfGeoTransform[5] /= 3600.0; } CPLFree(poDS->pszProjection); oSRS.exportToWkt(&(poDS->pszProjection)); } CSLDestroy(papszLines); } else { // Check for IMAGE.REP (Spatiocarte Defense 1.0) or name_of_image.rep // if it's a GIS-GeoSPOT image // For the specification of SPDF (in French), see // http://eden.ign.fr/download/pub/doc/emabgi/spdf10.pdf/download const CPLString szImageRepFilename = GetImageRepFilename(poOpenInfo->pszFilename); if (!szImageRepFilename.empty()) { fp = VSIFOpenL(szImageRepFilename.c_str(), "r"); } if (fp != NULL) { bool bUTM = false; bool bWGS84 = false; int bNorth = FALSE; bool bSouth = false; int utmZone = 0; while( (pszLine = CPLReadLineL(fp)) != NULL ) { if (STARTS_WITH(pszLine, "PROJ_ID") && strstr(pszLine, "UTM")) { bUTM = true; } else if (STARTS_WITH(pszLine, "PROJ_ZONE")) { const char *c = strchr(pszLine, '"'); if (c) { c++; if (*c >= '0' && *c <= '9') { utmZone = atoi(c); if (utmZone >= 1 && utmZone <= 60) { if( strstr(pszLine, "Nord") || strstr(pszLine, "NORD") ) { bNorth = TRUE; } else if( strstr(pszLine, "Sud") || strstr(pszLine, "SUD") ) { bSouth = true; } } } } } else if (STARTS_WITH(pszLine, "PROJ_CODE") && strstr(pszLine, "FR-MINDEF")) { const char *c = strchr(pszLine, 'A'); if (c) { c++; if (*c >= '0' && *c <= '9') { utmZone = atoi(c); if (utmZone >= 1 && utmZone <= 60) { if (c[1] == 'N' || (c[1] != '\0' && c[2] == 'N')) { bNorth = TRUE; } else if (c[1] == 'S' || (c[1] != '\0' && c[2] == 'S')) { bSouth = true; } } } } } else if( STARTS_WITH(pszLine, "HORIZ_DATUM") && (strstr(pszLine, "WGS 84") || strstr(pszLine, "WGS84")) ) { bWGS84 = true; } else if(STARTS_WITH(pszLine, "MAP_NUMBER") ) { const char *c = strchr(pszLine, '"'); if (c) { char *pszMapNumber = CPLStrdup(c + 1); char *c2 = strchr(pszMapNumber, '"'); if (c2) *c2 = 0; poDS->SetMetadataItem("SPDF_MAP_NUMBER", pszMapNumber); CPLFree(pszMapNumber); } } else if (STARTS_WITH(pszLine, "PRODUCTION_DATE")) { const char *c = pszLine + strlen("PRODUCTION_DATE"); while(*c == ' ') c++; if (*c) { poDS->SetMetadataItem("SPDF_PRODUCTION_DATE", c); } } } CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); if (utmZone != 0 && bUTM && bWGS84 && (bNorth || bSouth)) { char projCSStr[64] = { '\0' }; snprintf(projCSStr, sizeof(projCSStr), "WGS 84 / UTM zone %d%c", utmZone, (bNorth) ? 'N' : 'S'); OGRSpatialReference oSRS; oSRS.SetProjCS(projCSStr); oSRS.SetWellKnownGeogCS("WGS84"); oSRS.SetUTM(utmZone, bNorth); oSRS.SetAuthority("PROJCS", "EPSG", (bNorth ? 32600 : 32700) + utmZone); oSRS.AutoIdentifyEPSG(); CPLFree(poDS->pszProjection); oSRS.exportToWkt(&(poDS->pszProjection)); } else { CPLError(CE_Warning, CPLE_NotSupported, "Cannot retrieve projection from IMAGE.REP"); } } } // Check for a color table. const char *pszCLRFilename = CPLFormCIFilename(osPath, osName, "clr"); // Only read the .clr for byte, int16 or uint16 bands. if (nItemSize <= 2) fp = VSIFOpenL(pszCLRFilename, "r"); else fp = NULL; if( fp != NULL ) { GDALColorTable oColorTable; int bHasWarned = FALSE; while( true ) { pszLine = CPLReadLineL(fp); if ( !pszLine ) break; if( *pszLine == '#' || *pszLine == '!' ) continue; char **papszValues = CSLTokenizeString2(pszLine, "\t ", CSLT_HONOURSTRINGS); if ( CSLCount(papszValues) >= 4 ) { const int nIndex = atoi(papszValues[0]); if (nIndex >= 0 && nIndex < 65536) { const GDALColorEntry oEntry = { static_cast<short>(atoi(papszValues[1])), // Red static_cast<short>(atoi(papszValues[2])), // Green static_cast<short>(atoi(papszValues[3])), // Blue 255 }; oColorTable.SetColorEntry(nIndex, &oEntry); } else { // Negative values are valid. At least we can find use of // them here: // http://www.ngdc.noaa.gov/mgg/topo/elev/esri/clr/ // But, there's no way of representing them with GDAL color // table model. if (!bHasWarned) CPLDebug("EHdr", "Ignoring color index : %d", nIndex); bHasWarned = TRUE; } } CSLDestroy(papszValues); } CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); for( int i = 1; i <= poDS->nBands; i++ ) { GDALRasterBand *poBand = poDS->GetRasterBand(i); poBand->SetColorTable(&oColorTable); poBand->SetColorInterpretation(GCI_PaletteIndex); } poDS->bCLRDirty = false; } // Read statistics (.STX). poDS->ReadSTX(); // Initialize any PAM information. poDS->TryLoadXML(); // Check for overviews. poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename); return poDS; } /************************************************************************/ /* Create() */ /************************************************************************/ GDALDataset *EHdrDataset::Create( const char * pszFilename, int nXSize, int nYSize, int nBands, GDALDataType eType, char **papszParmList ) { // Verify input options. if (nBands <= 0) { CPLError(CE_Failure, CPLE_NotSupported, "EHdr driver does not support %d bands.", nBands); return NULL; } if( eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 && eType != GDT_Int16 && eType != GDT_Int32 && eType != GDT_UInt32 ) { CPLError(CE_Failure, CPLE_AppDefined, "Attempt to create ESRI .hdr labelled dataset with an illegal" "data type (%s).", GDALGetDataTypeName(eType)); return NULL; } // Try to create the file. VSILFILE *fp = VSIFOpenL(pszFilename, "wb"); if( fp == NULL ) { CPLError(CE_Failure, CPLE_OpenFailed, "Attempt to create file `%s' failed.", pszFilename); return NULL; } // Just write out a couple of bytes to establish the binary // file, and then close it. bool bOK = VSIFWriteL(reinterpret_cast<void *>(const_cast<char *>("\0\0")), 2, 1, fp) == 1; if( VSIFCloseL(fp) != 0 ) bOK = false; fp = NULL; if( !bOK ) return NULL; // Create the hdr filename. char *const pszHdrFilename = CPLStrdup(CPLResetExtension(pszFilename, "hdr")); // Open the file. fp = VSIFOpenL(pszHdrFilename, "wt"); if( fp == NULL ) { CPLError(CE_Failure, CPLE_OpenFailed, "Attempt to create file `%s' failed.", pszHdrFilename); CPLFree(pszHdrFilename); return NULL; } // Decide how many bits the file should have. int nBits = GDALGetDataTypeSize(eType); if( CSLFetchNameValue(papszParmList, "NBITS") != NULL ) nBits = atoi(CSLFetchNameValue(papszParmList, "NBITS")); const int nRowBytes = (nBits * nXSize + 7) / 8; // Check for signed byte. const char *pszPixelType = CSLFetchNameValue(papszParmList, "PIXELTYPE"); if( pszPixelType == NULL ) pszPixelType = ""; // Write out the raw definition for the dataset as a whole. bOK &= VSIFPrintfL(fp, "BYTEORDER I\n") >= 0; bOK &= VSIFPrintfL(fp, "LAYOUT BIL\n") >= 0; bOK &= VSIFPrintfL(fp, "NROWS %d\n", nYSize) >= 0; bOK &= VSIFPrintfL(fp, "NCOLS %d\n", nXSize) >= 0; bOK &= VSIFPrintfL(fp, "NBANDS %d\n", nBands) >= 0; bOK &= VSIFPrintfL(fp, "NBITS %d\n", nBits) >= 0; bOK &= VSIFPrintfL(fp, "BANDROWBYTES %d\n", nRowBytes) >= 0; bOK &= VSIFPrintfL(fp, "TOTALROWBYTES %d\n", nRowBytes * nBands) >= 0; if( eType == GDT_Float32 ) bOK &= VSIFPrintfL(fp, "PIXELTYPE FLOAT\n") >= 0; else if( eType == GDT_Int16 || eType == GDT_Int32 ) bOK &= VSIFPrintfL(fp, "PIXELTYPE SIGNEDINT\n") >= 0; else if( eType == GDT_Byte && EQUAL(pszPixelType, "SIGNEDBYTE") ) bOK &= VSIFPrintfL(fp, "PIXELTYPE SIGNEDINT\n") >= 0; else bOK &= VSIFPrintfL(fp, "PIXELTYPE UNSIGNEDINT\n") >= 0; if( VSIFCloseL(fp) != 0 ) bOK = false; CPLFree(pszHdrFilename); if( !bOK ) return NULL; return reinterpret_cast<GDALDataset *>(GDALOpen(pszFilename, GA_Update)); } /************************************************************************/ /* CreateCopy() */ /************************************************************************/ GDALDataset *EHdrDataset::CreateCopy( const char * pszFilename, GDALDataset * poSrcDS, int bStrict, char ** papszOptions, GDALProgressFunc pfnProgress, void * pProgressData ) { const int nBands = poSrcDS->GetRasterCount(); if (nBands == 0) { CPLError(CE_Failure, CPLE_NotSupported, "EHdr driver does not support source dataset without any " "bands."); return NULL; } char **papszAdjustedOptions = CSLDuplicate(papszOptions); // Ensure we pass on NBITS and PIXELTYPE structure information. if( poSrcDS->GetRasterBand(1)->GetMetadataItem("NBITS", "IMAGE_STRUCTURE") != NULL && CSLFetchNameValue(papszOptions, "NBITS") == NULL ) { papszAdjustedOptions = CSLSetNameValue(papszAdjustedOptions, "NBITS", poSrcDS->GetRasterBand(1)->GetMetadataItem( "NBITS", "IMAGE_STRUCTURE")); } if( poSrcDS->GetRasterBand(1)->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE") != NULL && CSLFetchNameValue(papszOptions, "PIXELTYPE") == NULL ) { papszAdjustedOptions = CSLSetNameValue(papszAdjustedOptions, "PIXELTYPE", poSrcDS->GetRasterBand(1)->GetMetadataItem( "PIXELTYPE", "IMAGE_STRUCTURE")); } // Proceed with normal copying using the default createcopy operators. GDALDriver *poDriver = reinterpret_cast<GDALDriver *>(GDALGetDriverByName("EHdr")); GDALDataset *poOutDS = poDriver->DefaultCreateCopy( pszFilename, poSrcDS, bStrict, papszAdjustedOptions, pfnProgress, pProgressData); CSLDestroy(papszAdjustedOptions); if( poOutDS != NULL ) poOutDS->FlushCache(); return poOutDS; } /************************************************************************/ /* GetNoDataValue() */ /************************************************************************/ double EHdrRasterBand::GetNoDataValue( int *pbSuccess ) { if( pbSuccess ) *pbSuccess = bNoDataSet; if( bNoDataSet ) return dfNoData; return RawRasterBand::GetNoDataValue(pbSuccess); } /************************************************************************/ /* GetMinimum() */ /************************************************************************/ double EHdrRasterBand::GetMinimum( int *pbSuccess ) { if( pbSuccess != NULL ) *pbSuccess = (minmaxmeanstddev & HAS_MIN_FLAG) != 0; if( minmaxmeanstddev & HAS_MIN_FLAG ) return dfMin; return RawRasterBand::GetMinimum(pbSuccess); } /************************************************************************/ /* GetMaximum() */ /************************************************************************/ double EHdrRasterBand::GetMaximum( int *pbSuccess ) { if( pbSuccess != NULL ) *pbSuccess = (minmaxmeanstddev & HAS_MAX_FLAG) != 0; if( minmaxmeanstddev & HAS_MAX_FLAG ) return dfMax; return RawRasterBand::GetMaximum(pbSuccess); } /************************************************************************/ /* GetStatistics() */ /************************************************************************/ CPLErr EHdrRasterBand::GetStatistics( int bApproxOK, int bForce, double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev ) { if( (minmaxmeanstddev & HAS_ALL_FLAGS) == HAS_ALL_FLAGS) { if( pdfMin ) *pdfMin = dfMin; if( pdfMax ) *pdfMax = dfMax; if( pdfMean ) *pdfMean = dfMean; if( pdfStdDev ) *pdfStdDev = dfStdDev; return CE_None; } const CPLErr eErr = RawRasterBand::GetStatistics(bApproxOK, bForce, &dfMin, &dfMax, &dfMean, &dfStdDev); if( eErr != CE_None ) return eErr; EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS); minmaxmeanstddev = HAS_ALL_FLAGS; if( poEDS->RewriteSTX() != CE_None ) RawRasterBand::SetStatistics(dfMin, dfMax, dfMean, dfStdDev); if( pdfMin ) *pdfMin = dfMin; if( pdfMax ) *pdfMax = dfMax; if( pdfMean ) *pdfMean = dfMean; if( pdfStdDev ) *pdfStdDev = dfStdDev; return CE_None; } /************************************************************************/ /* SetStatistics() */ /************************************************************************/ CPLErr EHdrRasterBand::SetStatistics( double dfMinIn, double dfMaxIn, double dfMeanIn, double dfStdDevIn ) { // Avoid churn if nothing is changing. if( dfMin == dfMinIn && dfMax == dfMaxIn && dfMean == dfMeanIn && dfStdDev == dfStdDevIn ) return CE_None; dfMin = dfMinIn; dfMax = dfMaxIn; dfMean = dfMeanIn; dfStdDev = dfStdDevIn; // marks stats valid minmaxmeanstddev = HAS_ALL_FLAGS; EHdrDataset *poEDS = reinterpret_cast<EHdrDataset *>(poDS); if( poEDS->RewriteSTX() != CE_None ) return RawRasterBand::SetStatistics( dfMinIn, dfMaxIn, dfMeanIn, dfStdDevIn); return CE_None; } /************************************************************************/ /* SetColorTable() */ /************************************************************************/ CPLErr EHdrRasterBand::SetColorTable( GDALColorTable *poNewCT ) { const CPLErr err = RawRasterBand::SetColorTable(poNewCT); if( err != CE_None ) return err; reinterpret_cast<EHdrDataset *>(poDS)->bCLRDirty = true; return CE_None; } /************************************************************************/ /* GDALRegister_EHdr() */ /************************************************************************/ void GDALRegister_EHdr() { if( GDALGetDriverByName("EHdr") != NULL ) return; GDALDriver *poDriver = new GDALDriver(); poDriver->SetDescription("EHdr"); poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ESRI .hdr Labelled"); poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "frmt_various.html#EHdr"); poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Byte Int16 UInt16 Int32 UInt32 Float32"); poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, "<CreationOptionList>" " <Option name='NBITS' type='int' description='Special pixel bits (1-7)'/>" " <Option name='PIXELTYPE' type='string' description='By setting this to SIGNEDBYTE, a new Byte file can be forced to be written as signed byte'/>" "</CreationOptionList>" ); poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); poDriver->pfnOpen = EHdrDataset::Open; poDriver->pfnCreate = EHdrDataset::Create; poDriver->pfnCreateCopy = EHdrDataset::CreateCopy; GetGDALDriverManager()->RegisterDriver(poDriver); }