ultimatepp/bazaar/plugin/gdal/frmts/pds/isis2dataset.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

1221 lines
48 KiB
C++

/******************************************************************************
* $Id: isis2dataset.cpp 27942 2014-11-11 00:57:41Z rouault $
*
* 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 27942 2014-11-11 00:57:41Z rouault $");
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) CPLAtof(value) * 1000.0; /* convert from km to m */
dfYDim = (float) CPLAtof(value) * 1000.0 * -1;
}
/*********** Grab LINE_PROJECTION_OFFSET ************/
value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET");
if (strlen(value) > 0) {
yulcenter = (float) CPLAtof(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) CPLAtof(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 =
CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) * 1000.0;
/*********** Grab semi-minor ************/
semi_minor =
CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) * 1000.0;
/*********** Grab CENTER_LAT ************/
center_lat =
CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.CENTER_LATITUDE"));
/*********** Grab CENTER_LON ************/
center_lon =
CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.CENTER_LONGITUDE"));
/*********** Grab 1st std parallel ************/
first_std_parallel =
CPLAtof(poDS->GetKeyword( "QUBE.IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL"));
/*********** Grab 2nd std parallel ************/
second_std_parallel =
CPLAtof(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_DCAP_RASTER, "YES" );
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 );
}
}