CPLErr GeoRasterRasterBand::GetStatistics( int bApproxOK, int bForce, double *pdfMin, double *pdfMax, double *pdfMean, double *pdfStdDev ) { (void) bForce; (void) bApproxOK; char szMin[MAX_DOUBLE_STR_REP + 1]; char szMax[MAX_DOUBLE_STR_REP + 1]; char szMean[MAX_DOUBLE_STR_REP + 1]; char szMedian[MAX_DOUBLE_STR_REP + 1]; char szMode[MAX_DOUBLE_STR_REP + 1]; char szStdDev[MAX_DOUBLE_STR_REP + 1]; char szSampling[MAX_DOUBLE_STR_REP + 1]; if( ! bValidStats ) { bValidStats = poGeoRaster->GetStatistics( nBand, szMin, szMax, szMean, szMedian, szMode, szStdDev, szSampling ); } if( bValidStats ) { dfMin = CPLScanDouble( szMin, MAX_DOUBLE_STR_REP ); dfMax = CPLScanDouble( szMax, MAX_DOUBLE_STR_REP ); dfMean = CPLScanDouble( szMean, MAX_DOUBLE_STR_REP ); dfMedian = CPLScanDouble( szMedian, MAX_DOUBLE_STR_REP ); dfMode = CPLScanDouble( szMode, MAX_DOUBLE_STR_REP ); dfStdDev = CPLScanDouble( szStdDev, MAX_DOUBLE_STR_REP ); SetMetadataItem( "STATISTICS_MINIMUM", szMin ); SetMetadataItem( "STATISTICS_MAXIMUM", szMax ); SetMetadataItem( "STATISTICS_MEAN", szMean ); SetMetadataItem( "STATISTICS_MEDIAN", szMedian ); SetMetadataItem( "STATISTICS_MODE", szMode ); SetMetadataItem( "STATISTICS_STDDEV", szStdDev ); SetMetadataItem( "STATISTICS_SKIPFACTORX", szSampling ); SetMetadataItem( "STATISTICS_SKIPFACTORY", szSampling ); *pdfMin = dfMin; *pdfMax = dfMax; *pdfMean = dfMean; *pdfStdDev = dfStdDev; return CE_None; } return CE_Failure; }
GDALDataset *FASTDataset::Open( GDALOpenInfo * poOpenInfo ) { if( poOpenInfo->nHeaderBytes < 1024) return NULL; if( !EQUALN((const char *) poOpenInfo->pabyHeader + 52, "ACQUISITION DATE =", 18) && !EQUALN((const char *) poOpenInfo->pabyHeader + 36, "ACQUISITION DATE =", 18) ) return NULL; /* -------------------------------------------------------------------- */ /* Create a corresponding GDALDataset. */ /* -------------------------------------------------------------------- */ FASTDataset *poDS = new FASTDataset(); poDS->fpHeader = VSIFOpenL(poOpenInfo->pszFilename, "rb"); if (poDS->fpHeader == NULL) { delete poDS; return NULL; } poDS->pszFilename = poOpenInfo->pszFilename; poDS->pszDirname = CPLStrdup( CPLGetDirname( poOpenInfo->pszFilename ) ); /* -------------------------------------------------------------------- */ /* Read the administrative record. */ /* -------------------------------------------------------------------- */ char *pszHeader = static_cast<char *>( CPLMalloc( ADM_HEADER_SIZE + 1 ) ); size_t nBytesRead = 0; if( VSIFSeekL( poDS->fpHeader, 0, SEEK_SET ) >= 0 ) nBytesRead = VSIFReadL( pszHeader, 1, ADM_HEADER_SIZE, poDS->fpHeader ); if ( nBytesRead < ADM_MIN_HEADER_SIZE ) { CPLDebug( "FAST", "Header file too short. Reading failed" ); CPLFree(pszHeader); delete poDS; return NULL; } pszHeader[nBytesRead] = '\0'; // Read acquisition date char *pszTemp = GetValue( pszHeader, ACQUISITION_DATE, ACQUISITION_DATE_SIZE, TRUE ); if (pszTemp == NULL) { CPLDebug( "FAST", "Cannot get ACQUISITION_DATE, using empty value." ); pszTemp = CPLStrdup(""); } poDS->SetMetadataItem( "ACQUISITION_DATE", pszTemp ); CPLFree( pszTemp ); // Read satellite name (will read the first one only) pszTemp = GetValue( pszHeader, SATELLITE_NAME, SATELLITE_NAME_SIZE, TRUE ); if (pszTemp == NULL) { CPLDebug( "FAST", "Cannot get SATELLITE_NAME, using empty value." ); pszTemp = CPLStrdup( "" ); } poDS->SetMetadataItem( "SATELLITE", pszTemp ); if ( STARTS_WITH_CI(pszTemp, "LANDSAT") ) poDS->iSatellite = LANDSAT; // TODO(schwehr): Was this a bug that both are IRS? // else if ( STARTS_WITH_CI(pszTemp, "IRS") ) // poDS->iSatellite = IRS; else poDS->iSatellite = IRS; // TODO(schwehr): Should this be FAST_UNKNOWN? CPLFree( pszTemp ); // Read sensor name (will read the first one only) pszTemp = GetValue( pszHeader, SENSOR_NAME, SENSOR_NAME_SIZE, TRUE ); if (pszTemp == NULL) { CPLDebug( "FAST", "Cannot get SENSOR_NAME, using empty value." ); pszTemp = CPLStrdup(""); } poDS->SetMetadataItem( "SENSOR", pszTemp ); CPLFree( pszTemp ); // Read filenames poDS->nBands = 0; if (strstr( pszHeader, FILENAME ) == NULL) { if (strstr(pszHeader, "GENERATING AGENCY =EUROMAP")) { // If we don't find the FILENAME field, let's try with the Euromap // PAN / LISS3 / WIFS IRS filename convention. if ((EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS 1C") || EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS 1D")) && (EQUAL(poDS->GetMetadataItem("SENSOR"), "PAN") || EQUAL(poDS->GetMetadataItem("SENSOR"), "LISS3") || EQUAL(poDS->GetMetadataItem("SENSOR"), "WIFS"))) { poDS->TryEuromap_IRS_1C_1D_ChannelNameConvention(); } else if (EQUAL(poDS->GetMetadataItem("SATELLITE"), "CARTOSAT-1") && (EQUAL(poDS->GetMetadataItem("SENSOR"), "FORE") || EQUAL(poDS->GetMetadataItem("SENSOR"), "AFT"))) { // See appendix F in // http://www.euromap.de/download/p5fast_20050301.pdf const CPLString osSuffix = CPLGetExtension( poDS->pszFilename ); const char *papszBasenames[] = { "BANDF", "bandf", "BANDA", "banda" }; for ( int i = 0; i < 4; i++ ) { const CPLString osChannelFilename = CPLFormFilename( poDS->pszDirname, papszBasenames[i], osSuffix ); if( poDS->OpenChannel( osChannelFilename, 0 ) ) { poDS->nBands = 1; break; } } } else if( EQUAL(poDS->GetMetadataItem("SATELLITE"), "IRS P6") ) { // If BANDS_PRESENT="2345", the file bands are "BAND2.DAT", // "BAND3.DAT", etc. pszTemp = GetValue( pszHeader, BANDS_PRESENT, BANDS_PRESENT_SIZE, TRUE ); if (pszTemp) { for( int i=0; pszTemp[i] != '\0'; i++ ) { if( pszTemp[i] >= '2' && pszTemp[i] <= '5' ) { if( poDS->FOpenChannel( poDS->pszFilename, poDS->nBands, pszTemp[i] - '0')) poDS->nBands++; } } CPLFree( pszTemp ); } } } } // If the previous lookup for band files didn't success, fallback to the // standard way of finding them, either by the FILENAME field, either with // the usual patterns like bandX.dat, etc. if( !poDS->nBands ) { pszTemp = pszHeader; for ( int i = 0; i < 7; i++ ) { char *pszFilename = NULL ; if ( pszTemp ) pszTemp = strstr( pszTemp, FILENAME ); if ( pszTemp ) { // Skip the parameter name pszTemp += strlen(FILENAME); // Skip whitespaces and equal signs while ( *pszTemp == ' ' ) pszTemp++; while ( *pszTemp == '=' ) pszTemp++; pszFilename = CPLScanString( pszTemp, FILENAME_SIZE, TRUE, FALSE ); } else pszTemp = NULL; if ( poDS->FOpenChannel( pszFilename, poDS->nBands, poDS->nBands + 1 ) ) poDS->nBands++; if ( pszFilename ) CPLFree( pszFilename ); } } if ( !poDS->nBands ) { CPLError( CE_Failure, CPLE_NotSupported, "Failed to find and open band data files." ); CPLFree(pszHeader); delete poDS; return NULL; } // Read number of pixels/lines and bit depth pszTemp = GetValue( pszHeader, PIXELS, PIXELS_SIZE, FALSE ); if ( pszTemp ) { poDS->nRasterXSize = atoi( pszTemp ); CPLFree( pszTemp ); } else { CPLDebug( "FAST", "Failed to find number of pixels in line." ); CPLFree(pszHeader); delete poDS; return NULL; } pszTemp = GetValue( pszHeader, LINES1, LINES_SIZE, FALSE ); if ( !pszTemp ) pszTemp = GetValue( pszHeader, LINES2, LINES_SIZE, FALSE ); if ( pszTemp ) { poDS->nRasterYSize = atoi( pszTemp ); CPLFree( pszTemp ); } else { CPLDebug( "FAST", "Failed to find number of lines in raster." ); CPLFree(pszHeader); delete poDS; return NULL; } if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) { CPLFree(pszHeader); delete poDS; return NULL; } pszTemp = GetValue( pszHeader, BITS_PER_PIXEL, BITS_PER_PIXEL_SIZE, FALSE ); if ( pszTemp ) { switch( atoi(pszTemp) ) { case 8: default: poDS->eDataType = GDT_Byte; break; // For a strange reason, some Euromap products declare 10 bits // output, but are 16 bits. case 10: case 16: poDS->eDataType = GDT_UInt16; break; } CPLFree( pszTemp ); } else { poDS->eDataType = GDT_Byte; } /* -------------------------------------------------------------------- */ /* Read radiometric record. */ /* -------------------------------------------------------------------- */ const char *pszFirst = NULL; const char *pszSecond = NULL; // Read gains and biases. This is a trick! pszTemp = strstr( pszHeader, "BIASES" );// It may be "BIASES AND GAINS" // or "GAINS AND BIASES" const char* pszGains = strstr( pszHeader, "GAINS" ); if( pszTemp == NULL || pszGains == NULL ) { CPLDebug( "FAST", "No BIASES and/or GAINS" ); CPLFree( pszHeader ); delete poDS; return NULL; } if ( pszTemp > pszGains ) { pszFirst = "GAIN%d"; pszSecond = "BIAS%d"; } else { pszFirst = "BIAS%d"; pszSecond = "GAIN%d"; } // Now search for the first number occurrence after that string. for ( int i = 1; i <= poDS->nBands; i++ ) { char *pszValue = NULL; size_t nValueLen = VALUE_SIZE; pszTemp = strpbrk( pszTemp, "-.0123456789" ); if ( pszTemp ) { nValueLen = strspn( pszTemp, "+-.0123456789" ); pszValue = CPLScanString( pszTemp, static_cast<int>(nValueLen), TRUE, TRUE ); poDS->SetMetadataItem( CPLSPrintf(pszFirst, i ), pszValue ); CPLFree( pszValue ); } pszTemp += nValueLen; pszTemp = strpbrk( pszTemp, "-.0123456789" ); if ( pszTemp ) { nValueLen = strspn( pszTemp, "+-.0123456789" ); pszValue = CPLScanString( pszTemp, static_cast<int>(nValueLen), TRUE, TRUE ); poDS->SetMetadataItem( CPLSPrintf(pszSecond, i ), pszValue ); CPLFree( pszValue ); } pszTemp += nValueLen; } /* -------------------------------------------------------------------- */ /* Read geometric record. */ /* -------------------------------------------------------------------- */ // Coordinates of pixel's centers double dfULX = 0.0; double dfULY = 0.0; double dfURX = 0.0; double dfURY = 0.0; double dfLLX = 0.0; double dfLLY = 0.0; double dfLRX = 0.0; double dfLRY = 0.0; // Read projection name pszTemp = GetValue( pszHeader, PROJECTION_NAME, PROJECTION_NAME_SIZE, FALSE ); long iProjSys = 0; if ( pszTemp && !EQUAL( pszTemp, "" ) ) iProjSys = USGSMnemonicToCode( pszTemp ); else iProjSys = 1L; // UTM by default CPLFree( pszTemp ); // Read ellipsoid name pszTemp = GetValue( pszHeader, ELLIPSOID_NAME, ELLIPSOID_NAME_SIZE, FALSE ); long iDatum = 0; // Clarke, 1866 (NAD1927) by default. if ( pszTemp && !EQUAL( pszTemp, "" ) ) iDatum = USGSEllipsoidToCode( pszTemp ); CPLFree( pszTemp ); // Read zone number. pszTemp = GetValue( pszHeader, ZONE_NUMBER, ZONE_NUMBER_SIZE, FALSE ); long iZone = 0; if ( pszTemp && !EQUAL( pszTemp, "" ) ) iZone = atoi( pszTemp ); CPLFree( pszTemp ); // Read 15 USGS projection parameters double adfProjParms[15] = { 0.0 }; pszTemp = strstr( pszHeader, USGS_PARAMETERS ); if ( pszTemp && !EQUAL( pszTemp, "" ) ) { pszTemp += strlen( USGS_PARAMETERS ); for ( int i = 0; i < 15; i++ ) { pszTemp = strpbrk( pszTemp, "-.0123456789" ); if ( pszTemp ) adfProjParms[i] = CPLScanDouble( pszTemp, VALUE_SIZE ); pszTemp = strpbrk( pszTemp, " \t" ); } } // Coordinates should follow the word "PROJECTION", otherwise we can // be confused by other occurrences of the corner keywords. char *pszGeomRecord = strstr( pszHeader, "PROJECTION" ); // Read corner coordinates pszTemp = strstr( pszGeomRecord, CORNER_UPPER_LEFT ); if ( pszTemp && !EQUAL( pszTemp, "" ) ) { pszTemp += strlen( CORNER_UPPER_LEFT ) + 28; dfULX = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); pszTemp += CORNER_VALUE_SIZE + 1; dfULY = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); } pszTemp = strstr( pszGeomRecord, CORNER_UPPER_RIGHT ); if ( pszTemp && !EQUAL( pszTemp, "" ) ) { pszTemp += strlen( CORNER_UPPER_RIGHT ) + 28; dfURX = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); pszTemp += CORNER_VALUE_SIZE + 1; dfURY = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); } pszTemp = strstr( pszGeomRecord, CORNER_LOWER_LEFT ); if ( pszTemp && !EQUAL( pszTemp, "" ) ) { pszTemp += strlen( CORNER_LOWER_LEFT ) + 28; dfLLX = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); pszTemp += CORNER_VALUE_SIZE + 1; dfLLY = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); } pszTemp = strstr( pszGeomRecord, CORNER_LOWER_RIGHT ); if ( pszTemp && !EQUAL( pszTemp, "" ) ) { pszTemp += strlen( CORNER_LOWER_RIGHT ) + 28; dfLRX = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); pszTemp += CORNER_VALUE_SIZE + 1; dfLRY = CPLScanDouble( pszTemp, CORNER_VALUE_SIZE ); } if ( dfULX != 0.0 && dfULY != 0.0 && dfURX != 0.0 && dfURY != 0.0 && dfLLX != 0.0 && dfLLY != 0.0 && dfLRX != 0.0 && dfLRY != 0.0 ) { // Strip out zone number from the easting values, if either if ( dfULX >= 1000000.0 ) dfULX -= static_cast<double>( iZone ) * 1000000.0; if ( dfURX >= 1000000.0 ) dfURX -= static_cast<double>( iZone ) * 1000000.0; if ( dfLLX >= 1000000.0 ) dfLLX -= static_cast<double>( iZone ) * 1000000.0; if ( dfLRX >= 1000000.0 ) dfLRX -= static_cast<double>( iZone ) * 1000000.0; // In EOSAT FAST Rev C, the angles are in decimal degrees // otherwise they are in packed DMS format. const int bAnglesInPackedDMSFormat = strstr( pszHeader, "REV C" ) == NULL; // Create projection definition OGRSpatialReference oSRS; OGRErr eErr = oSRS.importFromUSGS( iProjSys, iZone, adfProjParms, iDatum, bAnglesInPackedDMSFormat ); if ( eErr != OGRERR_NONE ) CPLDebug( "FAST", "Import projection from USGS failed: %d", eErr ); oSRS.SetLinearUnits( SRS_UL_METER, 1.0 ); // Read datum name pszTemp = GetValue( pszHeader, DATUM_NAME, DATUM_NAME_SIZE, FALSE ); if ( pszTemp ) { if ( EQUAL( pszTemp, "WGS84" ) ) oSRS.SetWellKnownGeogCS( "WGS84" ); else if ( EQUAL( pszTemp, "NAD27" ) ) oSRS.SetWellKnownGeogCS( "NAD27" ); else if ( EQUAL( pszTemp, "NAD83" ) ) oSRS.SetWellKnownGeogCS( "NAD83" ); CPLFree( pszTemp ); } else { // Reasonable fallback oSRS.SetWellKnownGeogCS( "WGS84" ); } if ( poDS->pszProjection ) CPLFree( poDS->pszProjection ); eErr = oSRS.exportToWkt( &poDS->pszProjection ); if ( eErr != OGRERR_NONE ) CPLDebug("FAST", "Export projection to WKT USGS failed: %d", eErr); // Generate GCPs GDAL_GCP *pasGCPList = static_cast<GDAL_GCP *>( CPLCalloc( sizeof( GDAL_GCP ), 4 ) ); GDALInitGCPs( 4, pasGCPList ); CPLFree(pasGCPList[0].pszId); CPLFree(pasGCPList[1].pszId); CPLFree(pasGCPList[2].pszId); CPLFree(pasGCPList[3].pszId); /* Let's order the GCP in TL, TR, BR, BL order to benefit from the */ /* GDALGCPsToGeoTransform optimization */ pasGCPList[0].pszId = CPLStrdup("UPPER_LEFT"); pasGCPList[0].dfGCPX = dfULX; pasGCPList[0].dfGCPY = dfULY; pasGCPList[0].dfGCPZ = 0.0; pasGCPList[0].dfGCPPixel = 0.5; pasGCPList[0].dfGCPLine = 0.5; pasGCPList[1].pszId = CPLStrdup("UPPER_RIGHT"); pasGCPList[1].dfGCPX = dfURX; pasGCPList[1].dfGCPY = dfURY; pasGCPList[1].dfGCPZ = 0.0; pasGCPList[1].dfGCPPixel = poDS->nRasterXSize-0.5; pasGCPList[1].dfGCPLine = 0.5; pasGCPList[2].pszId = CPLStrdup("LOWER_RIGHT"); pasGCPList[2].dfGCPX = dfLRX; pasGCPList[2].dfGCPY = dfLRY; pasGCPList[2].dfGCPZ = 0.0; pasGCPList[2].dfGCPPixel = poDS->nRasterXSize-0.5; pasGCPList[2].dfGCPLine = poDS->nRasterYSize-0.5; pasGCPList[3].pszId = CPLStrdup("LOWER_LEFT"); pasGCPList[3].dfGCPX = dfLLX; pasGCPList[3].dfGCPY = dfLLY; pasGCPList[3].dfGCPZ = 0.0; pasGCPList[3].dfGCPPixel = 0.5; pasGCPList[3].dfGCPLine = poDS->nRasterYSize-0.5; // Calculate transformation matrix, if accurate const bool transform_ok = CPL_TO_BOOL( GDALGCPsToGeoTransform( 4, pasGCPList, poDS->adfGeoTransform, 0 ) ); if( !transform_ok ) { poDS->adfGeoTransform[0] = 0.0; poDS->adfGeoTransform[1] = 1.0; poDS->adfGeoTransform[2] = 0.0; poDS->adfGeoTransform[3] = 0.0; poDS->adfGeoTransform[4] = 0.0; poDS->adfGeoTransform[5] = 1.0; if ( poDS->pszProjection ) CPLFree( poDS->pszProjection ); poDS->pszProjection = CPLStrdup(""); } GDALDeinitGCPs(4, pasGCPList); CPLFree(pasGCPList); } /* -------------------------------------------------------------------- */ /* Create band information objects. */ /* -------------------------------------------------------------------- */ const int nPixelOffset = GDALGetDataTypeSize(poDS->eDataType) / 8; const int nLineOffset = poDS->nRasterXSize * nPixelOffset; for( int i = 1; i <= poDS->nBands; i++ ) { poDS->SetBand( i, new FASTRasterBand( poDS, i, poDS->fpChannels[i - 1], 0, nPixelOffset, nLineOffset, poDS->eDataType, TRUE)); } CPLFree( pszHeader ); /* -------------------------------------------------------------------- */ /* Initialize any PAM information. */ /* -------------------------------------------------------------------- */ poDS->SetDescription( poOpenInfo->pszFilename ); poDS->TryLoadXML(); // opens overviews. poDS->oOvManager.Initialize(poDS, poDS->pszFilename); /* -------------------------------------------------------------------- */ /* Confirm the requested access is supported. */ /* -------------------------------------------------------------------- */ if( poOpenInfo->eAccess == GA_Update ) { delete poDS; CPLError( CE_Failure, CPLE_NotSupported, "The FAST driver does not support update access to existing" " datasets." ); return NULL; } return poDS; }