EVOLUTION-MANAGER
Edit File: isis2dataset.cpp
/****************************************************************************** * $Id: isis2dataset.cpp 27729 2014-09-24 00:40:16Z goatbar $ * * Project: ISIS Version 2 Driver * Purpose: Implementation of ISIS2Dataset * Author: Trent Hare (thare@usgs.gov), * Robert Soricone (rsoricone@usgs.gov) * Ludovic Mercier (ludovic.mercier@gmail.com) * Frank Warmerdam (warmerdam@pobox.com) * * NOTE: Original code authored by Trent and Robert and placed in the public * domain as per US government policy. I have (within my rights) appropriated * it and placed it under the following license. This is not intended to * diminish Trent and Roberts contribution. ****************************************************************************** * Copyright (c) 2006, Frank Warmerdam <warmerdam@pobox.com> * Copyright (c) 2008-2011, 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. ****************************************************************************/ #define NULL1 0 #define NULL2 -32768 #define NULL3 -3.4028226550889044521e+38 #ifndef PI # define PI 3.1415926535897932384626433832795 #endif #define RECORD_SIZE 512 #include "rawdataset.h" #include "ogr_spatialref.h" #include "cpl_string.h" #include "nasakeywordhandler.h" CPL_CVSID("$Id: isis2dataset.cpp 27729 2014-09-24 00:40:16Z goatbar $"); CPL_C_START void GDALRegister_ISIS2(void); CPL_C_END /************************************************************************/ /* ==================================================================== */ /* ISISDataset version2 */ /* ==================================================================== */ /************************************************************************/ class ISIS2Dataset : public RawDataset { VSILFILE *fpImage; // image data file. CPLString osExternalCube; NASAKeywordHandler oKeywords; int bGotTransform; double adfGeoTransform[6]; CPLString osProjection; int parse_label(const char *file, char *keyword, char *value); int strstrip(char instr[], char outstr[], int position); CPLString oTempResult; void CleanString( CPLString &osInput ); const char *GetKeyword( const char *pszPath, const char *pszDefault = ""); const char *GetKeywordSub( const char *pszPath, int iSubscript, const char *pszDefault = ""); public: ISIS2Dataset(); ~ISIS2Dataset(); virtual CPLErr GetGeoTransform( double * padfTransform ); virtual const char *GetProjectionRef(void); virtual char **GetFileList(); static int Identify( GDALOpenInfo * ); static GDALDataset *Open( GDALOpenInfo * ); static GDALDataset *Create( const char * pszFilename, int nXSize, int nYSize, int nBands, GDALDataType eType, char ** papszParmList ); // Write related. static int WriteRaster(CPLString osFilename, bool includeLabel, GUIntBig iRecord, GUIntBig iLabelRecords, GDALDataType eType, const char * pszInterleaving); static int WriteLabel(CPLString osFilename, CPLString osRasterFile, CPLString sObjectTag, unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType, GUIntBig iRecords, const char * pszInterleaving, GUIntBig & iLabelRecords, bool bRelaunch=false); static int WriteQUBE_Information(VSILFILE *fpLabel, unsigned int iLevel, unsigned int & nWritingBytes, unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType, const char * pszInterleaving); static unsigned int WriteKeyword(VSILFILE *fpLabel, unsigned int iLevel, CPLString key, CPLString value); static unsigned int WriteFormatting(VSILFILE *fpLabel, CPLString data); static GUIntBig RecordSizeCalculation(unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType ); }; /************************************************************************/ /* ISIS2Dataset() */ /************************************************************************/ ISIS2Dataset::ISIS2Dataset() { fpImage = NULL; bGotTransform = 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; } /************************************************************************/ /* ~ISIS2Dataset() */ /************************************************************************/ ISIS2Dataset::~ISIS2Dataset() { FlushCache(); if( fpImage != NULL ) VSIFCloseL( fpImage ); } /************************************************************************/ /* GetFileList() */ /************************************************************************/ char **ISIS2Dataset::GetFileList() { char **papszFileList = NULL; papszFileList = GDALPamDataset::GetFileList(); if( strlen(osExternalCube) > 0 ) papszFileList = CSLAddString( papszFileList, osExternalCube ); return papszFileList; } /************************************************************************/ /* GetProjectionRef() */ /************************************************************************/ const char *ISIS2Dataset::GetProjectionRef() { if( strlen(osProjection) > 0 ) return osProjection; else return GDALPamDataset::GetProjectionRef(); } /************************************************************************/ /* GetGeoTransform() */ /************************************************************************/ CPLErr ISIS2Dataset::GetGeoTransform( double * padfTransform ) { if( bGotTransform ) { memcpy( padfTransform, adfGeoTransform, sizeof(double) * 6 ); return CE_None; } else { return GDALPamDataset::GetGeoTransform( padfTransform ); } } /************************************************************************/ /* Identify() */ /************************************************************************/ int ISIS2Dataset::Identify( GDALOpenInfo * poOpenInfo ) { if( poOpenInfo->pabyHeader == NULL ) return FALSE; if( strstr((const char *)poOpenInfo->pabyHeader,"^QUBE") == NULL ) return FALSE; else return TRUE; } /************************************************************************/ /* Open() */ /************************************************************************/ GDALDataset *ISIS2Dataset::Open( GDALOpenInfo * poOpenInfo ) { /* -------------------------------------------------------------------- */ /* Does this look like a CUBE or an IMAGE Primary Data Object? */ /* -------------------------------------------------------------------- */ if( !Identify( poOpenInfo ) ) return NULL; /* -------------------------------------------------------------------- */ /* Open the file using the large file API. */ /* -------------------------------------------------------------------- */ VSILFILE *fpQube = VSIFOpenL( poOpenInfo->pszFilename, "rb" ); if( fpQube == NULL ) return NULL; ISIS2Dataset *poDS; poDS = new ISIS2Dataset(); if( ! poDS->oKeywords.Ingest( fpQube, 0 ) ) { VSIFCloseL( fpQube ); delete poDS; return NULL; } VSIFCloseL( fpQube ); /* -------------------------------------------------------------------- */ /* We assume the user is pointing to the label (ie. .lab) file. */ /* -------------------------------------------------------------------- */ // QUBE can be inline or detached and point to an image name // ^QUBE = 76 // ^QUBE = ("ui31s015.img",6441<BYTES>) - has another label on the image // ^QUBE = "ui31s015.img" - which implies no label or skip value const char *pszQube = poDS->GetKeyword( "^QUBE" ); GUIntBig nQube = 0; int bByteLocation = FALSE; CPLString osTargetFile = poOpenInfo->pszFilename; if( pszQube[0] == '"' ) { CPLString osTPath = CPLGetPath(poOpenInfo->pszFilename); CPLString osFilename = pszQube; poDS->CleanString( osFilename ); osTargetFile = CPLFormCIFilename( osTPath, osFilename, NULL ); poDS->osExternalCube = osTargetFile; } else if( pszQube[0] == '(' ) { CPLString osTPath = CPLGetPath(poOpenInfo->pszFilename); CPLString osFilename = poDS->GetKeywordSub("^QUBE",1,""); poDS->CleanString( osFilename ); osTargetFile = CPLFormCIFilename( osTPath, osFilename, NULL ); poDS->osExternalCube = osTargetFile; nQube = atoi(poDS->GetKeywordSub("^QUBE",2,"1")); if( strstr(poDS->GetKeywordSub("^QUBE",2,"1"),"<BYTES>") != NULL ) bByteLocation = true; } else { nQube = atoi(pszQube); if( strstr(pszQube,"<BYTES>") != NULL ) bByteLocation = true; } /* -------------------------------------------------------------------- */ /* Check if file an ISIS2 header file? Read a few lines of text */ /* searching for something starting with nrows or ncols. */ /* -------------------------------------------------------------------- */ GDALDataType eDataType = GDT_Byte; OGRSpatialReference oSRS; //image parameters int nRows, nCols, nBands = 1; GUIntBig nSkipBytes = 0; int itype; int s_ix, s_iy, s_iz; // check SUFFIX_ITEMS params. int record_bytes; int bNoDataSet = FALSE; char chByteOrder = 'M'; //default to MSB //Georef parameters double dfULXMap=0.5; double dfULYMap = 0.5; double dfXDim = 1.0; double dfYDim = 1.0; double dfNoData = 0.0; double xulcenter = 0.0; double yulcenter = 0.0; //projection parameters int bProjectionSet = TRUE; double semi_major = 0.0; double semi_minor = 0.0; double iflattening = 0.0; double center_lat = 0.0; double center_lon = 0.0; double first_std_parallel = 0.0; double second_std_parallel = 0.0; VSILFILE *fp; /* -------------------------------------------------------------------- */ /* Checks to see if this is valid ISIS2 cube */ /* SUFFIX_ITEM tag in .cub file should be (0,0,0); no side-planes */ /* -------------------------------------------------------------------- */ s_ix = atoi(poDS->GetKeywordSub( "QUBE.SUFFIX_ITEMS", 1 )); s_iy = atoi(poDS->GetKeywordSub( "QUBE.SUFFIX_ITEMS", 2 )); s_iz = atoi(poDS->GetKeywordSub( "QUBE.SUFFIX_ITEMS", 3 )); if( s_ix != 0 || s_iy != 0 || s_iz != 0 ) { CPLError( CE_Failure, CPLE_OpenFailed, "*** ISIS 2 cube file has invalid SUFFIX_ITEMS parameters:\n" "*** gdal isis2 driver requires (0, 0, 0), thus no sideplanes or backplanes\n" "found: (%i, %i, %i)\n\n", s_ix, s_iy, s_iz ); delete poDS; return NULL; } /**************** end SUFFIX_ITEM check ***********************/ /*********** Grab layout type (BSQ, BIP, BIL) ************/ // AXIS_NAME = (SAMPLE,LINE,BAND) /***********************************************************/ const char *value; char szLayout[10] = "BSQ"; //default to band seq. value = poDS->GetKeyword( "QUBE.AXIS_NAME", "" ); if (EQUAL(value,"(SAMPLE,LINE,BAND)") ) strcpy(szLayout,"BSQ"); else if (EQUAL(value,"(BAND,LINE,SAMPLE)") ) strcpy(szLayout,"BIP"); else if (EQUAL(value,"(SAMPLE,BAND,LINE)") || EQUAL(value,"") ) strcpy(szLayout,"BSQ"); else { CPLError( CE_Failure, CPLE_OpenFailed, "%s layout not supported. Abort\n\n", value); delete poDS; return NULL; } /*********** Grab samples lines band ************/ nCols = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS",1)); nRows = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS",2)); nBands = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS",3)); /*********** Grab Qube record bytes **********/ record_bytes = atoi(poDS->GetKeyword("RECORD_BYTES")); if (nQube > 0 && bByteLocation ) nSkipBytes = (nQube - 1); else if( nQube > 0 ) nSkipBytes = (nQube - 1) * record_bytes; else nSkipBytes = 0; /*********** Grab samples lines band ************/ CPLString osCoreItemType = poDS->GetKeyword( "QUBE.CORE_ITEM_TYPE" ); if( (EQUAL(osCoreItemType,"PC_INTEGER")) || (EQUAL(osCoreItemType,"PC_UNSIGNED_INTEGER")) || (EQUAL(osCoreItemType,"PC_REAL")) ) { chByteOrder = 'I'; } /******** Grab format type - isis2 only supports 8,16,32 *******/ itype = atoi(poDS->GetKeyword("QUBE.CORE_ITEM_BYTES","")); switch(itype) { case 1 : eDataType = GDT_Byte; dfNoData = NULL1; bNoDataSet = TRUE; break; case 2 : if( strstr(osCoreItemType,"UNSIGNED") != NULL ) { dfNoData = 0; eDataType = GDT_UInt16; } else { dfNoData = NULL2; eDataType = GDT_Int16; } bNoDataSet = TRUE; break; case 4 : eDataType = GDT_Float32; dfNoData = NULL3; bNoDataSet = TRUE; break; case 8 : eDataType = GDT_Float64; dfNoData = NULL3; bNoDataSet = TRUE; break; default : CPLError( CE_Failure, CPLE_AppDefined, "Itype of %d is not supported in ISIS 2.", itype); delete poDS; return NULL; } /*********** Grab Cellsize ************/ value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_SCALE"); if (strlen(value) > 0 ) { dfXDim = (float) atof(value) * 1000.0; /* convert from km to m */ dfYDim = (float) atof(value) * 1000.0 * -1; } /*********** Grab LINE_PROJECTION_OFFSET ************/ value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET"); if (strlen(value) > 0) { yulcenter = (float) atof(value); yulcenter = ((yulcenter) * dfYDim); dfULYMap = yulcenter - (dfYDim/2); } /*********** Grab SAMPLE_PROJECTION_OFFSET ************/ value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET"); if( strlen(value) > 0 ) { xulcenter = (float) atof(value); xulcenter = ((xulcenter) * dfXDim); dfULXMap = xulcenter - (dfXDim/2); } /*********** Grab TARGET_NAME ************/ /**** This is the planets name i.e. MARS ***/ CPLString target_name = poDS->GetKeyword("QUBE.TARGET_NAME"); /*********** Grab MAP_PROJECTION_TYPE ************/ CPLString map_proj_name = poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE"); poDS->CleanString( map_proj_name ); /*********** Grab SEMI-MAJOR ************/ semi_major = atof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) * 1000.0; /*********** Grab semi-minor ************/ semi_minor = atof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) * 1000.0; /*********** Grab CENTER_LAT ************/ center_lat = atof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.CENTER_LATITUDE")); /*********** Grab CENTER_LON ************/ center_lon = atof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.CENTER_LONGITUDE")); /*********** Grab 1st std parallel ************/ first_std_parallel = atof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL")); /*********** Grab 2nd std parallel ************/ second_std_parallel = atof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL")); /*** grab PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/ // Need to further study how ocentric/ographic will effect the gdal library. // So far we will use this fact to define a sphere or ellipse for some projections // Frank - may need to talk this over char bIsGeographic = TRUE; value = poDS->GetKeyword("CUBE.IMAGE_MAP_PROJECTION.PROJECTION_LATITUDE_TYPE"); if (EQUAL( value, "\"PLANETOCENTRIC\"" )) bIsGeographic = FALSE; CPLDebug("ISIS2","using projection %s", map_proj_name.c_str() ); //Set oSRS projection and parameters if ((EQUAL( map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL" )) || (EQUAL( map_proj_name, "EQUIRECTANGULAR" )) || (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) ) { oSRS.OGRSpatialReference::SetEquirectangular2 ( 0.0, center_lon, center_lat, 0, 0 ); } else if (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) { oSRS.OGRSpatialReference::SetOrthographic ( center_lat, center_lon, 0, 0 ); } else if ((EQUAL( map_proj_name, "SINUSOIDAL" )) || (EQUAL( map_proj_name, "SINUSOIDAL_EQUAL-AREA" ))) { oSRS.OGRSpatialReference::SetSinusoidal ( center_lon, 0, 0 ); } else if (EQUAL( map_proj_name, "MERCATOR" )) { oSRS.OGRSpatialReference::SetMercator ( center_lat, center_lon, 1, 0, 0 ); } else if (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC" )) { oSRS.OGRSpatialReference::SetPS ( center_lat, center_lon, 1, 0, 0 ); } else if (EQUAL( map_proj_name, "TRANSVERSE_MERCATOR" )) { oSRS.OGRSpatialReference::SetTM ( center_lat, center_lon, 1, 0, 0 ); } else if (EQUAL( map_proj_name, "LAMBERT_CONFORMAL_CONIC" )) { oSRS.OGRSpatialReference::SetLCC ( first_std_parallel, second_std_parallel, center_lat, center_lon, 0, 0 ); } else if (EQUAL( map_proj_name, "") ) { /* no projection */ bProjectionSet = FALSE; } else { CPLDebug( "ISIS2", "Dataset projection %s is not supported. Continuing...", map_proj_name.c_str() ); bProjectionSet = FALSE; } if (bProjectionSet) { //Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword CPLString proj_target_name = map_proj_name + " " + target_name; oSRS.SetProjCS(proj_target_name); //set ProjCS keyword //The geographic/geocentric name will be the same basic name as the body name //'GCS' = Geographic/Geocentric Coordinate System CPLString geog_name = "GCS_" + target_name; //The datum and sphere names will be the same basic name aas the planet CPLString datum_name = "D_" + target_name; CPLString sphere_name = target_name; // + "_IAU_IAG"); //Might not be IAU defined so don't add //calculate inverse flattening from major and minor axis: 1/f = a/(a-b) if ((semi_major - semi_minor) < 0.0000001) iflattening = 0; else iflattening = semi_major / (semi_major - semi_minor); //Set the body size but take into consideration which proj is being used to help w/ proj4 compatibility //The use of a Sphere, polar radius or ellipse here is based on how ISIS does it internally if ( ( (EQUAL( map_proj_name, "STEREOGRAPHIC" ) && (fabs(center_lat) == 90)) ) || (EQUAL( map_proj_name, "POLAR_STEREOGRAPHIC" ))) { if (bIsGeographic) { //Geograpraphic, so set an ellipse oSRS.SetGeogCS( geog_name, datum_name, sphere_name, semi_major, iflattening, "Reference_Meridian", 0.0 ); } else { //Geocentric, so force a sphere using the semi-minor axis. I hope... sphere_name += "_polarRadius"; oSRS.SetGeogCS( geog_name, datum_name, sphere_name, semi_minor, 0.0, "Reference_Meridian", 0.0 ); } } else if ( (EQUAL( map_proj_name, "SIMPLE_CYLINDRICAL" )) || (EQUAL( map_proj_name, "ORTHOGRAPHIC" )) || (EQUAL( map_proj_name, "STEREOGRAPHIC" )) || (EQUAL( map_proj_name, "SINUSOIDAL_EQUAL-AREA" )) || (EQUAL( map_proj_name, "SINUSOIDAL" )) ) { //isis uses the sphereical equation for these projections so force a sphere oSRS.SetGeogCS( geog_name, datum_name, sphere_name, semi_major, 0.0, "Reference_Meridian", 0.0 ); } else if ((EQUAL( map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL" )) || (EQUAL( map_proj_name, "EQUIRECTANGULAR" )) ) { //Calculate localRadius using ISIS3 simple elliptical method // not the more standard Radius of Curvature method //PI = 4 * atan(1); double radLat, localRadius; if (center_lon == 0) { //No need to calculate local radius oSRS.SetGeogCS( geog_name, datum_name, sphere_name, semi_major, 0.0, "Reference_Meridian", 0.0 ); } else { radLat = center_lat * PI / 180; // in radians localRadius = semi_major * semi_minor / sqrt(pow(semi_minor*cos(radLat),2) + pow(semi_major*sin(radLat),2) ); sphere_name += "_localRadius"; oSRS.SetGeogCS( geog_name, datum_name, sphere_name, localRadius, 0.0, "Reference_Meridian", 0.0 ); CPLDebug( "ISIS2", "local radius: %f", localRadius); } } else { //All other projections: Mercator, Transverse Mercator, Lambert Conformal, etc. //Geographic, so set an ellipse if (bIsGeographic) { oSRS.SetGeogCS( geog_name, datum_name, sphere_name, semi_major, iflattening, "Reference_Meridian", 0.0 ); } else { //Geocentric, so force a sphere. I hope... oSRS.SetGeogCS( geog_name, datum_name, sphere_name, semi_major, 0.0, "Reference_Meridian", 0.0 ); } } // translate back into a projection string. char *pszResult = NULL; oSRS.exportToWkt( &pszResult ); poDS->osProjection = pszResult; CPLFree( pszResult ); } /* END ISIS2 Label Read */ /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ /* -------------------------------------------------------------------- */ /* Did we get the required keywords? If not we return with */ /* this never having been considered to be a match. This isn't */ /* an error! */ /* -------------------------------------------------------------------- */ if( nRows < 1 || nCols < 1 || nBands < 1 ) { delete poDS; return NULL; } /* -------------------------------------------------------------------- */ /* Capture some information from the file that is of interest. */ /* -------------------------------------------------------------------- */ poDS->nRasterXSize = nCols; poDS->nRasterYSize = nRows; /* -------------------------------------------------------------------- */ /* Open target binary file. */ /* -------------------------------------------------------------------- */ if( poOpenInfo->eAccess == GA_ReadOnly ) poDS->fpImage = VSIFOpenL( osTargetFile, "rb" ); else poDS->fpImage = VSIFOpenL( osTargetFile, "r+b" ); if( poDS->fpImage == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open %s with write permission.\n%s", osTargetFile.c_str(), VSIStrerror( errno ) ); delete poDS; return NULL; } poDS->eAccess = poOpenInfo->eAccess; /* -------------------------------------------------------------------- */ /* Compute the line offset. */ /* -------------------------------------------------------------------- */ int nItemSize = GDALGetDataTypeSize(eDataType)/8; int nLineOffset, nPixelOffset, nBandOffset; if( EQUAL(szLayout,"BIP") ) { nPixelOffset = nItemSize * nBands; nLineOffset = nPixelOffset * nCols; nBandOffset = nItemSize; } else if( EQUAL(szLayout,"BSQ") ) { nPixelOffset = nItemSize; nLineOffset = nPixelOffset * nCols; nBandOffset = nLineOffset * nRows; } else /* assume BIL */ { nPixelOffset = nItemSize; nLineOffset = nItemSize * nBands * nCols; nBandOffset = nItemSize * nCols; } /* -------------------------------------------------------------------- */ /* Create band information objects. */ /* -------------------------------------------------------------------- */ int i; poDS->nBands = nBands;; for( i = 0; i < poDS->nBands; i++ ) { RawRasterBand *poBand; poBand = new RawRasterBand( poDS, i+1, poDS->fpImage, nSkipBytes + nBandOffset * i, nPixelOffset, nLineOffset, eDataType, #ifdef CPL_LSB chByteOrder == 'I' || chByteOrder == 'L', #else chByteOrder == 'M', #endif TRUE ); if( bNoDataSet ) poBand->SetNoDataValue( dfNoData ); poDS->SetBand( i+1, poBand ); // Set offset/scale values at the PAM level. poBand->SetOffset( CPLAtofM(poDS->GetKeyword("QUBE.CORE_BASE","0.0"))); poBand->SetScale( CPLAtofM(poDS->GetKeyword("QUBE.CORE_MULTIPLIER","1.0"))); } /* -------------------------------------------------------------------- */ /* Check for a .prj file. For isis2 I would like to keep this in */ /* -------------------------------------------------------------------- */ CPLString osPath, osName; osPath = CPLGetPath( poOpenInfo->pszFilename ); osName = CPLGetBasename(poOpenInfo->pszFilename); const char *pszPrjFile = CPLFormCIFilename( osPath, osName, "prj" ); fp = VSIFOpenL( pszPrjFile, "r" ); if( fp != NULL ) { char **papszLines; OGRSpatialReference oSRS; VSIFCloseL( fp ); papszLines = CSLLoad( pszPrjFile ); if( oSRS.importFromESRI( papszLines ) == OGRERR_NONE ) { char *pszResult = NULL; oSRS.exportToWkt( &pszResult ); poDS->osProjection = pszResult; CPLFree( pszResult ); } CSLDestroy( papszLines ); } if( dfULYMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0 ) { poDS->bGotTransform = TRUE; 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 = GDALReadWorldFile( poOpenInfo->pszFilename, "cbw", poDS->adfGeoTransform ); if( !poDS->bGotTransform ) poDS->bGotTransform = GDALReadWorldFile( poOpenInfo->pszFilename, "wld", poDS->adfGeoTransform ); /* -------------------------------------------------------------------- */ /* Initialize any PAM information. */ /* -------------------------------------------------------------------- */ poDS->SetDescription( poOpenInfo->pszFilename ); poDS->TryLoadXML(); /* -------------------------------------------------------------------- */ /* Check for overviews. */ /* -------------------------------------------------------------------- */ poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename ); return( poDS ); } /************************************************************************/ /* GetKeyword() */ /************************************************************************/ const char *ISIS2Dataset::GetKeyword( const char *pszPath, const char *pszDefault ) { return oKeywords.GetKeyword( pszPath, pszDefault ); } /************************************************************************/ /* GetKeywordSub() */ /************************************************************************/ const char *ISIS2Dataset::GetKeywordSub( const char *pszPath, int iSubscript, const char *pszDefault ) { const char *pszResult = oKeywords.GetKeyword( pszPath, NULL ); if( pszResult == NULL ) return pszDefault; if( pszResult[0] != '(' ) return pszDefault; char **papszTokens = CSLTokenizeString2( pszResult, "(,)", CSLT_HONOURSTRINGS ); if( iSubscript <= CSLCount(papszTokens) ) { oTempResult = papszTokens[iSubscript-1]; CSLDestroy( papszTokens ); return oTempResult.c_str(); } else { CSLDestroy( papszTokens ); return pszDefault; } } /************************************************************************/ /* CleanString() */ /* */ /* Removes single or double quotes, and converts spaces to underscores. */ /* The change is made in-place to CPLString. */ /************************************************************************/ void ISIS2Dataset::CleanString( CPLString &osInput ) { if( ( osInput.size() < 2 ) || ((osInput.at(0) != '"' || osInput.at(osInput.size()-1) != '"' ) && ( osInput.at(0) != '\'' || osInput.at(osInput.size()-1) != '\'')) ) return; char *pszWrk = CPLStrdup(osInput.c_str() + 1); int i; pszWrk[strlen(pszWrk)-1] = '\0'; for( i = 0; pszWrk[i] != '\0'; i++ ) { if( pszWrk[i] == ' ' ) pszWrk[i] = '_'; } osInput = pszWrk; CPLFree( pszWrk ); } /************************************************************************/ /* Create() */ /************************************************************************/ /** * Hidden Creation Options: * INTERLEAVE=BSQ/BIP/BIL: Force the generation specified type of interleaving. * BSQ --- band sequental (default), * BIP --- band interleaved by pixel, * BIL --- band interleaved by line. * OBJECT=QUBE/IMAGE/SPECTRAL_QUBE, if null default is QUBE */ GDALDataset *ISIS2Dataset::Create(const char* pszFilename, int nXSize, int nYSize, int nBands, GDALDataType eType, char** papszParmList) { /* Verify settings. In Isis 2 core pixel values can be represented in * three different ways : 1, 2 4, or 8 Bytes */ if( eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Float32 && eType != GDT_UInt16 && eType != GDT_Float64 ){ CPLError(CE_Failure, CPLE_AppDefined, "The ISIS2 driver does not supporting creating files of type %s.", GDALGetDataTypeName( eType ) ); return NULL; } /* (SAMPLE, LINE, BAND) - Band Sequential (BSQ) - default choice (SAMPLE, BAND, LINE) - Band Interleaved by Line (BIL) (BAND, SAMPLE, LINE) - Band Interleaved by Pixel (BIP) */ const char *pszInterleaving = "(SAMPLE,LINE,BAND)"; const char *pszInterleavingParam = CSLFetchNameValue( papszParmList, "INTERLEAVE" ); if ( pszInterleavingParam ) { if ( EQUALN( pszInterleavingParam, "bip", 3 ) ) pszInterleaving = "(BAND,SAMPLE,LINE)"; else if ( EQUALN( pszInterleavingParam, "bil", 3 ) ) pszInterleaving = "(SAMPLE,BAND,LINE)"; else pszInterleaving = "(SAMPLE,LINE,BAND)"; } /* default labeling method is attached */ bool bAttachedLabelingMethod = true; /* check if labeling method is set : check the all three first chars */ const char *pszLabelingMethod = CSLFetchNameValue( papszParmList, "LABELING_METHOD" ); if ( pszLabelingMethod ){ if ( EQUALN( pszLabelingMethod, "detached", 3 ) ){ bAttachedLabelingMethod = false; } if ( EQUALN( pszLabelingMethod, "attached", 3 ) ){ bAttachedLabelingMethod = true; } } /* set the label and data files */ CPLString osLabelFile, osRasterFile, osOutFile; if( bAttachedLabelingMethod ) { osLabelFile = ""; osRasterFile = pszFilename; osOutFile = osRasterFile; } else { CPLString sExtension = "cub"; const char* pszExtension = CSLFetchNameValue( papszParmList, "IMAGE_EXTENSION" ); if( pszExtension ){ sExtension = pszExtension; } if( EQUAL(CPLGetExtension( pszFilename ), sExtension) ) { CPLError( CE_Failure, CPLE_AppDefined, "IMAGE_EXTENSION (%s) cannot match LABEL file extension.", sExtension.c_str() ); return NULL; } osLabelFile = pszFilename; osRasterFile = CPLResetExtension( osLabelFile, sExtension ); osOutFile = osLabelFile; } const char *pszObject = CSLFetchNameValue( papszParmList, "OBJECT" ); CPLString sObject = "QUBE"; // default choice if (pszObject) { if ( EQUAL( pszObject, "IMAGE") ){ sObject = "IMAGE"; } if ( EQUAL( pszObject, "SPECTRAL_QUBE")){ sObject = "SPECTRAL_QUBE"; } } GUIntBig iRecords = ISIS2Dataset::RecordSizeCalculation(nXSize, nYSize, nBands, eType); GUIntBig iLabelRecords(2); CPLDebug("ISIS2","irecord = %i",static_cast<int>(iRecords)); if( bAttachedLabelingMethod ) { ISIS2Dataset::WriteLabel(osRasterFile, "", sObject, nXSize, nYSize, nBands, eType, iRecords, pszInterleaving, iLabelRecords, true); } else { ISIS2Dataset::WriteLabel(osLabelFile, osRasterFile, sObject, nXSize, nYSize, nBands, eType, iRecords, pszInterleaving, iLabelRecords); } if( !ISIS2Dataset::WriteRaster(osRasterFile, bAttachedLabelingMethod, iRecords, iLabelRecords, eType, pszInterleaving) ) return NULL; return (GDALDataset *) GDALOpen( osOutFile, GA_Update ); } /************************************************************************/ /* WriteRaster() */ /************************************************************************/ int ISIS2Dataset::WriteRaster(CPLString osFilename, bool includeLabel, GUIntBig iRecords, GUIntBig iLabelRecords, CPL_UNUSED GDALDataType eType, CPL_UNUSED const char * pszInterleaving) { GUIntBig nSize; GByte byZero(0); CPLString pszAccess("wb"); if(includeLabel) pszAccess = "ab"; VSILFILE *fpBin = VSIFOpenL( osFilename, pszAccess.c_str() ); if( fpBin == NULL ) { CPLError( CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s", osFilename.c_str(), VSIStrerror( errno ) ); return FALSE; } nSize = iRecords * RECORD_SIZE; CPLDebug("ISIS2","nSize = %i", static_cast<int>(nSize)); if(includeLabel) nSize = iLabelRecords * RECORD_SIZE + nSize; // write last byte if(VSIFSeekL( fpBin, nSize-1, SEEK_SET ) != 0 || VSIFWriteL( &byZero, 1, 1, fpBin ) != 1){ CPLError( CE_Failure, CPLE_FileIO, "Failed to write %s:\n%s", osFilename.c_str(), VSIStrerror( errno ) ); VSIFCloseL( fpBin ); return FALSE; } VSIFCloseL( fpBin ); return TRUE; } /************************************************************************/ /* RecordSizeCalculation() */ /************************************************************************/ GUIntBig ISIS2Dataset::RecordSizeCalculation(unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType ) { GUIntBig n = nXSize * nYSize * nBands * ( GDALGetDataTypeSize(eType) / 8); // size of pds file is a multiple of RECORD_SIZE Bytes. CPLDebug("ISIS2","n = %i", static_cast<int>(n)); CPLDebug("ISIS2","RECORD SIZE = %i", RECORD_SIZE); CPLDebug("ISIS2","nXSize = %i", nXSize); CPLDebug("ISIS2","nYSize = %i", nYSize); CPLDebug("ISIS2","nBands = %i", nBands); CPLDebug("ISIS2","DataTypeSize = %i", GDALGetDataTypeSize(eType)); return (GUIntBig) ceil(static_cast<float>(n)/RECORD_SIZE); } /************************************************************************/ /* WriteQUBE_Information() */ /************************************************************************/ int ISIS2Dataset::WriteQUBE_Information( VSILFILE *fpLabel, unsigned int iLevel, unsigned int & nWritingBytes, unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType, const char * pszInterleaving) { nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, ""); nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* Qube structure */"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "OBJECT", "QUBE"); iLevel++; nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "AXES", "3"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "AXIS_NAME", pszInterleaving); nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* Core description */"); CPLDebug("ISIS2","%d,%d,%d",nXSize,nYSize,nBands); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEMS",CPLString().Printf("(%d,%d,%d)",nXSize,nYSize,nBands)); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_NAME", "\"RAW DATA NUMBER\""); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_UNIT", "\"N/A\""); // TODO change for eType if( eType == GDT_Byte ) { nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_UNSIGNED_INTEGER"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "1"); } else if( eType == GDT_UInt16 ) { nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_UNSIGNED_INTEGER"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "2"); } else if( eType == GDT_Int16 ) { nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_INTEGER"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "2"); } else if( eType == GDT_Float32 ) { nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_REAL"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "4"); } else if( eType == GDT_Float64 ) { nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_REAL"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_ITEM_BYTES", "8"); } // TODO add core null value nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_BASE", "0.0"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "CORE_MULTIPLIER", "1.0"); nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* Suffix description */"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "SUFFIX_BYTES", "4"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "SUFFIX_ITEMS", "( 0, 0, 0)"); iLevel--; nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "END_OBJECT", "QUBE"); return TRUE; } /************************************************************************/ /* WriteLabel() */ /* */ /* osRasterFile : name of raster file but if it is empty we */ /* have only one file with an attached label */ /* sObjectTag : QUBE, IMAGE or SPECTRAL_QUBE */ /* bRelaunch : flag to allow recursiv call */ /************************************************************************/ int ISIS2Dataset::WriteLabel( CPLString osFilename, CPLString osRasterFile, CPLString sObjectTag, unsigned int nXSize, unsigned int nYSize, unsigned int nBands, GDALDataType eType, GUIntBig iRecords, const char * pszInterleaving, GUIntBig &iLabelRecords, CPL_UNUSED bool bRelaunch) { CPLDebug("ISIS2", "Write Label filename = %s, rasterfile = %s",osFilename.c_str(),osRasterFile.c_str()); bool bAttachedLabel = EQUAL(osRasterFile, ""); VSILFILE *fpLabel = VSIFOpenL( osFilename, "w" ); if( fpLabel == NULL ){ CPLError( CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s", osFilename.c_str(), VSIStrerror( errno ) ); return FALSE; } unsigned int iLevel(0); unsigned int nWritingBytes(0); /* write common header */ nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "PDS_VERSION_ID", "PDS3" ); nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, ""); nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* File identification and structure */"); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "RECORD_TYPE", "FIXED_LENGTH" ); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "RECORD_BYTES", CPLString().Printf("%d",RECORD_SIZE)); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "FILE_RECORDS", CPLString().Printf(CPL_FRMT_GUIB,iRecords)); nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "LABEL_RECORDS", CPLString().Printf(CPL_FRMT_GUIB,iLabelRecords)); if(!bAttachedLabel){ nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, "FILE_NAME", CPLGetFilename(osRasterFile)); } nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, ""); nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "/* Pointers to Data Objects */"); if(bAttachedLabel){ nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, CPLString().Printf("^%s",sObjectTag.c_str()), CPLString().Printf(CPL_FRMT_GUIB,iLabelRecords+1)); }else{ nWritingBytes += ISIS2Dataset::WriteKeyword( fpLabel, iLevel, CPLString().Printf("^%s",sObjectTag.c_str()), CPLString().Printf("(\"%s\",1)",CPLGetFilename(osRasterFile))); } if(EQUAL(sObjectTag, "QUBE")){ ISIS2Dataset::WriteQUBE_Information(fpLabel, iLevel, nWritingBytes, nXSize, nYSize, nBands, eType, pszInterleaving); } nWritingBytes += ISIS2Dataset::WriteFormatting( fpLabel, "END"); // check if file record is correct unsigned int q = nWritingBytes/RECORD_SIZE; if( q <= iLabelRecords){ // correct we add space after the label end for complete from iLabelRecords unsigned int nSpaceBytesToWrite = (unsigned int) (iLabelRecords * RECORD_SIZE - nWritingBytes); VSIFPrintfL(fpLabel,"%*c", nSpaceBytesToWrite, ' '); }else{ iLabelRecords = q+1; ISIS2Dataset::WriteLabel(osFilename, osRasterFile, sObjectTag, nXSize, nYSize, nBands, eType, iRecords, pszInterleaving, iLabelRecords); } VSIFCloseL( fpLabel ); return TRUE; } /************************************************************************/ /* WriteKeyword() */ /************************************************************************/ unsigned int ISIS2Dataset::WriteKeyword( VSILFILE *fpLabel, unsigned int iLevel, CPLString key, CPLString value) { CPLString tab = ""; iLevel *= 4; // each struct is idented by 4 spaces int ret = VSIFPrintfL(fpLabel,"%*s%s=%s\n", iLevel, tab.c_str(), key.c_str(), value.c_str()); return ret; } /************************************************************************/ /* WriteFormatting() */ /************************************************************************/ unsigned int ISIS2Dataset::WriteFormatting(VSILFILE *fpLabel, CPLString data) { int ret = VSIFPrintfL(fpLabel,"%s\n", data.c_str()); return ret; } /************************************************************************/ /* GDALRegister_ISIS2() */ /************************************************************************/ void GDALRegister_ISIS2() { GDALDriver *poDriver; if( GDALGetDriverByName( "ISIS2" ) == NULL ) { poDriver = new GDALDriver(); poDriver->SetDescription( "ISIS2" ); poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "USGS Astrogeology ISIS cube (Version 2)" ); poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "frmt_isis2.html" ); poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" ); poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Byte Int16 UInt16 Float32 Float64"); poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST, "<CreationOptionList>\n" " <Option name='LABELING_METHOD' type='string-select' default='ATTACHED'>\n" " <Value>ATTACHED</Value>" " <Value>DETACHED</Value>" " </Option>" " <Option name='IMAGE_EXTENSION' type='string' default='cub'/>\n" "</CreationOptionList>\n" ); poDriver->pfnIdentify = ISIS2Dataset::Identify; poDriver->pfnOpen = ISIS2Dataset::Open; poDriver->pfnCreate = ISIS2Dataset::Create; GetGDALDriverManager()->RegisterDriver( poDriver ); } }