EVOLUTION-MANAGER
Edit File: envidataset.cpp
/****************************************************************************** * $Id: envidataset.cpp 27366 2014-05-19 18:27:40Z rouault $ * * Project: ENVI .hdr Driver * Purpose: Implementation of ENVI .hdr labelled raw raster support. * Author: Frank Warmerdam, warmerdam@pobox.com * Maintainer: Chris Padwick (cpadwick at ittvis.com) * ****************************************************************************** * Copyright (c) 2002, Frank Warmerdam * 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 "rawdataset.h" #include "ogr_spatialref.h" #include "cpl_string.h" #include <algorithm> CPL_CVSID("$Id: envidataset.cpp 27366 2014-05-19 18:27:40Z rouault $"); CPL_C_START void GDALRegister_ENVI(void); CPL_C_END static const int anUsgsEsriZones[] = { 101, 3101, 102, 3126, 201, 3151, 202, 3176, 203, 3201, 301, 3226, 302, 3251, 401, 3276, 402, 3301, 403, 3326, 404, 3351, 405, 3376, 406, 3401, 407, 3426, 501, 3451, 502, 3476, 503, 3501, 600, 3526, 700, 3551, 901, 3601, 902, 3626, 903, 3576, 1001, 3651, 1002, 3676, 1101, 3701, 1102, 3726, 1103, 3751, 1201, 3776, 1202, 3801, 1301, 3826, 1302, 3851, 1401, 3876, 1402, 3901, 1501, 3926, 1502, 3951, 1601, 3976, 1602, 4001, 1701, 4026, 1702, 4051, 1703, 6426, 1801, 4076, 1802, 4101, 1900, 4126, 2001, 4151, 2002, 4176, 2101, 4201, 2102, 4226, 2103, 4251, 2111, 6351, 2112, 6376, 2113, 6401, 2201, 4276, 2202, 4301, 2203, 4326, 2301, 4351, 2302, 4376, 2401, 4401, 2402, 4426, 2403, 4451, 2500, 0, 2501, 4476, 2502, 4501, 2503, 4526, 2600, 0, 2601, 4551, 2602, 4576, 2701, 4601, 2702, 4626, 2703, 4651, 2800, 4676, 2900, 4701, 3001, 4726, 3002, 4751, 3003, 4776, 3101, 4801, 3102, 4826, 3103, 4851, 3104, 4876, 3200, 4901, 3301, 4926, 3302, 4951, 3401, 4976, 3402, 5001, 3501, 5026, 3502, 5051, 3601, 5076, 3602, 5101, 3701, 5126, 3702, 5151, 3800, 5176, 3900, 0, 3901, 5201, 3902, 5226, 4001, 5251, 4002, 5276, 4100, 5301, 4201, 5326, 4202, 5351, 4203, 5376, 4204, 5401, 4205, 5426, 4301, 5451, 4302, 5476, 4303, 5501, 4400, 5526, 4501, 5551, 4502, 5576, 4601, 5601, 4602, 5626, 4701, 5651, 4702, 5676, 4801, 5701, 4802, 5726, 4803, 5751, 4901, 5776, 4902, 5801, 4903, 5826, 4904, 5851, 5001, 6101, 5002, 6126, 5003, 6151, 5004, 6176, 5005, 6201, 5006, 6226, 5007, 6251, 5008, 6276, 5009, 6301, 5010, 6326, 5101, 5876, 5102, 5901, 5103, 5926, 5104, 5951, 5105, 5976, 5201, 6001, 5200, 6026, 5200, 6076, 5201, 6051, 5202, 6051, 5300, 0, 5400, 0 }; /************************************************************************/ /* ITTVISToUSGSZone() */ /* */ /* Convert ITTVIS style state plane zones to NOS style state */ /* plane zones. The ENVI default is to use the new NOS zones, */ /* but the old state plane zones can be used. Handle this. */ /************************************************************************/ static int ITTVISToUSGSZone( int nITTVISZone ) { int nPairs = sizeof(anUsgsEsriZones) / (2*sizeof(int)); int i; // Default is to use the zone as-is, as long as it is in the // available list for( i = 0; i < nPairs; i++ ) { if( anUsgsEsriZones[i*2] == nITTVISZone ) return anUsgsEsriZones[i*2]; } // If not found in the new style, see if it is present in the // old style list and convert it. We don't expect to see this // often, but older files allowed it and may still exist. for( i = 0; i < nPairs; i++ ) { if( anUsgsEsriZones[i*2+1] == nITTVISZone ) return anUsgsEsriZones[i*2]; } return nITTVISZone; // perhaps it *is* the USGS zone? } /************************************************************************/ /* ==================================================================== */ /* ENVIDataset */ /* ==================================================================== */ /************************************************************************/ class ENVIRasterBand; class ENVIDataset : public RawDataset { friend class ENVIRasterBand; VSILFILE *fpImage; // image data file. VSILFILE *fp; // header file char *pszHDRFilename; int bFoundMapinfo; int bHeaderDirty; double adfGeoTransform[6]; char *pszProjection; char **papszHeader; CPLString osStaFilename; int ReadHeader( VSILFILE * ); int ProcessMapinfo( const char * ); void ProcessRPCinfo( const char * ,int ,int); void ProcessStatsFile(); int byteSwapInt(int); float byteSwapFloat(float); double byteSwapDouble(double); void SetENVIDatum( OGRSpatialReference *, const char * ); void SetENVIEllipse( OGRSpatialReference *, char ** ); void WriteProjectionInfo(); int ParseRpcCoeffsMetaDataString(const char *psName, char *papszVal[], int& idx); int WriteRpcInfo(); int WritePseudoGcpInfo(); char **SplitList( const char * ); enum Interleave { BSQ, BIL, BIP } interleave; static int GetEnviType(GDALDataType eType); public: ENVIDataset(); ~ENVIDataset(); virtual void FlushCache( void ); virtual CPLErr GetGeoTransform( double * padfTransform ); virtual CPLErr SetGeoTransform( double * ); virtual const char *GetProjectionRef(void); virtual CPLErr SetProjection( const char * ); virtual char **GetFileList(void); virtual void SetDescription( const char * ); virtual CPLErr SetMetadata( char ** papszMetadata, const char * pszDomain = "" ); virtual CPLErr SetMetadataItem( const char * pszName, const char * pszValue, const char * pszDomain = "" ); virtual CPLErr SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, const char *pszGCPProjection ); static GDALDataset *Open( GDALOpenInfo * ); static GDALDataset *Create( const char * pszFilename, int nXSize, int nYSize, int nBands, GDALDataType eType, char ** papszOptions ); }; /************************************************************************/ /* ==================================================================== */ /* ENVIRasterBand */ /* ==================================================================== */ /************************************************************************/ class ENVIRasterBand : public RawRasterBand { public: ENVIRasterBand( GDALDataset *poDS, int nBand, void * fpRaw, vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset, GDALDataType eDataType, int bNativeOrder, int bIsVSIL = FALSE, int bOwnsFP = FALSE ); virtual void SetDescription( const char * ); virtual CPLErr SetCategoryNames( char ** ); }; /************************************************************************/ /* ENVIDataset() */ /************************************************************************/ ENVIDataset::ENVIDataset() { fpImage = NULL; fp = NULL; pszHDRFilename = NULL; pszProjection = CPLStrdup(""); papszHeader = NULL; bFoundMapinfo = FALSE; bHeaderDirty = 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; } /************************************************************************/ /* ~ENVIDataset() */ /************************************************************************/ ENVIDataset::~ENVIDataset() { FlushCache(); if( fpImage ) VSIFCloseL( fpImage ); if( fp ) VSIFCloseL( fp ); CPLFree( pszProjection ); CSLDestroy( papszHeader ); CPLFree(pszHDRFilename); } /************************************************************************/ /* FlushCache() */ /************************************************************************/ void ENVIDataset::FlushCache() { RawDataset::FlushCache(); GDALRasterBand* band = (GetRasterCount() > 0) ? GetRasterBand(1) : NULL; if ( band == NULL || !bHeaderDirty ) return; CPLLocaleC oLocaleEnforcer; // If opening an existing file in Update mode (i.e. "r+") we need to make // sure any existing content is cleared, otherwise the file may contain // trailing content from the previous write. VSIFTruncateL( fp, 0 ); VSIFSeekL( fp, 0, SEEK_SET ); /* -------------------------------------------------------------------- */ /* Rewrite out the header. */ /* -------------------------------------------------------------------- */ int iBigEndian; const char *pszInterleaving; char** catNames; #ifdef CPL_LSB iBigEndian = 0; #else iBigEndian = 1; #endif VSIFPrintfL( fp, "ENVI\n" ); if ("" != sDescription) VSIFPrintfL( fp, "description = {\n%s}\n", sDescription.c_str()); VSIFPrintfL( fp, "samples = %d\nlines = %d\nbands = %d\n", nRasterXSize, nRasterYSize, nBands ); catNames = band->GetCategoryNames(); VSIFPrintfL( fp, "header offset = 0\n"); if (0 == catNames) VSIFPrintfL( fp, "file type = ENVI Standard\n" ); else VSIFPrintfL( fp, "file type = ENVI Classification\n" ); int iENVIType = GetEnviType(band->GetRasterDataType()); VSIFPrintfL( fp, "data type = %d\n", iENVIType ); switch (interleave) { case BIP: pszInterleaving = "bip"; // interleaved by pixel break; case BIL: pszInterleaving = "bil"; // interleaved by line break; case BSQ: pszInterleaving = "bsq"; // band sequental by default break; default: pszInterleaving = "bsq"; break; } VSIFPrintfL( fp, "interleave = %s\n", pszInterleaving); VSIFPrintfL( fp, "byte order = %d\n", iBigEndian ); /* -------------------------------------------------------------------- */ /* Write class and color information */ /* -------------------------------------------------------------------- */ catNames = band->GetCategoryNames(); if (0 != catNames) { int nrClasses = 0; while (*catNames++) ++nrClasses; if (nrClasses > 0) { VSIFPrintfL( fp, "classes = %d\n", nrClasses ); GDALColorTable* colorTable = band->GetColorTable(); if (0 != colorTable) { int nrColors = colorTable->GetColorEntryCount(); if (nrColors > nrClasses) nrColors = nrClasses; VSIFPrintfL( fp, "class lookup = {\n"); for (int i = 0; i < nrColors; ++i) { const GDALColorEntry* color = colorTable->GetColorEntry(i); VSIFPrintfL(fp, "%d, %d, %d", color->c1, color->c2, color->c3); if (i < nrColors - 1) { VSIFPrintfL(fp, ", "); if (0 == (i+1) % 5) VSIFPrintfL(fp, "\n"); } } VSIFPrintfL(fp, "}\n"); } catNames = band->GetCategoryNames(); if (0 != *catNames) { VSIFPrintfL( fp, "class names = {\n%s", *catNames++); int i = 0; while (*catNames) { VSIFPrintfL( fp, ","); if (0 == (++i) % 5) VSIFPrintfL(fp, "\n"); VSIFPrintfL( fp, " %s", *catNames++); } VSIFPrintfL( fp, "}\n"); } } } /* -------------------------------------------------------------------- */ /* Write the rest of header. */ /* -------------------------------------------------------------------- */ // only one map info type should be set // - rpc // - pseudo/gcp // - standard if ( !WriteRpcInfo() ) // are rpcs in the metadata { if ( !WritePseudoGcpInfo() ) // are gcps in the metadata { WriteProjectionInfo(); // standard - affine xform/coord sys str } } VSIFPrintfL( fp, "band names = {\n" ); for ( int i = 1; i <= nBands; i++ ) { CPLString sBandDesc = GetRasterBand( i )->GetDescription(); if ( sBandDesc == "" ) sBandDesc = CPLSPrintf( "Band %d", i ); VSIFPrintfL( fp, "%s", sBandDesc.c_str() ); if ( i != nBands ) VSIFPrintfL( fp, ",\n" ); } VSIFPrintfL( fp, "}\n" ); /* -------------------------------------------------------------------- */ /* Write the metadata that was read into the ENVI domain */ /* -------------------------------------------------------------------- */ char** papszENVIMetadata = GetMetadata("ENVI"); int i; int count = CSLCount(papszENVIMetadata); char **papszTokens; // For every item of metadata in the ENVI domain for (i = 0; i < count; i++) { // Split the entry into two parts at the = character char *pszEntry = papszENVIMetadata[i]; papszTokens = CSLTokenizeString2( pszEntry, "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES); if (CSLCount(papszTokens) != 2) { CPLDebug("ENVI", "Line of header file could not be split at = into two elements: %s", papszENVIMetadata[i]); CSLDestroy( papszTokens ); continue; } // Replace _'s in the string with spaces std::string poKey(papszTokens[0]); std::replace(poKey.begin(), poKey.end(), '_', ' '); // Don't write it out if it is one of the bits of metadata that is written out elsewhere in this routine if (poKey == "description" || poKey == "samples" || poKey == "lines" || poKey == "bands" || poKey == "header offset" || poKey == "file type" || poKey == "data type" || poKey == "interleave" || poKey == "byte order" || poKey == "class names" || poKey == "band names" || poKey == "map info" || poKey == "projection info") { CSLDestroy( papszTokens ); continue; } VSIFPrintfL( fp, "%s = %s\n", poKey.c_str(), papszTokens[1]); CSLDestroy( papszTokens ); } /* Clean dirty flag */ bHeaderDirty = FALSE; } /************************************************************************/ /* GetFileList() */ /************************************************************************/ char **ENVIDataset::GetFileList() { char **papszFileList = NULL; // Main data file, etc. papszFileList = RawDataset::GetFileList(); // Header file. papszFileList = CSLAddString( papszFileList, pszHDRFilename ); // Statistics file if (osStaFilename.size() != 0) papszFileList = CSLAddString( papszFileList, osStaFilename ); return papszFileList; } /************************************************************************/ /* GetEPSGGeogCS() */ /* */ /* Try to establish what the EPSG code for this coordinate */ /* systems GEOGCS might be. Returns -1 if no reasonable guess */ /* can be made. */ /* */ /* TODO: We really need to do some name lookups. */ /************************************************************************/ static int ENVIGetEPSGGeogCS( OGRSpatialReference *poThis ) { const char *pszAuthName = poThis->GetAuthorityName( "GEOGCS" ); /* -------------------------------------------------------------------- */ /* Do we already have it? */ /* -------------------------------------------------------------------- */ if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") ) return atoi(poThis->GetAuthorityCode( "GEOGCS" )); /* -------------------------------------------------------------------- */ /* Get the datum and geogcs names. */ /* -------------------------------------------------------------------- */ const char *pszGEOGCS = poThis->GetAttrValue( "GEOGCS" ); const char *pszDatum = poThis->GetAttrValue( "DATUM" ); // We can only operate on coordinate systems with a geogcs. if( pszGEOGCS == NULL || pszDatum == NULL ) return -1; /* -------------------------------------------------------------------- */ /* Is this a "well known" geographic coordinate system? */ /* -------------------------------------------------------------------- */ int bWGS, bNAD; bWGS = strstr(pszGEOGCS,"WGS") != NULL || strstr(pszDatum, "WGS") || strstr(pszGEOGCS,"World Geodetic System") || strstr(pszGEOGCS,"World_Geodetic_System") || strstr(pszDatum, "World Geodetic System") || strstr(pszDatum, "World_Geodetic_System"); bNAD = strstr(pszGEOGCS,"NAD") != NULL || strstr(pszDatum, "NAD") || strstr(pszGEOGCS,"North American") || strstr(pszGEOGCS,"North_American") || strstr(pszDatum, "North American") || strstr(pszDatum, "North_American"); if( bWGS && (strstr(pszGEOGCS,"84") || strstr(pszDatum,"84")) ) return 4326; if( bWGS && (strstr(pszGEOGCS,"72") || strstr(pszDatum,"72")) ) return 4322; if( bNAD && (strstr(pszGEOGCS,"83") || strstr(pszDatum,"83")) ) return 4269; if( bNAD && (strstr(pszGEOGCS,"27") || strstr(pszDatum,"27")) ) return 4267; /* -------------------------------------------------------------------- */ /* If we know the datum, associate the most likely GCS with */ /* it. */ /* -------------------------------------------------------------------- */ pszAuthName = poThis->GetAuthorityName( "GEOGCS|DATUM" ); if( pszAuthName != NULL && EQUAL(pszAuthName,"epsg") && poThis->GetPrimeMeridian() == 0.0 ) { int nDatum = atoi(poThis->GetAuthorityCode("GEOGCS|DATUM")); if( nDatum >= 6000 && nDatum <= 6999 ) return nDatum - 2000; } return -1; } /************************************************************************/ /* WriteProjectionInfo() */ /************************************************************************/ void ENVIDataset::WriteProjectionInfo() { /* -------------------------------------------------------------------- */ /* Format the location (geotransform) portion of the map info */ /* line. */ /* -------------------------------------------------------------------- */ CPLString osLocation; osLocation.Printf( "1, 1, %.15g, %.15g, %.15g, %.15g", adfGeoTransform[0], adfGeoTransform[3], adfGeoTransform[1], fabs(adfGeoTransform[5]) ); /* -------------------------------------------------------------------- */ /* Minimal case - write out simple geotransform if we have a */ /* non-default geotransform. */ /* -------------------------------------------------------------------- */ if( pszProjection == NULL || strlen(pszProjection) == 0 || (strlen(pszProjection) >= 8 && strncmp(pszProjection, "LOCAL_CS", 8) == 0 ) ) { if( adfGeoTransform[0] != 0.0 || adfGeoTransform[1] != 1.0 || adfGeoTransform[2] != 0.0 || adfGeoTransform[3] != 0.0 || adfGeoTransform[4] != 0.0 || adfGeoTransform[5] != 1.0 ) { const char* pszHemisphere = "North"; VSIFPrintfL( fp, "map info = {Arbitrary, %s, %d, %s}\n", osLocation.c_str(), 0, pszHemisphere); } return; } /* -------------------------------------------------------------------- */ /* Ingest WKT. */ /* -------------------------------------------------------------------- */ OGRSpatialReference oSRS; char *pszProj = pszProjection; if( oSRS.importFromWkt( &pszProj ) != OGRERR_NONE ) return; /* -------------------------------------------------------------------- */ /* Try to translate the datum and get major/minor ellipsoid */ /* values. */ /* -------------------------------------------------------------------- */ int nEPSG_GCS = ENVIGetEPSGGeogCS( &oSRS ); CPLString osDatum, osCommaDatum; double dfA, dfB; if( nEPSG_GCS == 4326 ) osDatum = "WGS-84"; else if( nEPSG_GCS == 4322 ) osDatum = "WGS-72"; else if( nEPSG_GCS == 4269 ) osDatum = "North America 1983"; else if( nEPSG_GCS == 4267 ) osDatum = "North America 1927"; else if( nEPSG_GCS == 4230 ) osDatum = "European 1950"; else if( nEPSG_GCS == 4277 ) osDatum = "Ordnance Survey of Great Britain '36"; else if( nEPSG_GCS == 4291 ) osDatum = "SAD-69/Brazil"; else if( nEPSG_GCS == 4283 ) osDatum = "Geocentric Datum of Australia 1994"; else if( nEPSG_GCS == 4275 ) osDatum = "Nouvelle Triangulation Francaise IGN"; if( osDatum != "" ) osCommaDatum.Printf( ",%s", osDatum.c_str() ); dfA = oSRS.GetSemiMajor(); dfB = oSRS.GetSemiMinor(); /* -------------------------------------------------------------------- */ /* Do we have unusual linear units? */ /* -------------------------------------------------------------------- */ CPLString osOptionalUnits; if( fabs(oSRS.GetLinearUnits()-0.3048) < 0.0001 ) osOptionalUnits = ", units=Feet"; /* -------------------------------------------------------------------- */ /* Handle UTM case. */ /* -------------------------------------------------------------------- */ const char *pszHemisphere; const char *pszProjName = oSRS.GetAttrValue("PROJECTION"); int bNorth; int iUTMZone; iUTMZone = oSRS.GetUTMZone( &bNorth ); if ( iUTMZone ) { if ( bNorth ) pszHemisphere = "North"; else pszHemisphere = "South"; VSIFPrintfL( fp, "map info = {UTM, %s, %d, %s%s%s}\n", osLocation.c_str(), iUTMZone, pszHemisphere, osCommaDatum.c_str(), osOptionalUnits.c_str() ); } else if( oSRS.IsGeographic() ) { VSIFPrintfL( fp, "map info = {Geographic Lat/Lon, %s%s}\n", osLocation.c_str(), osCommaDatum.c_str()); } else if( pszProjName == NULL ) { // what to do? } else if( EQUAL(pszProjName,SRS_PT_NEW_ZEALAND_MAP_GRID) ) { VSIFPrintfL( fp, "map info = {New Zealand Map Grid, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {39, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, New Zealand Map Grid}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_TRANSVERSE_MERCATOR) ) { VSIFPrintfL( fp, "map info = {Transverse Mercator, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {3, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Transverse Mercator}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) || EQUAL(pszProjName,SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP_BELGIUM) ) { VSIFPrintfL( fp, "map info = {Lambert Conformal Conic, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {4, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Lambert Conformal Conic}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName, SRS_PT_HOTINE_OBLIQUE_MERCATOR_TWO_POINT_NATURAL_ORIGIN) ) { VSIFPrintfL( fp, "map info = {Hotine Oblique Mercator A, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {5, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Hotine Oblique Mercator A}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_1,0.0), oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_1,0.0), oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_POINT_2,0.0), oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_POINT_2,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_HOTINE_OBLIQUE_MERCATOR) ) { VSIFPrintfL( fp, "map info = {Hotine Oblique Mercator B, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {6, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Hotine Oblique Mercator B}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_AZIMUTH,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_STEREOGRAPHIC) || EQUAL(pszProjName,SRS_PT_OBLIQUE_STEREOGRAPHIC) ) { VSIFPrintfL( fp, "map info = {Stereographic (ellipsoid), %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {7, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %s, Stereographic (ellipsoid)}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR,1.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_ALBERS_CONIC_EQUAL_AREA) ) { VSIFPrintfL( fp, "map info = {Albers Conical Equal Area, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {9, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Albers Conical Equal Area}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1,0.0), oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2,0.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_POLYCONIC) ) { VSIFPrintfL( fp, "map info = {Polyconic, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {10, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Polyconic}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA) ) { VSIFPrintfL( fp, "map info = {Lambert Azimuthal Equal Area, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {11, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Lambert Azimuthal Equal Area}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_AZIMUTHAL_EQUIDISTANT) ) { VSIFPrintfL( fp, "map info = {Azimuthal Equadistant, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {12, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Azimuthal Equadistant}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,0.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), osCommaDatum.c_str() ); } else if( EQUAL(pszProjName,SRS_PT_POLAR_STEREOGRAPHIC) ) { VSIFPrintfL( fp, "map info = {Polar Stereographic, %s%s%s}\n", osLocation.c_str(), osCommaDatum.c_str(), osOptionalUnits.c_str() ); VSIFPrintfL( fp, "projection info = {31, %.16g, %.16g, %.16g, %.16g, %.16g, %.16g%s, Polar Stereographic}\n", dfA, dfB, oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN,90.0), oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING,0.0), oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING,0.0), osCommaDatum.c_str() ); } else { VSIFPrintfL( fp, "map info = {%s, %s}\n", pszProjName, osLocation.c_str()); } // write out coordinate system string if ( oSRS.morphToESRI() == OGRERR_NONE ) { char *pszProjESRI = NULL; if ( oSRS.exportToWkt(&pszProjESRI) == OGRERR_NONE ) { if ( strlen(pszProjESRI) ) VSIFPrintfL( fp, "coordinate system string = {%s}\n", pszProjESRI); } CPLFree(pszProjESRI); pszProjESRI = NULL; } } /************************************************************************/ /* ParseRpcCoeffsMetaDataString() */ /************************************************************************/ int ENVIDataset::ParseRpcCoeffsMetaDataString(const char *psName, char **papszVal, int& idx) { // separate one string with 20 coefficients into an array of 20 strings. const char *psz20Vals = GetMetadataItem(psName, "RPC"); if (!psz20Vals) return FALSE; char** papszArr = CSLTokenizeString2(psz20Vals, " ", 0); if (!papszArr) return FALSE; int x = 0; while ((papszArr[x] != NULL) && (x < 20)) { papszVal[idx++] = CPLStrdup(papszArr[x]); x++; } CSLDestroy(papszArr); return (x == 20); } #define CPLStrdupIfNotNull(x) ((x) ? CPLStrdup(x) : NULL) /************************************************************************/ /* WriteRpcInfo() */ /************************************************************************/ int ENVIDataset::WriteRpcInfo() { // write out 90 rpc coeffs into the envi header plus 3 envi specific rpc values // returns 0 if the coeffs are not present or not valid int bRet = FALSE; int x, idx = 0; char* papszVal[93]; papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LINE_OFF", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("SAMP_OFF", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LAT_OFF", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LONG_OFF", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("HEIGHT_OFF", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LINE_SCALE", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("SAMP_SCALE", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LAT_SCALE", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("LONG_SCALE", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("HEIGHT_SCALE", "RPC")); for (x=0; x<10; x++) // if we do not have 10 values we return 0 { if (!papszVal[x]) goto end; } if (!ParseRpcCoeffsMetaDataString("LINE_NUM_COEFF", papszVal, idx)) goto end; if (!ParseRpcCoeffsMetaDataString("LINE_DEN_COEFF", papszVal, idx)) goto end; if (!ParseRpcCoeffsMetaDataString("SAMP_NUM_COEFF", papszVal, idx)) goto end; if (!ParseRpcCoeffsMetaDataString("SAMP_DEN_COEFF", papszVal, idx)) goto end; papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("TILE_ROW_OFFSET", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("TILE_COL_OFFSET", "RPC")); papszVal[idx++] = CPLStrdupIfNotNull(GetMetadataItem("ENVI_RPC_EMULATION", "RPC")); CPLAssert(idx == 93); for (x=90; x<93; x++) { if (!papszVal[x]) goto end; } // ok all the needed 93 values are present so write the rpcs into the envi header x = 1; VSIFPrintfL(fp, "rpc info = {\n"); for (int iR=0; iR<93; iR++) { if (papszVal[iR][0] == '-') VSIFPrintfL(fp, " %s", papszVal[iR]); else VSIFPrintfL(fp, " %s", papszVal[iR]); if (iR<92) VSIFPrintfL(fp, ","); if ((x % 4) == 0) VSIFPrintfL(fp, "\n"); x++; if (x > 4) x = 1; } VSIFPrintfL(fp, "}\n" ); bRet = TRUE; end: for (x=0;x<idx;x++) CPLFree(papszVal[x]); return bRet; } /************************************************************************/ /* WritePseudoGcpInfo() */ /************************************************************************/ int ENVIDataset::WritePseudoGcpInfo() { // write out gcps into the envi header // returns 0 if the gcps are not present int iNum = GetGCPCount(); if (iNum == 0) return FALSE; const GDAL_GCP *pGcpStructs = GetGCPs(); // double dfGCPPixel; /** Pixel (x) location of GCP on raster */ // double dfGCPLine; /** Line (y) location of GCP on raster */ // double dfGCPX; /** X position of GCP in georeferenced space */ // double dfGCPY; /** Y position of GCP in georeferenced space */ VSIFPrintfL(fp, "geo points = {\n"); for (int iR=0; iR<iNum; iR++) { VSIFPrintfL(fp, " %#0.4f, %#0.4f, %#0.8f, %#0.8f", pGcpStructs[iR].dfGCPPixel, pGcpStructs[iR].dfGCPLine, pGcpStructs[iR].dfGCPY, pGcpStructs[iR].dfGCPX); if (iR<iNum-1) VSIFPrintfL(fp, ",\n"); } VSIFPrintfL(fp, "}\n" ); return TRUE; } /************************************************************************/ /* GetProjectionRef() */ /************************************************************************/ const char *ENVIDataset::GetProjectionRef() { return pszProjection; } /************************************************************************/ /* SetProjection() */ /************************************************************************/ CPLErr ENVIDataset::SetProjection( const char *pszNewProjection ) { CPLFree( pszProjection ); pszProjection = CPLStrdup( pszNewProjection ); bHeaderDirty = TRUE; return CE_None; } /************************************************************************/ /* GetGeoTransform() */ /************************************************************************/ CPLErr ENVIDataset::GetGeoTransform( double * padfTransform ) { memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); if( bFoundMapinfo ) return CE_None; else return CE_Failure; } /************************************************************************/ /* SetGeoTransform() */ /************************************************************************/ CPLErr ENVIDataset::SetGeoTransform( double * padfTransform ) { memcpy( adfGeoTransform, padfTransform, sizeof(double) * 6 ); bHeaderDirty = TRUE; bFoundMapinfo = TRUE; return CE_None; } /************************************************************************/ /* SetDescription() */ /************************************************************************/ void ENVIDataset::SetDescription( const char * pszDescription ) { bHeaderDirty = TRUE; RawDataset::SetDescription(pszDescription); } /************************************************************************/ /* SetMetadata() */ /************************************************************************/ CPLErr ENVIDataset::SetMetadata( char ** papszMetadata, const char * pszDomain ) { if( pszDomain && (EQUAL(pszDomain, "RPC") || EQUAL(pszDomain, "ENVI")) ) { bHeaderDirty = TRUE; } return RawDataset::SetMetadata(papszMetadata, pszDomain); } /************************************************************************/ /* SetMetadataItem() */ /************************************************************************/ CPLErr ENVIDataset::SetMetadataItem( const char * pszName, const char * pszValue, const char * pszDomain ) { if( pszDomain && (EQUAL(pszDomain, "RPC") || EQUAL(pszDomain, "ENVI")) ) { bHeaderDirty = TRUE; } return RawDataset::SetMetadataItem(pszName, pszValue, pszDomain); } /************************************************************************/ /* SetGCPs() */ /************************************************************************/ CPLErr ENVIDataset::SetGCPs( int nGCPCount, const GDAL_GCP *pasGCPList, const char *pszGCPProjection ) { bHeaderDirty = TRUE; return RawDataset::SetGCPs(nGCPCount, pasGCPList, pszGCPProjection); } /************************************************************************/ /* SplitList() */ /* */ /* Split an ENVI value list into component fields, and strip */ /* white space. */ /************************************************************************/ char **ENVIDataset::SplitList( const char *pszCleanInput ) { char **papszReturn = NULL; char *pszInput = CPLStrdup(pszCleanInput); if( pszInput[0] != '{' ) { CPLFree(pszInput); return NULL; } int iChar=1; while( pszInput[iChar] != '}' && pszInput[iChar] != '\0' ) { int iFStart=-1, iFEnd=-1; // Find start of token. iFStart = iChar; while( pszInput[iFStart] == ' ' ) iFStart++; iFEnd = iFStart; while( pszInput[iFEnd] != ',' && pszInput[iFEnd] != '}' && pszInput[iFEnd] != '\0' ) iFEnd++; if( pszInput[iFEnd] == '\0' ) break; iChar = iFEnd + 1; iFEnd = iFEnd - 1; while( iFEnd > iFStart && pszInput[iFEnd] == ' ' ) iFEnd--; pszInput[iFEnd + 1] = '\0'; papszReturn = CSLAddString( papszReturn, pszInput + iFStart ); } CPLFree( pszInput ); return papszReturn; } /************************************************************************/ /* SetENVIDatum() */ /************************************************************************/ void ENVIDataset::SetENVIDatum( OGRSpatialReference *poSRS, const char *pszENVIDatumName ) { // datums if( EQUAL(pszENVIDatumName, "WGS-84") ) poSRS->SetWellKnownGeogCS( "WGS84" ); else if( EQUAL(pszENVIDatumName, "WGS-72") ) poSRS->SetWellKnownGeogCS( "WGS72" ); else if( EQUAL(pszENVIDatumName, "North America 1983") ) poSRS->SetWellKnownGeogCS( "NAD83" ); else if( EQUAL(pszENVIDatumName, "North America 1927") || strstr(pszENVIDatumName,"NAD27") || strstr(pszENVIDatumName,"NAD-27") ) poSRS->SetWellKnownGeogCS( "NAD27" ); else if( EQUALN(pszENVIDatumName, "European 1950",13) ) poSRS->SetWellKnownGeogCS( "EPSG:4230" ); else if( EQUAL(pszENVIDatumName, "Ordnance Survey of Great Britain '36") ) poSRS->SetWellKnownGeogCS( "EPSG:4277" ); else if( EQUAL(pszENVIDatumName, "SAD-69/Brazil") ) poSRS->SetWellKnownGeogCS( "EPSG:4291" ); else if( EQUAL(pszENVIDatumName, "Geocentric Datum of Australia 1994") ) poSRS->SetWellKnownGeogCS( "EPSG:4283" ); else if( EQUAL(pszENVIDatumName, "Australian Geodetic 1984") ) poSRS->SetWellKnownGeogCS( "EPSG:4203" ); else if( EQUAL(pszENVIDatumName, "Nouvelle Triangulation Francaise IGN") ) poSRS->SetWellKnownGeogCS( "EPSG:4275" ); // Ellipsoids else if( EQUAL(pszENVIDatumName, "GRS 80") ) poSRS->SetWellKnownGeogCS( "NAD83" ); else if( EQUAL(pszENVIDatumName, "Airy") ) poSRS->SetWellKnownGeogCS( "EPSG:4001" ); else if( EQUAL(pszENVIDatumName, "Australian National") ) poSRS->SetWellKnownGeogCS( "EPSG:4003" ); else if( EQUAL(pszENVIDatumName, "Bessel 1841") ) poSRS->SetWellKnownGeogCS( "EPSG:4004" ); else if( EQUAL(pszENVIDatumName, "Clark 1866") ) poSRS->SetWellKnownGeogCS( "EPSG:4008" ); else { CPLError( CE_Warning, CPLE_AppDefined, "Unrecognised datum '%s', defaulting to WGS84.", pszENVIDatumName); poSRS->SetWellKnownGeogCS( "WGS84" ); } } /************************************************************************/ /* SetENVIEllipse() */ /************************************************************************/ void ENVIDataset::SetENVIEllipse( OGRSpatialReference *poSRS, char **papszPI_EI ) { double dfA = CPLAtofM(papszPI_EI[0]); double dfB = CPLAtofM(papszPI_EI[1]); double dfInvF; if( fabs(dfA-dfB) < 0.1 ) dfInvF = 0.0; // sphere else dfInvF = dfA / (dfA - dfB); poSRS->SetGeogCS( "Ellipse Based", "Ellipse Based", "Unnamed", dfA, dfInvF ); } /************************************************************************/ /* ProcessMapinfo() */ /* */ /* Extract projection, and geotransform from a mapinfo value in */ /* the header. */ /************************************************************************/ int ENVIDataset::ProcessMapinfo( const char *pszMapinfo ) { char **papszFields; int nCount; OGRSpatialReference oSRS; papszFields = SplitList( pszMapinfo ); nCount = CSLCount(papszFields); if( nCount < 7 ) { CSLDestroy( papszFields ); return FALSE; } /* -------------------------------------------------------------------- */ /* Check if we have coordinate system string, and if so parse it. */ /* -------------------------------------------------------------------- */ char **papszCSS = NULL; if( CSLFetchNameValue( papszHeader, "coordinate_system_string" ) != NULL ) { papszCSS = CSLTokenizeString2( CSLFetchNameValue( papszHeader, "coordinate_system_string" ), "{}", CSLT_PRESERVEQUOTES ); } /* -------------------------------------------------------------------- */ /* Check if we have projection info, and if so parse it. */ /* -------------------------------------------------------------------- */ char **papszPI = NULL; int nPICount = 0; if( CSLFetchNameValue( papszHeader, "projection_info" ) != NULL ) { papszPI = SplitList( CSLFetchNameValue( papszHeader, "projection_info" ) ); nPICount = CSLCount(papszPI); } /* -------------------------------------------------------------------- */ /* Capture geotransform. */ /* -------------------------------------------------------------------- */ adfGeoTransform[1] = atof(papszFields[5]); // Pixel width adfGeoTransform[5] = -atof(papszFields[6]); // Pixel height adfGeoTransform[0] = // Upper left X coordinate atof(papszFields[3]) - (atof(papszFields[1]) - 1) * adfGeoTransform[1]; adfGeoTransform[3] = // Upper left Y coordinate atof(papszFields[4]) - (atof(papszFields[2]) - 1) * adfGeoTransform[5]; adfGeoTransform[2] = 0.0; adfGeoTransform[4] = 0.0; /* -------------------------------------------------------------------- */ /* Capture projection. */ /* -------------------------------------------------------------------- */ if ( oSRS.importFromESRI( papszCSS ) != OGRERR_NONE ) { oSRS.Clear(); if( EQUALN(papszFields[0],"UTM",3) && nCount >= 9 ) { oSRS.SetUTM( atoi(papszFields[7]), !EQUAL(papszFields[8],"South") ); if( nCount >= 10 && strstr(papszFields[9],"=") == NULL ) SetENVIDatum( &oSRS, papszFields[9] ); else oSRS.SetWellKnownGeogCS( "NAD27" ); } else if( EQUALN(papszFields[0],"State Plane (NAD 27)",19) && nCount >= 7 ) { oSRS.SetStatePlane( ITTVISToUSGSZone(atoi(papszFields[7])), FALSE ); } else if( EQUALN(papszFields[0],"State Plane (NAD 83)",19) && nCount >= 7 ) { oSRS.SetStatePlane( ITTVISToUSGSZone(atoi(papszFields[7])), TRUE ); } else if( EQUALN(papszFields[0],"Geographic Lat",14) && nCount >= 8 ) { if( nCount >= 8 && strstr(papszFields[7],"=") == NULL ) SetENVIDatum( &oSRS, papszFields[7] ); else oSRS.SetWellKnownGeogCS( "WGS84" ); } else if( nPICount > 8 && atoi(papszPI[0]) == 3 ) // TM { oSRS.SetTM( CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[7]), CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); } else if( nPICount > 8 && atoi(papszPI[0]) == 4 ) // Lambert Conformal Conic { oSRS.SetLCC( CPLAtofM(papszPI[7]), CPLAtofM(papszPI[8]), CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); } else if( nPICount > 10 && atoi(papszPI[0]) == 5 ) // Oblique Merc (2 point) { oSRS.SetHOM2PNO( CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]), CPLAtofM(papszPI[7]), CPLAtofM(papszPI[10]), CPLAtofM(papszPI[8]), CPLAtofM(papszPI[9]) ); } else if( nPICount > 8 && atoi(papszPI[0]) == 6 ) // Oblique Merc { oSRS.SetHOM(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]), 0.0, CPLAtofM(papszPI[8]), CPLAtofM(papszPI[6]), CPLAtofM(papszPI[7]) ); } else if( nPICount > 8 && atoi(papszPI[0]) == 7 ) // Stereographic { oSRS.SetStereographic( CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[7]), CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); } else if( nPICount > 8 && atoi(papszPI[0]) == 9 ) // Albers Equal Area { oSRS.SetACEA( CPLAtofM(papszPI[7]), CPLAtofM(papszPI[8]), CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); } else if( nPICount > 6 && atoi(papszPI[0]) == 10 ) // Polyconic { oSRS.SetPolyconic(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); } else if( nPICount > 6 && atoi(papszPI[0]) == 11 ) // LAEA { oSRS.SetLAEA(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); } else if( nPICount > 6 && atoi(papszPI[0]) == 12 ) // Azimuthal Equid. { oSRS.SetAE(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); } else if( nPICount > 6 && atoi(papszPI[0]) == 31 ) // Polar Stereographic { oSRS.SetPS(CPLAtofM(papszPI[3]), CPLAtofM(papszPI[4]), 1.0, CPLAtofM(papszPI[5]), CPLAtofM(papszPI[6]) ); } } CSLDestroy( papszCSS ); // Still lots more that could be added for someone with the patience. /* -------------------------------------------------------------------- */ /* fallback to localcs if we don't recognise things. */ /* -------------------------------------------------------------------- */ if( oSRS.GetRoot() == NULL ) oSRS.SetLocalCS( papszFields[0] ); /* -------------------------------------------------------------------- */ /* Try to set datum from projection info line if we have a */ /* projected coordinate system without a GEOGCS. */ /* -------------------------------------------------------------------- */ if( oSRS.IsProjected() && oSRS.GetAttrNode("GEOGCS") == NULL && nPICount > 3 ) { // Do we have a datum on the projection info line? int iDatum = nPICount-1; // Ignore units= items. if( strstr(papszPI[iDatum],"=") != NULL ) iDatum--; // Skip past the name. iDatum--; CPLString osDatumName = papszPI[iDatum]; if( osDatumName.find_first_of("abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ") != CPLString::npos ) { SetENVIDatum( &oSRS, osDatumName ); } else { SetENVIEllipse( &oSRS, papszPI + 1 ); } } /* -------------------------------------------------------------------- */ /* Try to process specialized units. */ /* -------------------------------------------------------------------- */ if( EQUALN( papszFields[nCount-1],"units",5)) { /* Handle linear units first. */ if (EQUAL(papszFields[nCount-1],"units=Feet") ) oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_FOOT, atof(SRS_UL_FOOT_CONV) ); else if (EQUAL(papszFields[nCount-1],"units=Meters") ) oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_METER, 1. ); else if (EQUAL(papszFields[nCount-1],"units=Km") ) oSRS.SetLinearUnitsAndUpdateParameters( "Kilometer", 1000. ); else if (EQUAL(papszFields[nCount-1],"units=Yards") ) oSRS.SetLinearUnitsAndUpdateParameters( "Yard", .9144 ); else if (EQUAL(papszFields[nCount-1],"units=Miles") ) oSRS.SetLinearUnitsAndUpdateParameters( "Mile", 1609.344 ); else if (EQUAL(papszFields[nCount-1],"units=Nautical Miles") ) oSRS.SetLinearUnitsAndUpdateParameters( SRS_UL_NAUTICAL_MILE, atof(SRS_UL_NAUTICAL_MILE_CONV) ); /* Only handle angular units if we know the projection is geographic. */ if (oSRS.IsGeographic()) { if (EQUAL(papszFields[nCount-1],"units=Radians") ) oSRS.SetAngularUnits( SRS_UA_RADIAN, 1. ); else { /* Degrees, minutes and seconds will all be represented as degrees. */ oSRS.SetAngularUnits( SRS_UA_DEGREE, atof(SRS_UA_DEGREE_CONV)); double conversionFactor = 1.; if (EQUAL(papszFields[nCount-1],"units=Minutes") ) conversionFactor = 60.; else if( EQUAL(papszFields[nCount-1],"units=Seconds") ) conversionFactor = 3600.; adfGeoTransform[0] /= conversionFactor; adfGeoTransform[1] /= conversionFactor; adfGeoTransform[2] /= conversionFactor; adfGeoTransform[3] /= conversionFactor; adfGeoTransform[4] /= conversionFactor; adfGeoTransform[5] /= conversionFactor; } } } /* -------------------------------------------------------------------- */ /* Turn back into WKT. */ /* -------------------------------------------------------------------- */ if( oSRS.GetRoot() != NULL ) { oSRS.Fixup(); if ( pszProjection ) { CPLFree( pszProjection ); pszProjection = NULL; } oSRS.exportToWkt( &pszProjection ); } CSLDestroy( papszFields ); CSLDestroy( papszPI ); return TRUE; } /************************************************************************/ /* ProcessRPCinfo() */ /* */ /* Extract RPC transformation coefficients if they are present */ /* and sets into the standard metadata fields for RPC. */ /************************************************************************/ void ENVIDataset::ProcessRPCinfo( const char *pszRPCinfo, int numCols, int numRows) { char **papszFields; char sVal[1280]; int nCount; papszFields = SplitList( pszRPCinfo ); nCount = CSLCount(papszFields); if( nCount < 90 ) { CSLDestroy( papszFields ); return; } snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[0])); SetMetadataItem("LINE_OFF",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[5])); SetMetadataItem("LINE_SCALE",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[1])); SetMetadataItem("SAMP_OFF",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[6])); SetMetadataItem("SAMP_SCALE",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[2])); SetMetadataItem("LAT_OFF",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[7])); SetMetadataItem("LAT_SCALE",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[3])); SetMetadataItem("LONG_OFF",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[8])); SetMetadataItem("LONG_SCALE",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[4])); SetMetadataItem("HEIGHT_OFF",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g",atof(papszFields[9])); SetMetadataItem("HEIGHT_SCALE",sVal,"RPC"); sVal[0] = '\0'; int i; for(i = 0; i < 20; i++ ) snprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ", atof(papszFields[10+i])); SetMetadataItem("LINE_NUM_COEFF",sVal,"RPC"); sVal[0] = '\0'; for(i = 0; i < 20; i++ ) snprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ", atof(papszFields[30+i])); SetMetadataItem("LINE_DEN_COEFF",sVal,"RPC"); sVal[0] = '\0'; for(i = 0; i < 20; i++ ) snprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ", atof(papszFields[50+i])); SetMetadataItem("SAMP_NUM_COEFF",sVal,"RPC"); sVal[0] = '\0'; for(i = 0; i < 20; i++ ) snprintf(sVal+strlen(sVal), sizeof(sVal), "%.16g ", atof(papszFields[70+i])); SetMetadataItem("SAMP_DEN_COEFF",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g", atof(papszFields[3]) - atof(papszFields[8])); SetMetadataItem("MIN_LONG",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g", atof(papszFields[3]) + atof(papszFields[8]) ); SetMetadataItem("MAX_LONG",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g", atof(papszFields[2]) - atof(papszFields[7])); SetMetadataItem("MIN_LAT",sVal,"RPC"); snprintf(sVal, sizeof(sVal), "%.16g", atof(papszFields[2]) + atof(papszFields[7])); SetMetadataItem("MAX_LAT",sVal,"RPC"); if (nCount == 93) { SetMetadataItem("TILE_ROW_OFFSET",papszFields[90],"RPC"); SetMetadataItem("TILE_COL_OFFSET",papszFields[91],"RPC"); SetMetadataItem("ENVI_RPC_EMULATION",papszFields[92],"RPC"); } /* Handle the chipping case where the image is a subset. */ double rowOffset, colOffset; rowOffset = (nCount == 93) ? atof(papszFields[90]) : 0; colOffset = (nCount == 93) ? atof(papszFields[91]) : 0; if (rowOffset || colOffset) { SetMetadataItem("ICHIP_SCALE_FACTOR", "1"); SetMetadataItem("ICHIP_ANAMORPH_CORR", "0"); SetMetadataItem("ICHIP_SCANBLK_NUM", "0"); SetMetadataItem("ICHIP_OP_ROW_11", "0.5"); SetMetadataItem("ICHIP_OP_COL_11", "0.5"); SetMetadataItem("ICHIP_OP_ROW_12", "0.5"); SetMetadataItem("ICHIP_OP_COL_21", "0.5"); snprintf(sVal, sizeof(sVal), "%.16g", numCols - 0.5); SetMetadataItem("ICHIP_OP_COL_12", sVal); SetMetadataItem("ICHIP_OP_COL_22", sVal); snprintf(sVal, sizeof(sVal), "%.16g", numRows - 0.5); SetMetadataItem("ICHIP_OP_ROW_21", sVal); SetMetadataItem("ICHIP_OP_ROW_22", sVal); snprintf(sVal, sizeof(sVal), "%.16g", rowOffset + 0.5); SetMetadataItem("ICHIP_FI_ROW_11", sVal); SetMetadataItem("ICHIP_FI_ROW_12", sVal); snprintf(sVal, sizeof(sVal), "%.16g", colOffset + 0.5); SetMetadataItem("ICHIP_FI_COL_11", sVal); SetMetadataItem("ICHIP_FI_COL_21", sVal); snprintf(sVal, sizeof(sVal), "%.16g", colOffset + numCols - 0.5); SetMetadataItem("ICHIP_FI_COL_12", sVal); SetMetadataItem("ICHIP_FI_COL_22", sVal); snprintf(sVal, sizeof(sVal), "%.16g", rowOffset + numRows - 0.5); SetMetadataItem("ICHIP_FI_ROW_21", sVal); SetMetadataItem("ICHIP_FI_ROW_22", sVal); } CSLDestroy(papszFields); } void ENVIDataset::ProcessStatsFile() { VSILFILE *fpStaFile; osStaFilename = CPLResetExtension( pszHDRFilename, "sta" ); fpStaFile = VSIFOpenL( osStaFilename, "rb" ); if (!fpStaFile) { osStaFilename = ""; return; } int lTestHeader[10],lOffset; if( VSIFReadL( lTestHeader, sizeof(int), 10, fpStaFile ) != 10 ) { VSIFCloseL( fpStaFile ); osStaFilename = ""; return; } int isFloat; isFloat = (byteSwapInt(lTestHeader[0]) == 1111838282); int nb,i; float * fStats; double * dStats, dMin, dMax, dMean, dStd; nb=byteSwapInt(lTestHeader[3]); if (nb < 0 || nb > nBands) { CPLDebug("ENVI", ".sta file has statistics for %d bands, " "whereas the dataset has only %d bands", nb, nBands); nb = nBands; } VSIFSeekL(fpStaFile,40+(nb+1)*4,SEEK_SET); if (VSIFReadL(&lOffset,sizeof(int),1,fpStaFile) == 1) { VSIFSeekL(fpStaFile,40+(nb+1)*8+byteSwapInt(lOffset)+nb,SEEK_SET); // This should be the beginning of the statistics if (isFloat) { fStats = (float*)CPLCalloc(nb*4,4); if ((int)VSIFReadL(fStats,4,nb*4,fpStaFile) == nb*4) { for (i=0;i<nb;i++) { GetRasterBand(i+1)->SetStatistics( byteSwapFloat(fStats[i]), byteSwapFloat(fStats[nb+i]), byteSwapFloat(fStats[2*nb+i]), byteSwapFloat(fStats[3*nb+i])); } } CPLFree(fStats); } else { dStats = (double*)CPLCalloc(nb*4,8); if ((int)VSIFReadL(dStats,8,nb*4,fpStaFile) == nb*4) { for (i=0;i<nb;i++) { dMin = byteSwapDouble(dStats[i]); dMax = byteSwapDouble(dStats[nb+i]); dMean = byteSwapDouble(dStats[2*nb+i]); dStd = byteSwapDouble(dStats[3*nb+i]); if (dMin != dMax && dStd != 0) GetRasterBand(i+1)->SetStatistics(dMin,dMax,dMean,dStd); } } CPLFree(dStats); } } VSIFCloseL( fpStaFile ); } int ENVIDataset::byteSwapInt(int swapMe) { CPL_MSBPTR32(&swapMe); return swapMe; } float ENVIDataset::byteSwapFloat(float swapMe) { CPL_MSBPTR32(&swapMe); return swapMe; } double ENVIDataset::byteSwapDouble(double swapMe) { CPL_MSBPTR64(&swapMe); return swapMe; } /************************************************************************/ /* ReadHeader() */ /************************************************************************/ int ENVIDataset::ReadHeader( VSILFILE * fpHdr ) { CPLReadLineL( fpHdr ); /* -------------------------------------------------------------------- */ /* Now start forming sets of name/value pairs. */ /* -------------------------------------------------------------------- */ while( TRUE ) { const char *pszNewLine; char *pszWorkingLine; pszNewLine = CPLReadLineL( fpHdr ); if( pszNewLine == NULL ) break; if( strstr(pszNewLine,"=") == NULL ) continue; pszWorkingLine = CPLStrdup(pszNewLine); // Collect additional lines if we have open sqiggly bracket. if( strstr(pszWorkingLine,"{") != NULL && strstr(pszWorkingLine,"}") == NULL ) { do { pszNewLine = CPLReadLineL( fpHdr ); if( pszNewLine ) { pszWorkingLine = (char *) CPLRealloc(pszWorkingLine, strlen(pszWorkingLine)+strlen(pszNewLine)+1); strcat( pszWorkingLine, pszNewLine ); } } while( pszNewLine != NULL && strstr(pszNewLine,"}") == NULL ); } // Try to break input into name and value portions. Trim whitespace. const char *pszValue; int iEqual; for( iEqual = 0; pszWorkingLine[iEqual] != '\0' && pszWorkingLine[iEqual] != '='; iEqual++ ) {} if( pszWorkingLine[iEqual] == '=' ) { int i; pszValue = pszWorkingLine + iEqual + 1; while( *pszValue == ' ' || *pszValue == '\t' ) pszValue++; pszWorkingLine[iEqual--] = '\0'; while( iEqual > 0 && (pszWorkingLine[iEqual] == ' ' || pszWorkingLine[iEqual] == '\t') ) pszWorkingLine[iEqual--] = '\0'; // Convert spaces in the name to underscores. for( i = 0; pszWorkingLine[i] != '\0'; i++ ) { if( pszWorkingLine[i] == ' ' ) pszWorkingLine[i] = '_'; } papszHeader = CSLSetNameValue( papszHeader, pszWorkingLine, pszValue ); } CPLFree( pszWorkingLine ); } return TRUE; } /************************************************************************/ /* Open() */ /************************************************************************/ GDALDataset *ENVIDataset::Open( GDALOpenInfo * poOpenInfo ) { int i; /* -------------------------------------------------------------------- */ /* We assume the user is pointing to the binary (ie. .bil) file. */ /* -------------------------------------------------------------------- */ if( poOpenInfo->nHeaderBytes < 2 ) return NULL; /* -------------------------------------------------------------------- */ /* Do we have a .hdr file? Try upper and lower case, and */ /* replacing the extension as well as appending the extension */ /* to whatever we currently have. */ /* -------------------------------------------------------------------- */ const char *pszMode; CPLString osHdrFilename; VSILFILE *fpHeader = NULL; if( poOpenInfo->eAccess == GA_Update ) pszMode = "r+"; else pszMode = "r"; if (poOpenInfo->papszSiblingFiles == NULL) { osHdrFilename = CPLResetExtension( poOpenInfo->pszFilename, "hdr" ); fpHeader = VSIFOpenL( osHdrFilename, pszMode ); if( fpHeader == NULL && VSIIsCaseSensitiveFS(osHdrFilename) ) { osHdrFilename = CPLResetExtension( poOpenInfo->pszFilename, "HDR" ); fpHeader = VSIFOpenL( osHdrFilename, pszMode ); } if( fpHeader == NULL ) { osHdrFilename = CPLFormFilename( NULL, poOpenInfo->pszFilename, "hdr" ); fpHeader = VSIFOpenL( osHdrFilename, pszMode ); } if( fpHeader == NULL && VSIIsCaseSensitiveFS(osHdrFilename) ) { osHdrFilename = CPLFormFilename( NULL, poOpenInfo->pszFilename, "HDR" ); fpHeader = VSIFOpenL( osHdrFilename, pszMode ); } } else { /* -------------------------------------------------------------------- */ /* Now we need to tear apart the filename to form a .HDR */ /* filename. */ /* -------------------------------------------------------------------- */ CPLString osPath = CPLGetPath( poOpenInfo->pszFilename ); CPLString osName = CPLGetFilename( poOpenInfo->pszFilename ); int iFile = CSLFindString(poOpenInfo->papszSiblingFiles, CPLResetExtension( osName, "hdr" ) ); if( iFile >= 0 ) { osHdrFilename = CPLFormFilename( osPath, poOpenInfo->papszSiblingFiles[iFile], NULL ); fpHeader = VSIFOpenL( osHdrFilename, pszMode ); } else { iFile = CSLFindString(poOpenInfo->papszSiblingFiles, CPLFormFilename( NULL, osName, "hdr" )); if( iFile >= 0 ) { osHdrFilename = CPLFormFilename( osPath, poOpenInfo->papszSiblingFiles[iFile], NULL ); fpHeader = VSIFOpenL( osHdrFilename, pszMode ); } } } if( fpHeader == NULL ) return NULL; /* -------------------------------------------------------------------- */ /* Check that the first line says "ENVI". */ /* -------------------------------------------------------------------- */ char szTestHdr[4]; if( VSIFReadL( szTestHdr, 4, 1, fpHeader ) != 1 ) { VSIFCloseL( fpHeader ); return NULL; } if( strncmp(szTestHdr,"ENVI",4) != 0 ) { VSIFCloseL( fpHeader ); return NULL; } /* -------------------------------------------------------------------- */ /* Create a corresponding GDALDataset. */ /* -------------------------------------------------------------------- */ ENVIDataset *poDS; poDS = new ENVIDataset(); poDS->pszHDRFilename = CPLStrdup(osHdrFilename); poDS->fp = fpHeader; /* -------------------------------------------------------------------- */ /* Read the header. */ /* -------------------------------------------------------------------- */ if( !poDS->ReadHeader( fpHeader ) ) { delete poDS; return NULL; } /* -------------------------------------------------------------------- */ /* Has the user selected the .hdr file to open? */ /* -------------------------------------------------------------------- */ if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "hdr") ) { delete poDS; CPLError( CE_Failure, CPLE_AppDefined, "The selected file is an ENVI header file, but to\n" "open ENVI datasets, the data file should be selected\n" "instead of the .hdr file. Please try again selecting\n" "the data file corresponding to the header file:\n" " %s\n", poOpenInfo->pszFilename ); return NULL; } /* -------------------------------------------------------------------- */ /* Has the user selected the .sta (stats) file to open? */ /* -------------------------------------------------------------------- */ if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "sta") ) { delete poDS; CPLError( CE_Failure, CPLE_AppDefined, "The selected file is an ENVI statistics file.\n" "To open ENVI datasets, the data file should be selected\n" "instead of the .sta file. Please try again selecting\n" "the data file corresponding to the statistics file:\n" " %s\n", poOpenInfo->pszFilename ); return NULL; } /* -------------------------------------------------------------------- */ /* Extract required values from the .hdr. */ /* -------------------------------------------------------------------- */ int nLines = 0, nSamples = 0, nBands = 0, nHeaderSize = 0; const char *pszInterleave = NULL; if( CSLFetchNameValue(poDS->papszHeader,"lines") ) nLines = atoi(CSLFetchNameValue(poDS->papszHeader,"lines")); if( CSLFetchNameValue(poDS->papszHeader,"samples") ) nSamples = atoi(CSLFetchNameValue(poDS->papszHeader,"samples")); if( CSLFetchNameValue(poDS->papszHeader,"bands") ) nBands = atoi(CSLFetchNameValue(poDS->papszHeader,"bands")); pszInterleave = CSLFetchNameValue(poDS->papszHeader,"interleave"); /* In case, there is no interleave keyword, we try to derive it from the */ /* file extension. */ if( pszInterleave == NULL ) { const char* pszExtension = CPLGetExtension(poOpenInfo->pszFilename); pszInterleave = pszExtension; } if ( !EQUALN(pszInterleave, "BSQ",3) && !EQUALN(pszInterleave, "BIP",3) && !EQUALN(pszInterleave, "BIL",3) ) { CPLDebug("ENVI", "Unset or unknown value for 'interleave' keyword --> assuming BSQ interleaving"); pszInterleave = "bsq"; } if (!GDALCheckDatasetDimensions(nSamples, nLines) || !GDALCheckBandCount(nBands, FALSE)) { delete poDS; CPLError( CE_Failure, CPLE_AppDefined, "The file appears to have an associated ENVI header, but\n" "one or more of the samples, lines and bands\n" "keywords appears to be missing or invalid." ); return NULL; } if( CSLFetchNameValue(poDS->papszHeader,"header_offset") ) nHeaderSize = atoi(CSLFetchNameValue(poDS->papszHeader,"header_offset")); /* -------------------------------------------------------------------- */ /* Translate the datatype. */ /* -------------------------------------------------------------------- */ GDALDataType eType = GDT_Byte; if( CSLFetchNameValue(poDS->papszHeader,"data_type" ) != NULL ) { switch( atoi(CSLFetchNameValue(poDS->papszHeader,"data_type" )) ) { case 1: eType = GDT_Byte; break; case 2: eType = GDT_Int16; break; case 3: eType = GDT_Int32; break; case 4: eType = GDT_Float32; break; case 5: eType = GDT_Float64; break; case 6: eType = GDT_CFloat32; break; case 9: eType = GDT_CFloat64; break; case 12: eType = GDT_UInt16; break; case 13: eType = GDT_UInt32; break; /* 14=Int64, 15=UInt64 */ default: delete poDS; CPLError( CE_Failure, CPLE_AppDefined, "The file does not have a value for the data_type\n" "that is recognised by the GDAL ENVI driver."); return NULL; } } /* -------------------------------------------------------------------- */ /* Translate the byte order. */ /* -------------------------------------------------------------------- */ int bNativeOrder = TRUE; if( CSLFetchNameValue(poDS->papszHeader,"byte_order" ) != NULL ) { #ifdef CPL_LSB bNativeOrder = atoi(CSLFetchNameValue(poDS->papszHeader, "byte_order" )) == 0; #else bNativeOrder = atoi(CSLFetchNameValue(poDS->papszHeader, "byte_order" )) != 0; #endif } /* -------------------------------------------------------------------- */ /* Warn about unsupported file types virtual mosaic and meta file.*/ /* -------------------------------------------------------------------- */ if( CSLFetchNameValue(poDS->papszHeader,"file_type" ) != NULL ) { // when the file type is one of these we return an invalid file type err //'envi meta file' //'envi virtual mosaic' //'envi spectral library' //'envi fft result' // when the file type is one of these we open it //'envi standard' //'envi classification' // when the file type is anything else we attempt to open it as a raster. const char * pszEnviFileType; pszEnviFileType = CSLFetchNameValue(poDS->papszHeader,"file_type"); // envi gdal does not support any of these // all others we will attempt to open if(EQUAL(pszEnviFileType, "envi meta file") || EQUAL(pszEnviFileType, "envi virtual mosaic") || EQUAL(pszEnviFileType, "envi spectral library")) { CPLError( CE_Failure, CPLE_OpenFailed, "File %s contains an invalid file type in the ENVI .hdr\n" "GDAL does not support '%s' type files.", poOpenInfo->pszFilename, pszEnviFileType ); delete poDS; return NULL; } } /* -------------------------------------------------------------------- */ /* Detect (gzipped) compressed datasets. */ /* -------------------------------------------------------------------- */ int bIsCompressed = FALSE; if( CSLFetchNameValue(poDS->papszHeader,"file_compression" ) != NULL ) { if( atoi(CSLFetchNameValue(poDS->papszHeader,"file_compression" )) != 0 ) { bIsCompressed = TRUE; } } /* -------------------------------------------------------------------- */ /* Capture some information from the file that is of interest. */ /* -------------------------------------------------------------------- */ poDS->nRasterXSize = nSamples; poDS->nRasterYSize = nLines; poDS->eAccess = poOpenInfo->eAccess; /* -------------------------------------------------------------------- */ /* Reopen file in update mode if necessary. */ /* -------------------------------------------------------------------- */ CPLString osImageFilename(poOpenInfo->pszFilename); if (bIsCompressed) osImageFilename = "/vsigzip/" + osImageFilename; if( poOpenInfo->eAccess == GA_Update ) { if (bIsCompressed) { delete poDS; CPLError( CE_Failure, CPLE_OpenFailed, "Cannot open compressed file in update mode.\n"); return NULL; } poDS->fpImage = VSIFOpenL( osImageFilename, "rb+" ); } else poDS->fpImage = VSIFOpenL( osImageFilename, "rb" ); if( poDS->fpImage == NULL ) { delete poDS; CPLError( CE_Failure, CPLE_OpenFailed, "Failed to re-open %s within ENVI driver.\n", poOpenInfo->pszFilename ); return NULL; } /* -------------------------------------------------------------------- */ /* Compute the line offset. */ /* -------------------------------------------------------------------- */ int nDataSize = GDALGetDataTypeSize(eType)/8; int nPixelOffset, nLineOffset; vsi_l_offset nBandOffset; int bIntOverflow = FALSE; if( EQUALN(pszInterleave, "bil", 3) ) { poDS->interleave = BIL; poDS->SetMetadataItem( "INTERLEAVE", "LINE", "IMAGE_STRUCTURE" ); if (nSamples > INT_MAX / (nDataSize * nBands)) bIntOverflow = TRUE; nLineOffset = nDataSize * nSamples * nBands; nPixelOffset = nDataSize; nBandOffset = (vsi_l_offset)nDataSize * nSamples; } else if( EQUALN(pszInterleave, "bip", 3) ) { poDS->interleave = BIP; poDS->SetMetadataItem( "INTERLEAVE", "PIXEL", "IMAGE_STRUCTURE" ); if (nSamples > INT_MAX / (nDataSize * nBands)) bIntOverflow = TRUE; nLineOffset = nDataSize * nSamples * nBands; nPixelOffset = nDataSize * nBands; nBandOffset = nDataSize; } else /* bsq */ { poDS->interleave = BSQ; poDS->SetMetadataItem( "INTERLEAVE", "BAND", "IMAGE_STRUCTURE" ); if (nSamples > INT_MAX / nDataSize) bIntOverflow = TRUE; nLineOffset = nDataSize * nSamples; nPixelOffset = nDataSize; nBandOffset = (vsi_l_offset)nLineOffset * nLines; } if (bIntOverflow) { delete poDS; CPLError( CE_Failure, CPLE_AppDefined, "Int overflow occured."); return NULL; } /* -------------------------------------------------------------------- */ /* Create band information objects. */ /* -------------------------------------------------------------------- */ poDS->nBands = nBands; for( i = 0; i < poDS->nBands; i++ ) { poDS->SetBand( i + 1, new ENVIRasterBand(poDS, i + 1, poDS->fpImage, nHeaderSize + nBandOffset * i, nPixelOffset, nLineOffset, eType, bNativeOrder, TRUE) ); } /* -------------------------------------------------------------------- */ /* Apply band names if we have them. */ /* Use wavelength for more descriptive information if possible */ /* -------------------------------------------------------------------- */ if( CSLFetchNameValue( poDS->papszHeader, "band_names" ) != NULL || CSLFetchNameValue( poDS->papszHeader, "wavelength" ) != NULL) { char **papszBandNames = poDS->SplitList( CSLFetchNameValue( poDS->papszHeader, "band_names" ) ); char **papszWL = poDS->SplitList( CSLFetchNameValue( poDS->papszHeader, "wavelength" ) ); const char *pszWLUnits = NULL; int nWLCount = CSLCount(papszWL); if (papszWL) { /* If WL information is present, process wavelength units */ pszWLUnits = CSLFetchNameValue( poDS->papszHeader, "wavelength_units" ); if (pszWLUnits) { /* Don't show unknown or index units */ if (EQUAL(pszWLUnits,"Unknown") || EQUAL(pszWLUnits,"Index") ) pszWLUnits=0; } if (pszWLUnits) { /* set wavelength units to dataset metadata */ poDS->SetMetadataItem("wavelength_units", pszWLUnits); } } for( i = 0; i < nBands; i++ ) { CPLString osBandId, osBandName, osWavelength; /* First set up the wavelength names and units if available */ if (papszWL && nWLCount > i) { osWavelength = papszWL[i]; if (pszWLUnits) { osWavelength += " "; osWavelength += pszWLUnits; } } /* Build the final name for this band */ if (papszBandNames && CSLCount(papszBandNames) > i) { osBandName = papszBandNames[i]; if (strlen(osWavelength) > 0) { osBandName += " ("; osBandName += osWavelength; osBandName += ")"; } } else /* WL but no band names */ osBandName = osWavelength; /* Description is for internal GDAL usage */ poDS->GetRasterBand(i + 1)->SetDescription( osBandName ); /* Metadata field named Band_1, etc. needed for ArcGIS integration */ osBandId = CPLSPrintf("Band_%i", i+1); poDS->SetMetadataItem(osBandId, osBandName); /* Set wavelength metadata to band */ if (papszWL && nWLCount > i) { poDS->GetRasterBand(i+1)->SetMetadataItem( "wavelength", papszWL[i]); if (pszWLUnits) { poDS->GetRasterBand(i+1)->SetMetadataItem( "wavelength_units", pszWLUnits); } } } CSLDestroy( papszWL ); CSLDestroy( papszBandNames ); } /* -------------------------------------------------------------------- */ /* Apply class names if we have them. */ /* -------------------------------------------------------------------- */ if( CSLFetchNameValue( poDS->papszHeader, "class_names" ) != NULL ) { char **papszClassNames = poDS->SplitList( CSLFetchNameValue( poDS->papszHeader, "class_names" ) ); poDS->GetRasterBand(1)->SetCategoryNames( papszClassNames ); CSLDestroy( papszClassNames ); } /* -------------------------------------------------------------------- */ /* Apply colormap if we have one. */ /* -------------------------------------------------------------------- */ if( CSLFetchNameValue( poDS->papszHeader, "class_lookup" ) != NULL ) { char **papszClassColors = poDS->SplitList( CSLFetchNameValue( poDS->papszHeader, "class_lookup" ) ); int nColorValueCount = CSLCount(papszClassColors); GDALColorTable oCT; for( i = 0; i*3+2 < nColorValueCount; i++ ) { GDALColorEntry sEntry; sEntry.c1 = (short) atoi(papszClassColors[i*3+0]); sEntry.c2 = (short) atoi(papszClassColors[i*3+1]); sEntry.c3 = (short) atoi(papszClassColors[i*3+2]); sEntry.c4 = 255; oCT.SetColorEntry( i, &sEntry ); } CSLDestroy( papszClassColors ); poDS->GetRasterBand(1)->SetColorTable( &oCT ); poDS->GetRasterBand(1)->SetColorInterpretation( GCI_PaletteIndex ); } /* -------------------------------------------------------------------- */ /* Set the nodata value if it is present */ /* -------------------------------------------------------------------- */ if( CSLFetchNameValue(poDS->papszHeader,"data_ignore_value" ) != NULL ) { for( i = 0; i < poDS->nBands; i++ ) ((RawRasterBand*)poDS->GetRasterBand(i+1))->SetNoDataValue(atof( CSLFetchNameValue(poDS->papszHeader,"data_ignore_value"))); } /* -------------------------------------------------------------------- */ /* Set all the header metadata into the ENVI domain */ /* -------------------------------------------------------------------- */ { char** pTmp = poDS->papszHeader; while (*pTmp != NULL) { char** pTokens = CSLTokenizeString2(*pTmp, "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES); if (pTokens[0] != NULL && pTokens[1] != NULL && pTokens[2] == NULL) { poDS->SetMetadataItem(pTokens[0], pTokens[1], "ENVI"); } CSLDestroy(pTokens); pTmp++; } } /* -------------------------------------------------------------------- */ /* Read the stats file if it is present */ /* -------------------------------------------------------------------- */ poDS->ProcessStatsFile(); /* -------------------------------------------------------------------- */ /* Look for mapinfo */ /* -------------------------------------------------------------------- */ if( CSLFetchNameValue( poDS->papszHeader, "map_info" ) != NULL ) { poDS->bFoundMapinfo = poDS->ProcessMapinfo( CSLFetchNameValue(poDS->papszHeader,"map_info") ); } /* -------------------------------------------------------------------- */ /* Look for RPC mapinfo */ /* -------------------------------------------------------------------- */ if( !poDS->bFoundMapinfo && CSLFetchNameValue( poDS->papszHeader, "rpc_info" ) != NULL ) { poDS->ProcessRPCinfo( CSLFetchNameValue(poDS->papszHeader,"rpc_info"), poDS->nRasterXSize, poDS->nRasterYSize); } /* -------------------------------------------------------------------- */ /* Initialize any PAM information. */ /* -------------------------------------------------------------------- */ poDS->SetDescription( poOpenInfo->pszFilename ); poDS->TryLoadXML(); /* -------------------------------------------------------------------- */ /* Check for overviews. */ /* -------------------------------------------------------------------- */ poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); // SetMetadata() calls in Open() makes the header dirty. // Don't re-write the header if nothing external has changed the metadata poDS->bHeaderDirty = FALSE; return( poDS ); } int ENVIDataset::GetEnviType(GDALDataType eType) { int iENVIType; switch( eType ) { case GDT_Byte: iENVIType = 1; break; case GDT_Int16: iENVIType = 2; break; case GDT_Int32: iENVIType = 3; break; case GDT_Float32: iENVIType = 4; break; case GDT_Float64: iENVIType = 5; break; case GDT_CFloat32: iENVIType = 6; break; case GDT_CFloat64: iENVIType = 9; break; case GDT_UInt16: iENVIType = 12; break; case GDT_UInt32: iENVIType = 13; break; /* 14=Int64, 15=UInt64 */ default: CPLError( CE_Failure, CPLE_AppDefined, "Attempt to create ENVI .hdr labelled dataset with an illegal\n" "data type (%s).\n", GDALGetDataTypeName(eType) ); return 1; } return iENVIType; } /************************************************************************/ /* Create() */ /************************************************************************/ GDALDataset *ENVIDataset::Create( const char * pszFilename, int nXSize, int nYSize, int nBands, GDALDataType eType, char ** papszOptions ) { /* -------------------------------------------------------------------- */ /* Verify input options. */ /* -------------------------------------------------------------------- */ int iENVIType = GetEnviType(eType); if (0 == iENVIType) return 0; /* -------------------------------------------------------------------- */ /* Try to create the file. */ /* -------------------------------------------------------------------- */ VSILFILE *fp; fp = VSIFOpenL( pszFilename, "wb" ); if( fp == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Attempt to create file `%s' failed.\n", pszFilename ); return NULL; } /* -------------------------------------------------------------------- */ /* Just write out a couple of bytes to establish the binary */ /* file, and then close it. */ /* -------------------------------------------------------------------- */ VSIFWriteL( (void *) "\0\0", 2, 1, fp ); VSIFCloseL( fp ); /* -------------------------------------------------------------------- */ /* Create the .hdr filename. */ /* -------------------------------------------------------------------- */ const char *pszHDRFilename; const char *pszSuffix; pszSuffix = CSLFetchNameValue( papszOptions, "SUFFIX" ); if ( pszSuffix && EQUALN( pszSuffix, "ADD", 3 )) pszHDRFilename = CPLFormFilename( NULL, pszFilename, "hdr" ); else pszHDRFilename = CPLResetExtension(pszFilename, "hdr" ); /* -------------------------------------------------------------------- */ /* Open the file. */ /* -------------------------------------------------------------------- */ fp = VSIFOpenL( pszHDRFilename, "wt" ); if( fp == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Attempt to create file `%s' failed.\n", pszHDRFilename ); return NULL; } /* -------------------------------------------------------------------- */ /* Write out the header. */ /* -------------------------------------------------------------------- */ int iBigEndian; const char *pszInterleaving; #ifdef CPL_LSB iBigEndian = 0; #else iBigEndian = 1; #endif VSIFPrintfL( fp, "ENVI\n" ); VSIFPrintfL( fp, "samples = %d\nlines = %d\nbands = %d\n", nXSize, nYSize, nBands ); VSIFPrintfL( fp, "header offset = 0\nfile type = ENVI Standard\n" ); VSIFPrintfL( fp, "data type = %d\n", iENVIType ); pszInterleaving = CSLFetchNameValue( papszOptions, "INTERLEAVE" ); if ( pszInterleaving ) { if ( EQUALN( pszInterleaving, "bip", 3 ) ) pszInterleaving = "bip"; // interleaved by pixel else if ( EQUALN( pszInterleaving, "bil", 3 ) ) pszInterleaving = "bil"; // interleaved by line else pszInterleaving = "bsq"; // band sequental by default } else pszInterleaving = "bsq"; VSIFPrintfL( fp, "interleave = %s\n", pszInterleaving); VSIFPrintfL( fp, "byte order = %d\n", iBigEndian ); VSIFCloseL( fp ); return (GDALDataset *) GDALOpen( pszFilename, GA_Update ); } /************************************************************************/ /* ENVIRasterBand() */ /************************************************************************/ ENVIRasterBand::ENVIRasterBand( GDALDataset *poDS, int nBand, void * fpRaw, vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset, GDALDataType eDataType, int bNativeOrder, int bIsVSIL, int bOwnsFP ) : RawRasterBand(poDS, nBand, fpRaw, nImgOffset, nPixelOffset, nLineOffset, eDataType, bNativeOrder, bIsVSIL, bOwnsFP) { } /************************************************************************/ /* SetDescription() */ /************************************************************************/ void ENVIRasterBand::SetDescription( const char * pszDescription ) { ((ENVIDataset*)poDS)->bHeaderDirty = TRUE; RawRasterBand::SetDescription(pszDescription); } /************************************************************************/ /* SetCategoryNames() */ /************************************************************************/ CPLErr ENVIRasterBand::SetCategoryNames( char ** papszCategoryNames ) { ((ENVIDataset*)poDS)->bHeaderDirty = TRUE; return RawRasterBand::SetCategoryNames(papszCategoryNames); } /************************************************************************/ /* GDALRegister_ENVI() */ /************************************************************************/ void GDALRegister_ENVI() { GDALDriver *poDriver; if( GDALGetDriverByName( "ENVI" ) == NULL ) { poDriver = new GDALDriver(); poDriver->SetDescription( "ENVI" ); poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "ENVI .hdr Labelled" ); poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_various.html#ENVI" ); poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "" ); poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte Int16 UInt16 Int32 UInt32 " "Float32 Float64 CFloat32 CFloat64" ); poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, "<CreationOptionList>" " <Option name='SUFFIX' type='string-select'>" " <Value>ADD</Value>" " </Option>" " <Option name='INTERLEAVE' type='string-select'>" " <Value>BIP</Value>" " <Value>BIL</Value>" " <Value>BSQ</Value>" " </Option>" "</CreationOptionList>" ); poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); poDriver->pfnOpen = ENVIDataset::Open; poDriver->pfnCreate = ENVIDataset::Create; GetGDALDriverManager()->RegisterDriver( poDriver ); } }