ultimatepp/bazaar/plugin/gdal/frmts/raw/lcpdataset.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

1784 lines
63 KiB
C++

/******************************************************************************
* $Id: lcpdataset.cpp 28053 2014-12-04 09:31:07Z rouault $
*
* Project: LCP Driver
* Purpose: FARSITE v.4 Landscape file (.lcp) reader for GDAL
* Author: Chris Toney
*
******************************************************************************
* Copyright (c) 2008, Chris Toney
* Copyright (c) 2008-2011, Even Rouault <even dot rouault at mines-paris dot org>
* Copyright (c) 2013, Kyle Shannon <kyle at pobox dot com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include "rawdataset.h"
#include "cpl_port.h"
#include "cpl_string.h"
#include "ogr_spatialref.h"
CPL_CVSID("$Id: lcpdataset.cpp 28053 2014-12-04 09:31:07Z rouault $");
CPL_C_START
void GDALRegister_LCP(void);
CPL_C_END
#define LCP_HEADER_SIZE 7316
#define LCP_MAX_BANDS 10
#define LCP_MAX_PATH 256
#define LCP_MAX_DESC 512
#define LCP_MAX_CLASSES 100
/************************************************************************/
/* ==================================================================== */
/* LCPDataset */
/* ==================================================================== */
/************************************************************************/
class LCPDataset : public RawDataset
{
VSILFILE *fpImage; // image data file.
char pachHeader[LCP_HEADER_SIZE];
CPLString osPrjFilename;
char *pszProjection;
static CPLErr ClassifyBandData( GDALRasterBand *poBand,
GInt32 *pnNumClasses,
GInt32 *panClasses );
public:
LCPDataset();
~LCPDataset();
virtual char **GetFileList(void);
virtual CPLErr GetGeoTransform( double * );
static int Identify( GDALOpenInfo * );
static GDALDataset *Open( GDALOpenInfo * );
static GDALDataset *CreateCopy( const char * pszFilename,
GDALDataset *poSrcDS,
int bStrict, char ** papszOptions,
GDALProgressFunc pfnProgress,
void * pProgressData );
virtual const char *GetProjectionRef(void);
int bHaveProjection;
};
/************************************************************************/
/* LCPDataset() */
/************************************************************************/
LCPDataset::LCPDataset()
{
fpImage = NULL;
pszProjection = CPLStrdup( "" );
bHaveProjection = FALSE;
}
/************************************************************************/
/* ~LCPDataset() */
/************************************************************************/
LCPDataset::~LCPDataset()
{
FlushCache();
if( fpImage != NULL )
VSIFCloseL( fpImage );
CPLFree(pszProjection);
}
/************************************************************************/
/* GetGeoTransform() */
/************************************************************************/
CPLErr LCPDataset::GetGeoTransform( double * padfTransform )
{
double dfEast, dfWest, dfNorth, dfSouth, dfCellX, dfCellY;
memcpy(&dfEast, pachHeader + 4172, sizeof(double));
memcpy(&dfWest, pachHeader + 4180, sizeof(double));
memcpy(&dfNorth, pachHeader + 4188, sizeof(double));
memcpy(&dfSouth, pachHeader + 4196, sizeof(double));
memcpy(&dfCellX, pachHeader + 4208, sizeof(double));
memcpy(&dfCellY, pachHeader + 4216, sizeof(double));
CPL_LSBPTR64(&dfEast);
CPL_LSBPTR64(&dfWest);
CPL_LSBPTR64(&dfNorth);
CPL_LSBPTR64(&dfSouth);
CPL_LSBPTR64(&dfCellX);
CPL_LSBPTR64(&dfCellY);
padfTransform[0] = dfWest;
padfTransform[3] = dfNorth;
padfTransform[1] = dfCellX;
padfTransform[2] = 0.0;
padfTransform[4] = 0.0;
padfTransform[5] = -1 * dfCellY;
return CE_None;
}
/************************************************************************/
/* Identify() */
/************************************************************************/
int LCPDataset::Identify( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* Verify that this is a FARSITE v.4 LCP file */
/* -------------------------------------------------------------------- */
if( poOpenInfo->nHeaderBytes < 50 )
return FALSE;
/* check if first three fields have valid data */
if( (CPL_LSBINT32PTR(poOpenInfo->pabyHeader) != 20
&& CPL_LSBINT32PTR(poOpenInfo->pabyHeader) != 21)
|| (CPL_LSBINT32PTR(poOpenInfo->pabyHeader+4) != 20
&& CPL_LSBINT32PTR(poOpenInfo->pabyHeader+4) != 21)
|| (CPL_LSBINT32PTR(poOpenInfo->pabyHeader+8) < -90
|| CPL_LSBINT32PTR(poOpenInfo->pabyHeader+8) > 90) )
{
return FALSE;
}
return TRUE;
}
/************************************************************************/
/* GetFileList() */
/************************************************************************/
char **LCPDataset::GetFileList()
{
char **papszFileList = GDALPamDataset::GetFileList();
if( bHaveProjection )
{
papszFileList = CSLAddString( papszFileList, osPrjFilename );
}
return papszFileList;
}
/************************************************************************/
/* Open() */
/************************************************************************/
GDALDataset *LCPDataset::Open( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* Verify that this is a FARSITE LCP file */
/* -------------------------------------------------------------------- */
if( !Identify( poOpenInfo ) )
return NULL;
/* -------------------------------------------------------------------- */
/* Confirm the requested access is supported. */
/* -------------------------------------------------------------------- */
if( poOpenInfo->eAccess == GA_Update )
{
CPLError( CE_Failure, CPLE_NotSupported,
"The LCP driver does not support update access to existing"
" datasets." );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Create a corresponding GDALDataset. */
/* -------------------------------------------------------------------- */
LCPDataset *poDS;
VSILFILE *fpImage;
fpImage = VSIFOpenL(poOpenInfo->pszFilename, "rb");
if (fpImage == NULL)
return NULL;
poDS = new LCPDataset();
poDS->fpImage = fpImage;
/* -------------------------------------------------------------------- */
/* Read the header and extract some information. */
/* -------------------------------------------------------------------- */
int bHaveCrownFuels, bHaveGroundFuels;
int nBands, i;
long nWidth = -1, nHeight = -1;
int nTemp, nTemp2;
char szTemp[32];
char* pszList;
VSIFSeekL( poDS->fpImage, 0, SEEK_SET );
if (VSIFReadL( poDS->pachHeader, 1, LCP_HEADER_SIZE, poDS->fpImage ) != LCP_HEADER_SIZE)
{
CPLError(CE_Failure, CPLE_FileIO, "File too short");
delete poDS;
return NULL;
}
nWidth = CPL_LSBINT32PTR (poDS->pachHeader + 4164);
nHeight = CPL_LSBINT32PTR (poDS->pachHeader + 4168);
poDS->nRasterXSize = nWidth;
poDS->nRasterYSize = nHeight;
if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
{
delete poDS;
return NULL;
}
// crown fuels = canopy height, canopy base height, canopy bulk density
// 21 = have them, 20 = don't have them
bHaveCrownFuels = ( CPL_LSBINT32PTR (poDS->pachHeader + 0) - 20 );
// ground fuels = duff loading, coarse woody
bHaveGroundFuels = ( CPL_LSBINT32PTR (poDS->pachHeader + 4) - 20 );
if( bHaveCrownFuels )
{
if( bHaveGroundFuels )
nBands = 10;
else
nBands = 8;
}
else
{
if( bHaveGroundFuels )
nBands = 7;
else
nBands = 5;
}
// add dataset-level metadata
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 8);
sprintf(szTemp, "%d", nTemp);
poDS->SetMetadataItem( "LATITUDE", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 4204);
if ( nTemp == 0 )
poDS->SetMetadataItem( "LINEAR_UNIT", "Meters" );
if ( nTemp == 1 )
poDS->SetMetadataItem( "LINEAR_UNIT", "Feet" );
poDS->pachHeader[LCP_HEADER_SIZE-1] = '\0';
poDS->SetMetadataItem( "DESCRIPTION", poDS->pachHeader + 6804 );
/* -------------------------------------------------------------------- */
/* Create band information objects. */
/* -------------------------------------------------------------------- */
int iPixelSize;
iPixelSize = nBands * 2;
int bNativeOrder;
if (nWidth > INT_MAX / iPixelSize)
{
CPLError( CE_Failure, CPLE_AppDefined, "Int overflow occured");
delete poDS;
return NULL;
}
#ifdef CPL_LSB
bNativeOrder = TRUE;
#else
bNativeOrder = FALSE;
#endif
pszList = (char*)CPLMalloc(2048);
pszList[0] = '\0';
for( int iBand = 1; iBand <= nBands; iBand++ )
{
GDALRasterBand *poBand = NULL;
poBand = new RawRasterBand(
poDS, iBand, poDS->fpImage, LCP_HEADER_SIZE + ((iBand-1)*2),
iPixelSize, iPixelSize * nWidth, GDT_Int16, bNativeOrder, TRUE );
poDS->SetBand(iBand, poBand);
switch ( iBand ) {
case 1:
poBand->SetDescription("Elevation");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4224);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "ELEVATION_UNIT", szTemp );
if ( nTemp == 0 )
poBand->SetMetadataItem( "ELEVATION_UNIT_NAME", "Meters" );
if ( nTemp == 1 )
poBand->SetMetadataItem( "ELEVATION_UNIT_NAME", "Feet" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 44);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "ELEVATION_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 48);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "ELEVATION_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 52);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "ELEVATION_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 4244 + 255) = '\0';
poBand->SetMetadataItem( "ELEVATION_FILE", poDS->pachHeader + 4244 );
break;
case 2:
poBand->SetDescription("Slope");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4226);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "SLOPE_UNIT", szTemp );
if ( nTemp == 0 )
poBand->SetMetadataItem( "SLOPE_UNIT_NAME", "Degrees" );
if ( nTemp == 1 )
poBand->SetMetadataItem( "SLOPE_UNIT_NAME", "Percent" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 456);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "SLOPE_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 460);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "SLOPE_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 464);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "SLOPE_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 4500 + 255) = '\0';
poBand->SetMetadataItem( "SLOPE_FILE", poDS->pachHeader + 4500 );
break;
case 3:
poBand->SetDescription("Aspect");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4228);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "ASPECT_UNIT", szTemp );
if ( nTemp == 0 )
poBand->SetMetadataItem( "ASPECT_UNIT_NAME", "Grass categories" );
if ( nTemp == 1 )
poBand->SetMetadataItem( "ASPECT_UNIT_NAME", "Grass degrees" );
if ( nTemp == 2 )
poBand->SetMetadataItem( "ASPECT_UNIT_NAME", "Azimuth degrees" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 868);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "ASPECT_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 872);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "ASPECT_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 876);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "ASPECT_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 4756 + 255) = '\0';
poBand->SetMetadataItem( "ASPECT_FILE", poDS->pachHeader + 4756 );
break;
case 4:
int nMinFM, nMaxFM;
poBand->SetDescription("Fuel models");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4230);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "FUEL_MODEL_OPTION", szTemp );
if ( nTemp == 0 )
poBand->SetMetadataItem( "FUEL_MODEL_OPTION_DESC", "no custom models AND no conversion file needed" );
if ( nTemp == 1 )
poBand->SetMetadataItem( "FUEL_MODEL_OPTION_DESC", "custom models BUT no conversion file needed" );
if ( nTemp == 2 )
poBand->SetMetadataItem( "FUEL_MODEL_OPTION_DESC", "no custom models BUT conversion file needed" );
if ( nTemp == 3 )
poBand->SetMetadataItem( "FUEL_MODEL_OPTION_DESC", "custom models AND conversion file needed" );
nMinFM = CPL_LSBINT32PTR (poDS->pachHeader + 1280);
sprintf(szTemp, "%d", nMinFM);
poBand->SetMetadataItem( "FUEL_MODEL_MIN", szTemp );
nMaxFM = CPL_LSBINT32PTR (poDS->pachHeader + 1284);
sprintf(szTemp, "%d", nMaxFM);
poBand->SetMetadataItem( "FUEL_MODEL_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 1288);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "FUEL_MODEL_NUM_CLASSES", szTemp );
if (nTemp > 0 && nTemp <= 100) {
strcpy(pszList, "");
for ( i = 0; i <= nTemp; i++ ) {
nTemp2 = CPL_LSBINT32PTR (poDS->pachHeader + (1292+(i*4))) ;
if ( nTemp2 >= nMinFM && nTemp2 <= nMaxFM ) {
sprintf(szTemp, "%d", nTemp2);
strcat(pszList, szTemp);
if (i < (nTemp) )
strcat(pszList, ",");
}
}
}
poBand->SetMetadataItem( "FUEL_MODEL_VALUES", pszList );
*(poDS->pachHeader + 5012 + 255) = '\0';
poBand->SetMetadataItem( "FUEL_MODEL_FILE", poDS->pachHeader + 5012 );
break;
case 5:
poBand->SetDescription("Canopy cover");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4232);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CANOPY_COV_UNIT", szTemp );
if ( nTemp == 0 )
poBand->SetMetadataItem( "CANOPY_COV_UNIT_NAME", "Categories (0-4)" );
if ( nTemp == 1 )
poBand->SetMetadataItem( "CANOPY_COV_UNIT_NAME", "Percent" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 1692);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CANOPY_COV_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 1696);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CANOPY_COV_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 1700);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CANOPY_COV_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 5268 + 255) = '\0';
poBand->SetMetadataItem( "CANOPY_COV_FILE", poDS->pachHeader + 5268 );
break;
case 6:
if(bHaveCrownFuels) {
poBand->SetDescription("Canopy height");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4234);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CANOPY_HT_UNIT", szTemp );
if ( nTemp == 1 )
poBand->SetMetadataItem( "CANOPY_HT_UNIT_NAME", "Meters" );
if ( nTemp == 2 )
poBand->SetMetadataItem( "CANOPY_HT_UNIT_NAME", "Feet" );
if ( nTemp == 3 )
poBand->SetMetadataItem( "CANOPY_HT_UNIT_NAME", "Meters x 10" );
if ( nTemp == 4 )
poBand->SetMetadataItem( "CANOPY_HT_UNIT_NAME", "Feet x 10" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2104);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CANOPY_HT_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2108);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CANOPY_HT_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2112);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CANOPY_HT_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 5524 + 255) = '\0';
poBand->SetMetadataItem( "CANOPY_HT_FILE", poDS->pachHeader + 5524 );
}
else {
poBand->SetDescription("Duff");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4240);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "DUFF_UNIT", szTemp );
if ( nTemp == 1 )
poBand->SetMetadataItem( "DUFF_UNIT_NAME", "Mg/ha" );
if ( nTemp == 2 )
poBand->SetMetadataItem( "DUFF_UNIT_NAME", "t/ac" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3340);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "DUFF_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3344);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "DUFF_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3348);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "DUFF_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 6292 + 255) = '\0';
poBand->SetMetadataItem( "DUFF_FILE", poDS->pachHeader + 6292 );
}
break;
case 7:
if(bHaveCrownFuels) {
poBand->SetDescription("Canopy base height");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4236);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CBH_UNIT", szTemp );
if ( nTemp == 1 )
poBand->SetMetadataItem( "CBH_UNIT_NAME", "Meters" );
if ( nTemp == 2 )
poBand->SetMetadataItem( "CBH_UNIT_NAME", "Feet" );
if ( nTemp == 3 )
poBand->SetMetadataItem( "CBH_UNIT_NAME", "Meters x 10" );
if ( nTemp == 4 )
poBand->SetMetadataItem( "CBH_UNIT_NAME", "Feet x 10" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2516);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CBH_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2520);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CBH_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2524);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CBH_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 5780 + 255) = '\0';
poBand->SetMetadataItem( "CBH_FILE", poDS->pachHeader + 5780 );
}
else {
poBand->SetDescription("Coarse woody debris");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4242);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CWD_OPTION", szTemp );
//if ( nTemp == 1 )
// poBand->SetMetadataItem( "CWD_UNIT_DESC", "?" );
//if ( nTemp == 2 )
// poBand->SetMetadataItem( "CWD_UNIT_DESC", "?" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3752);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CWD_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3756);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CWD_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3760);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CWD_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 6548 + 255) = '\0';
poBand->SetMetadataItem( "CWD_FILE", poDS->pachHeader + 6548 );
}
break;
case 8:
poBand->SetDescription("Canopy bulk density");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4238);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CBD_UNIT", szTemp );
if ( nTemp == 1 )
poBand->SetMetadataItem( "CBD_UNIT_NAME", "kg/m^3" );
if ( nTemp == 2 )
poBand->SetMetadataItem( "CBD_UNIT_NAME", "lb/ft^3" );
if ( nTemp == 3 )
poBand->SetMetadataItem( "CBD_UNIT_NAME", "kg/m^3 x 100" );
if ( nTemp == 4 )
poBand->SetMetadataItem( "CBD_UNIT_NAME", "lb/ft^3 x 1000" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2928);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CBD_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2932);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CBD_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 2936);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CBD_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 6036 + 255) = '\0';
poBand->SetMetadataItem( "CBD_FILE", poDS->pachHeader + 6036 );
break;
case 9:
poBand->SetDescription("Duff");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4240);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "DUFF_UNIT", szTemp );
if ( nTemp == 1 )
poBand->SetMetadataItem( "DUFF_UNIT_NAME", "Mg/ha" );
if ( nTemp == 2 )
poBand->SetMetadataItem( "DUFF_UNIT_NAME", "t/ac" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3340);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "DUFF_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3344);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "DUFF_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3348);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "DUFF_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 6292 + 255) = '\0';
poBand->SetMetadataItem( "DUFF_FILE", poDS->pachHeader + 6292 );
break;
case 10:
poBand->SetDescription("Coarse woody debris");
nTemp = CPL_LSBINT16PTR (poDS->pachHeader + 4242);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CWD_OPTION", szTemp );
//if ( nTemp == 1 )
// poBand->SetMetadataItem( "CWD_UNIT_DESC", "?" );
//if ( nTemp == 2 )
// poBand->SetMetadataItem( "CWD_UNIT_DESC", "?" );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3752);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CWD_MIN", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3756);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CWD_MAX", szTemp );
nTemp = CPL_LSBINT32PTR (poDS->pachHeader + 3760);
sprintf(szTemp, "%d", nTemp);
poBand->SetMetadataItem( "CWD_NUM_CLASSES", szTemp );
*(poDS->pachHeader + 6548 + 255) = '\0';
poBand->SetMetadataItem( "CWD_FILE", poDS->pachHeader + 6548 );
break;
}
}
/* -------------------------------------------------------------------- */
/* Try to read projection file. */
/* -------------------------------------------------------------------- */
char *pszDirname, *pszBasename;
VSIStatBufL sStatBuf;
pszDirname = CPLStrdup(CPLGetPath(poOpenInfo->pszFilename));
pszBasename = CPLStrdup(CPLGetBasename(poOpenInfo->pszFilename));
poDS->osPrjFilename = CPLFormFilename( pszDirname, pszBasename, "prj" );
int nRet = VSIStatL( poDS->osPrjFilename, &sStatBuf );
if( nRet != 0 && VSIIsCaseSensitiveFS(poDS->osPrjFilename))
{
poDS->osPrjFilename = CPLFormFilename( pszDirname, pszBasename, "PRJ" );
nRet = VSIStatL( poDS->osPrjFilename, &sStatBuf );
}
if( nRet == 0 )
{
OGRSpatialReference oSRS;
char** papszPrj = CSLLoad( poDS->osPrjFilename );
CPLDebug( "LCP", "Loaded SRS from %s",
poDS->osPrjFilename.c_str() );
if( oSRS.importFromESRI( papszPrj ) == OGRERR_NONE )
{
CPLFree( poDS->pszProjection );
oSRS.exportToWkt( &(poDS->pszProjection) );
poDS->bHaveProjection = TRUE;
}
CSLDestroy(papszPrj);
}
CPLFree( pszDirname );
CPLFree( pszBasename );
/* -------------------------------------------------------------------- */
/* Initialize any PAM information. */
/* -------------------------------------------------------------------- */
poDS->SetDescription( poOpenInfo->pszFilename );
poDS->TryLoadXML();
/* -------------------------------------------------------------------- */
/* Check for external overviews. */
/* -------------------------------------------------------------------- */
poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles() );
CPLFree(pszList);
return( poDS );
}
/************************************************************************/
/* ClassifyBandData() */
/* Classify a band and store 99 or fewer unique values. If there are */
/* more than 99 unique values, then set pnNumClasses to -1 as a flag */
/* that represents this. These are legacy values in the header, and */
/* while we should never deprecate them, we could possibly not */
/* calculate them by default. */
/************************************************************************/
CPLErr LCPDataset::ClassifyBandData( GDALRasterBand *poBand,
GInt32 *pnNumClasses,
GInt32 *panClasses )
{
if( pnNumClasses == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid pointer for panClasses" );
return CE_Failure;
}
if( panClasses == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid pointer for panClasses" );
*pnNumClasses = -1;
return CE_Failure;
}
if( poBand == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid band passed to ClassifyBandData()" );
*pnNumClasses = -1;
memset( panClasses, 0, 400 );
return CE_Failure;
}
int nXSize = poBand->GetXSize();
int nYSize = poBand->GetYSize();
double dfMax, dfDummy;
poBand->GetStatistics( FALSE, TRUE, &dfDummy, &dfMax, &dfDummy, &dfDummy );
int nSpan = (int)dfMax;
GInt16 *panValues = (GInt16*)CPLMalloc( sizeof( GInt16 ) * nXSize );
GByte *pabyFlags = (GByte*)CPLMalloc( sizeof( GByte ) * nSpan + 1 );
memset( pabyFlags, 0, nSpan + 1 );
int nFound = 0;
int bTooMany = FALSE;
CPLErr eErr = CE_None;
for( int iLine = 0; iLine < nYSize; iLine++ )
{
eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
panValues, nXSize, 1,
GDT_Int16, 0, 0, NULL );
for( int iPixel = 0; iPixel < nXSize; iPixel++ )
{
if( panValues[iPixel] == -9999 )
{
continue;
}
if( nFound > 99 )
{
CPLDebug( "LCP", "Found more that 100 unique values in " \
"band %d. Not 'classifying' the data.",
poBand->GetBand() );
nFound = -1;
bTooMany = TRUE;
break;
}
if( bTooMany )
{
break;
}
if( pabyFlags[panValues[iPixel]] == 0 )
{
pabyFlags[panValues[iPixel]] = 1;
nFound++;
}
}
}
CPLAssert( nFound <= 100 );
/*
** The classes are always padded with a leading 0. This was for aligning
** offsets, or making it a 1-based array instead of 0-based.
*/
panClasses[0] = 0;
int nIndex = 1;
for( int j = 0; j < nSpan + 1; j++ )
{
if( pabyFlags[j] == 1 )
{
panClasses[nIndex++] = j;
}
}
*pnNumClasses = nFound;
CPLFree( (void*)pabyFlags );
CPLFree( (void*)panValues );
return eErr;
}
/************************************************************************/
/* CreateCopy() */
/************************************************************************/
GDALDataset *LCPDataset::CreateCopy( const char * pszFilename,
GDALDataset * poSrcDS,
int bStrict, char ** papszOptions,
GDALProgressFunc pfnProgress,
void * pProgressData )
{
int nBands = poSrcDS->GetRasterCount();
int nXSize = poSrcDS->GetRasterXSize();
int nYSize = poSrcDS->GetRasterYSize();
/* -------------------------------------------------------------------- */
/* Verify input options. */
/* -------------------------------------------------------------------- */
if( nBands != 5 && nBands != 7 && nBands != 8 && nBands != 10 )
{
CPLError( CE_Failure, CPLE_NotSupported,
"LCP driver doesn't support %d bands. Must be 5, 7, 8 "
"or 10 bands.", nBands );
return NULL;
}
GDALDataType eType = poSrcDS->GetRasterBand( 1 )->GetRasterDataType();
if( eType != GDT_Int16 && bStrict )
{
CPLError( CE_Failure, CPLE_AppDefined,
"LCP only supports 16-bit signed integer data types." );
return NULL;
}
else if( eType != GDT_Int16 )
{
CPLError( CE_Warning, CPLE_AppDefined,
"Setting data type to 16-bit integer." );
}
/* -------------------------------------------------------------------- */
/* What schema do we have (ground/crown fuels) */
/* -------------------------------------------------------------------- */
int bHaveCrownFuels = FALSE;
int bHaveGroundFuels = FALSE;
if( nBands == 8 || nBands == 10 )
{
bHaveCrownFuels = TRUE;
}
if( nBands == 7 || nBands == 10 )
{
bHaveGroundFuels = TRUE;
}
/*
** Since units are 'configurable', we should check for user
** defined units. This is a bit cumbersome, but the user should
** be allowed to specify none to get default units/options. Use
** default units every chance we get.
*/
GInt16 panMetadata[LCP_MAX_BANDS];
int i;
GInt32 nTemp;
double dfTemp;
const char *pszTemp;
panMetadata[0] = 0; /* ELEVATION_UNIT */
panMetadata[1] = 0; /* SLOPE_UNIT */
panMetadata[2] = 2; /* ASPECT_UNIT */
panMetadata[3] = 0; /* FUEL_MODEL_OPTION */
panMetadata[4] = 1; /* CANOPY_COV_UNIT */
panMetadata[5] = 3; /* CANOPY_HT_UNIT */
panMetadata[6] = 3; /* CBH_UNIT */
panMetadata[7] = 3; /* CBD_UNIT */
panMetadata[8] = 1; /* DUFF_UNIT */
panMetadata[9] = 0; /* CWD_OPTION */
/* Check the units/options for user overrides */
pszTemp = CSLFetchNameValueDef( papszOptions, "ELEVATION_UNIT", "METERS" );
if( EQUALN( pszTemp, "METER", 5 ) )
{
panMetadata[0] = 0;
}
else if( EQUAL( pszTemp, "FEET" ) || EQUAL( pszTemp, "FOOT" ) )
{
panMetadata[0] = 1;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for ELEVATION_UNIT.",
pszTemp );
return NULL;
}
pszTemp = CSLFetchNameValueDef( papszOptions, "SLOPE_UNIT", "DEGREES" );
if( EQUAL( pszTemp, "DEGREES" ) )
{
panMetadata[1] = 0;
}
else if( EQUAL( pszTemp, "PERCENT" ) )
{
panMetadata[1] = 1;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for SLOPE_UNIT.",
pszTemp );
return NULL;
}
pszTemp = CSLFetchNameValueDef( papszOptions, "ASPECT_UNIT",
"AZIMUTH_DEGREES" );
if( EQUAL( pszTemp, "GRASS_CATEGORIES" ) )
{
panMetadata[2] = 0;
}
else if( EQUAL( pszTemp, "GRASS_DEGREES" ) )
{
panMetadata[2] = 1;
}
else if( EQUAL( pszTemp, "AZIMUTH_DEGREES" ) )
{
panMetadata[2] = 2;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for ASPECT_UNIT.",
pszTemp );
return NULL;
}
pszTemp = CSLFetchNameValueDef( papszOptions, "FUEL_MODEL_OPTION",
"NO_CUSTOM_AND_NO_FILE" );
if( EQUAL( pszTemp, "NO_CUSTOM_AND_NO_FILE" ) )
{
panMetadata[3] = 0;
}
else if( EQUAL( pszTemp, "CUSTOM_AND_NO_FILE" ) )
{
panMetadata[3] = 1;
}
else if( EQUAL( pszTemp, "NO_CUSTOM_AND_FILE" ) )
{
panMetadata[3] = 2;
}
else if( EQUAL( pszTemp, "CUSTOM_AND_FILE" ) )
{
panMetadata[3] = 3;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for FUEL_MODEL_OPTION.",
pszTemp );
return NULL;
}
pszTemp = CSLFetchNameValueDef( papszOptions, "CANOPY_COV_UNIT",
"PERCENT" );
if( EQUAL( pszTemp, "CATEGORIES" ) )
{
panMetadata[4] = 0;
}
else if( EQUAL( pszTemp, "PERCENT" ) )
{
panMetadata[4] = 1;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for CANOPY_COV_UNIT.",
pszTemp );
return NULL;
}
if( bHaveCrownFuels )
{
pszTemp = CSLFetchNameValueDef( papszOptions, "CANOPY_HT_UNIT",
"METERS_X_10" );
if( EQUAL( pszTemp, "METERS" ) || EQUAL( pszTemp, "METER" ) )
{
panMetadata[5] = 1;
}
else if( EQUAL( pszTemp, "FEET" ) || EQUAL( pszTemp, "FOOT" ) )
{
panMetadata[5] = 2;
}
else if( EQUAL( pszTemp, "METERS_X_10" ) ||
EQUAL( pszTemp, "METER_X_10" ) )
{
panMetadata[5] = 3;
}
else if( EQUAL( pszTemp, "FEET_X_10" ) || EQUAL( pszTemp, "FOOT_X_10" ) )
{
panMetadata[5] = 4;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for CANOPY_HT_UNIT.",
pszTemp );
return NULL;
}
pszTemp = CSLFetchNameValueDef( papszOptions, "CBH_UNIT",
"METERS_X_10" );
if( EQUAL( pszTemp, "METERS" ) || EQUAL( pszTemp, "METER" ) )
{
panMetadata[6] = 1;
}
else if( EQUAL( pszTemp, "FEET" ) || EQUAL( pszTemp, "FOOT" ) )
{
panMetadata[6] = 2;
}
else if( EQUAL( pszTemp, "METERS_X_10" ) ||
EQUAL( pszTemp, "METER_X_10" ) )
{
panMetadata[6] = 3;
}
else if( EQUAL( pszTemp, "FEET_X_10" ) || EQUAL( pszTemp, "FOOT_X_10" ) )
{
panMetadata[6] = 4;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for CBH_UNIT.",
pszTemp );
return NULL;
}
pszTemp = CSLFetchNameValueDef( papszOptions, "CBD_UNIT",
"KG_PER_CUBIC_METER_X_100" );
if( EQUAL( pszTemp, "KG_PER_CUBIC_METER" ) )
{
panMetadata[7] = 1;
}
else if( EQUAL( pszTemp, "POUND_PER_CUBIC_FOOT" ) )
{
panMetadata[7] = 2;
}
else if( EQUAL( pszTemp, "KG_PER_CUBIC_METER_X_100" ) )
{
panMetadata[7] = 3;
}
else if( EQUAL( pszTemp, "POUND_PER_CUBIC_FOOT_X_1000" ) )
{
panMetadata[7] = 4;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for CBD_UNIT.",
pszTemp );
return NULL;
}
}
if( bHaveGroundFuels )
{
pszTemp = CSLFetchNameValueDef( papszOptions, "DUFF_UNIT",
"MG_PER_HECTARE_X_10" );
if( EQUAL( pszTemp, "MG_PER_HECTARE_X_10" ) )
{
panMetadata[8] = 1;
}
else if ( EQUAL( pszTemp, "TONS_PER_ACRE_X_10" ) )
{
panMetadata[8] = 2;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Invalid value (%s) for DUFF_UNIT.",
pszTemp );
return NULL;
}
if( bHaveGroundFuels )
{
panMetadata[9] = 1;
}
else
{
panMetadata[9] = 0;
}
}
/*
** Calculate the stats for each band. The binary file carries along
** these metadata for display purposes(?).
*/
int bCalculateStats = CSLFetchBoolean( papszOptions, "CALCULATE_STATS",
TRUE );
int bClassifyData = CSLFetchBoolean( papszOptions, "CLASSIFY_DATA",
TRUE );
/*
** We should have stats if we classify, we'll get them anyway.
*/
if( bClassifyData && !bCalculateStats )
{
CPLError( CE_Warning, CPLE_AppDefined,
"Ignoring request to not calculate statistics, " \
"because CLASSIFY_DATA was set to ON" );
bCalculateStats = TRUE;
}
pszTemp = CSLFetchNameValueDef( papszOptions, "LINEAR_UNIT",
"SET_FROM_SRS" );
int nLinearUnits = 0;
int bSetLinearUnits = FALSE;
if( EQUAL( pszTemp, "SET_FROM_SRS" ) )
{
bSetLinearUnits = TRUE;
}
else if( EQUALN( pszTemp, "METER", 5 ) )
{
nLinearUnits = 0;
}
else if( EQUAL( pszTemp, "FOOT" ) || EQUAL( pszTemp, "FEET" ) )
{
nLinearUnits = 1;
}
else if( EQUALN( pszTemp, "KILOMETER", 9 ) )
{
nLinearUnits = 2;
}
int bCalculateLatitude = TRUE;
int nLatitude;
if( CSLFetchNameValue( papszOptions, "LATITUDE" ) != NULL )
{
bCalculateLatitude = FALSE;
nLatitude = atoi( CSLFetchNameValue( papszOptions, "LATITUDE" ) );
if( nLatitude > 90 || nLatitude < -90 )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Invalid value (%d) for LATITUDE.", nLatitude );
return NULL;
}
}
/*
** If no latitude is supplied, attempt to extract the central latitude
** from the image. It must be set either manually or here, otherwise
** we fail.
*/
double adfSrcGeoTransform[6];
poSrcDS->GetGeoTransform( adfSrcGeoTransform );
OGRSpatialReference oSrcSRS, oDstSRS;
const char *pszWkt = poSrcDS->GetProjectionRef();
double dfLongitude = 0.0;
double dfLatitude = 0.0;
if( !bCalculateLatitude )
{
dfLatitude = nLatitude;
}
else if( !EQUAL( pszWkt, "" ) )
{
oSrcSRS.importFromWkt( (char**)&pszWkt );
oDstSRS.importFromEPSG( 4269 );
OGRCoordinateTransformation *poCT;
poCT = (OGRCoordinateTransformation*)
OGRCreateCoordinateTransformation( &oSrcSRS, &oDstSRS );
int nErr;
if( poCT != NULL )
{
dfLatitude = adfSrcGeoTransform[3] + adfSrcGeoTransform[5] * nYSize / 2;
nErr = (int)poCT->Transform( 1, &dfLongitude, &dfLatitude );
if( !nErr )
{
dfLatitude = 0.0;
/*
** For the most part, this is an invalid LCP, but it is a
** changeable value in Flammap/Farsite, etc. We should
** probably be strict here all the time.
*/
CPLError( CE_Failure, CPLE_AppDefined,
"Could not calculate latitude from spatial " \
"reference and LATITUDE was not set." );
return NULL;
}
}
OGRCoordinateTransformation::DestroyCT( poCT );
}
else
{
/*
** See comment above on failure to transform.
*/
CPLError( CE_Failure, CPLE_AppDefined,
"Could not calculate latitude from spatial reference " \
"and LATITUDE was not set." );
return NULL;
}
/*
** Set the linear units if the metadata item wasn't already set, and we
** have an SRS.
*/
if( bSetLinearUnits && !EQUAL( pszWkt, "" ) )
{
const char *pszUnit;
pszUnit = oSrcSRS.GetAttrValue( "UNIT", 0 );
if( pszUnit == NULL )
{
if( bStrict )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Could not parse linear unit." );
return NULL;
}
else
{
CPLError( CE_Warning, CPLE_AppDefined,
"Could not parse linear unit, using meters" );
nLinearUnits = 0;
}
}
else
{
CPLDebug( "LCP", "Setting linear unit to %s", pszUnit );
if( EQUAL( pszUnit, "meter" ) || EQUAL( pszUnit, "metre" ) )
{
nLinearUnits = 0;
}
else if( EQUAL( pszUnit, "feet" ) || EQUAL( pszUnit, "foot" ) )
{
nLinearUnits = 1;
}
else if( EQUALN( pszUnit, "kilomet", 7 ) )
{
nLinearUnits = 2;
}
else
{
if( bStrict )
nLinearUnits = 0;
}
pszUnit = oSrcSRS.GetAttrValue( "UNIT", 1 );
if( pszUnit != NULL )
{
double dfScale = CPLAtof( pszUnit );
if( dfScale != 1.0 )
{
if( bStrict )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unit scale is %lf (!=1.0). It is not " \
"supported.", dfScale );
return NULL;
}
else
{
CPLError( CE_Warning, CPLE_AppDefined,
"Unit scale is %lf (!=1.0). It is not " \
"supported, ignoring.", dfScale );
}
}
}
}
}
else if( bSetLinearUnits )
{
/*
** This can be defaulted if it isn't a strict creation.
*/
if( bStrict )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Could not parse linear unit from spatial reference "
"and LINEAR_UNIT was not set." );
return NULL;
}
else
{
CPLError( CE_Warning, CPLE_AppDefined,
"Could not parse linear unit from spatial reference "
"and LINEAR_UNIT was not set, defaulting to meters." );
nLinearUnits = 0;
}
}
const char *pszDescription =
CSLFetchNameValueDef( papszOptions, "DESCRIPTION",
"LCP file created by GDAL." );
/*
** Loop through and get the stats for the bands if we need to calculate
** them. This probably should be done when we copy the data over to the
** destination dataset, since we load the values into memory, but this is
** much simpler code using GDALRasterBand->GetStatistics(). We also may
** need to classify the data (number of unique values and a list of those
** values if the number of unique values is > 100. It is currently unclear
** how these data are used though, so we will implement that at some point
** if need be.
*/
GDALRasterBand *poBand;
double *padfMin = (double*)CPLMalloc( sizeof( double ) * nBands );
double *padfMax = (double*)CPLMalloc( sizeof( double ) * nBands );
double dfDummy;
GInt32 *panFound = (GInt32*)VSIMalloc2( sizeof( GInt32 ), nBands );
GInt32 *panClasses = (GInt32*)VSIMalloc3( sizeof( GInt32 ), nBands, LCP_MAX_CLASSES );
/*
** Initialize these arrays to zeros
*/
memset( panFound, 0, sizeof( GInt32 ) * nBands );
memset( panClasses, 0, sizeof( GInt32 ) * nBands * LCP_MAX_CLASSES );
CPLErr eErr;
if( bCalculateStats )
{
for( i = 0; i < nBands; i++ )
{
poBand = poSrcDS->GetRasterBand( i + 1 );
eErr = poBand->GetStatistics( FALSE, TRUE, &padfMin[i],
&padfMax[i], &dfDummy, &dfDummy );
if( eErr != CE_None )
{
CPLError( CE_Warning, CPLE_AppDefined, "Failed to properly " \
"calculate statistics "
"on band %d", i );
padfMin[i] = 0.0;
padfMax[i] = 0.0;
}
/*
** See comment above.
*/
if( bClassifyData )
{
eErr = ClassifyBandData( poBand, panFound+ i,
panClasses + ( i * LCP_MAX_CLASSES ) );
}
}
}
VSILFILE *fp;
fp = VSIFOpenL( pszFilename, "wb" );
if( fp == NULL )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Unable to create lcp file %s.", pszFilename );
CPLFree( padfMin );
CPLFree( padfMax );
CPLFree( panFound );
CPLFree( panClasses );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Write the header */
/* -------------------------------------------------------------------- */
nTemp = bHaveCrownFuels ? 21 : 20;
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
nTemp = bHaveGroundFuels ? 21 : 20;
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
nTemp = (GInt32)( dfLatitude + 0.5 );
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
dfLongitude = adfSrcGeoTransform[0] + adfSrcGeoTransform[1] * nXSize;
CPL_LSBPTR64( &dfLongitude );
VSIFWriteL( &dfLongitude, 8, 1, fp );
dfLongitude = adfSrcGeoTransform[0];
CPL_LSBPTR64( &dfLongitude );
VSIFWriteL( &dfLongitude, 8, 1, fp );
dfLatitude = adfSrcGeoTransform[3];
CPL_LSBPTR64( &dfLatitude );
VSIFWriteL( &dfLatitude, 8, 1, fp );
dfLatitude = adfSrcGeoTransform[3] + adfSrcGeoTransform[5] * nYSize;
CPL_LSBPTR64( &dfLatitude );
VSIFWriteL( &dfLatitude, 8, 1, fp );
/*
** Swap the two classification arrays if we are writing them, and they need
** to be swapped.
*/
#ifdef CPL_MSB
if( bClassifyData )
{
GDALSwapWords( panFound, 2, nBands, 2 );
GDALSwapWords( panClasses, 2, LCP_MAX_CLASSES, 2 );
}
#endif
if( bCalculateStats )
{
for( i = 0; i < nBands; i++ )
{
/*
** If we don't have Crown fuels, but do have Ground fuels, we
** have to 'fast forward'.
*/
if( i == 5 && !bHaveCrownFuels && bHaveGroundFuels )
{
VSIFSeekL( fp, 3340, SEEK_SET );
}
nTemp = (GInt32)padfMin[i];
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
nTemp = (GInt32)padfMax[i];
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
if( bClassifyData )
{
/*
** These two arrays were swapped in their entirety above.
*/
VSIFWriteL( panFound + i, 4, 1, fp );
VSIFWriteL( panClasses + ( i * LCP_MAX_CLASSES ), 4, 100, fp );
}
else
{
nTemp = -1;
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
VSIFSeekL( fp, 400, SEEK_CUR );
}
}
}
else
{
VSIFSeekL( fp, 4164, SEEK_SET );
}
CPLFree( (void*)padfMin );
CPLFree( (void*)padfMax );
CPLFree( (void*)panFound );
CPLFree( (void*)panClasses );
/*
** Should be at one of 3 locations, 2104, 3340, or 4164.
*/
CPLAssert( VSIFTellL( fp ) == 2104 ||
VSIFTellL( fp ) == 3340 ||
VSIFTellL( fp ) == 4164 );
VSIFSeekL( fp, 4164, SEEK_SET );
/* Image size */
nTemp = (GInt32)nXSize;
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
nTemp = (GInt32)nYSize;
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
/* X and Y boundaries */
/* max x */
dfTemp = adfSrcGeoTransform[0] + adfSrcGeoTransform[1] * nXSize;
CPL_LSBPTR64( &dfTemp );
VSIFWriteL( &dfTemp, 8, 1, fp );
/* min x */
dfTemp = adfSrcGeoTransform[0];
CPL_LSBPTR64( &dfTemp );
VSIFWriteL( &dfTemp, 8, 1, fp );
/* max y */
dfTemp = adfSrcGeoTransform[3];
CPL_LSBPTR64( &dfTemp );
VSIFWriteL( &dfTemp, 8, 1, fp );
/* min y */
dfTemp = adfSrcGeoTransform[3] + adfSrcGeoTransform[5] * nYSize;
CPL_LSBPTR64( &dfTemp );
VSIFWriteL( &dfTemp, 8, 1, fp );
nTemp = nLinearUnits;
CPL_LSBINT32PTR( &nTemp );
VSIFWriteL( &nTemp, 4, 1, fp );
/* Resolution */
/* x resolution */
dfTemp = adfSrcGeoTransform[1];
CPL_LSBPTR64( &dfTemp );
VSIFWriteL( &dfTemp, 8, 1, fp );
/* y resolution */
dfTemp = fabs( adfSrcGeoTransform[5] );
CPL_LSBPTR64( &dfTemp );
VSIFWriteL( &dfTemp, 8, 1, fp );
#ifdef CPL_MSB
GDALSwapWords( panMetadata, 2, LCP_MAX_BANDS, 2 );
#endif
VSIFWriteL( panMetadata, 2, LCP_MAX_BANDS, fp );
/* Write the source filenames */
char **papszFileList = poSrcDS->GetFileList();
if( papszFileList != NULL )
{
for( i = 0; i < nBands; i++ )
{
if( i == 5 && !bHaveCrownFuels && bHaveGroundFuels )
{
VSIFSeekL( fp, 6292, SEEK_SET );
}
VSIFWriteL( papszFileList[0], 1,
CPLStrnlen( papszFileList[0], LCP_MAX_PATH ), fp );
VSIFSeekL( fp, 4244 + ( 256 * ( i+1 ) ), SEEK_SET );
}
}
/*
** No file list, mem driver, etc.
*/
else
{
VSIFSeekL( fp, 6804, SEEK_SET );
}
CSLDestroy( papszFileList );
/*
** Should be at location 5524, 6292 or 6804.
*/
CPLAssert( VSIFTellL( fp ) == 5524 ||
VSIFTellL( fp ) == 6292 ||
VSIFTellL( fp ) == 6804 );
VSIFSeekL( fp, 6804, SEEK_SET );
/* Description */
VSIFWriteL( pszDescription, 1, CPLStrnlen( pszDescription, LCP_MAX_DESC ),
fp );
/*
** Should be at or below location 7316, all done with the header.
*/
CPLAssert( VSIFTellL( fp ) <= 7316 );
VSIFSeekL( fp, 7316, SEEK_SET );
/* -------------------------------------------------------------------- */
/* Loop over image, copying image data. */
/* -------------------------------------------------------------------- */
GInt16 *panScanline = (GInt16 *)VSIMalloc3( 2, nBands, nXSize );
if( !pfnProgress( 0.0, NULL, pProgressData ) )
{
VSIFCloseL( fp );
VSIFree( (void*)panScanline );
return NULL;
}
for( int iLine = 0; iLine < nYSize; iLine++ )
{
for( int iBand = 0; iBand < nBands; iBand++ )
{
GDALRasterBand * poBand = poSrcDS->GetRasterBand( iBand+1 );
eErr = poBand->RasterIO( GF_Read, 0, iLine, nXSize, 1,
panScanline + iBand, nXSize, 1, GDT_Int16,
nBands * 2, nBands * nXSize * 2, NULL );
/* Not sure what to do here */
if( eErr != CE_None )
{
CPLError( CE_Warning, CPLE_AppDefined, "Error reported in " \
"RasterIO" );
/*
** CPLError( eErr, CPLE_AppDefined,
** "Error reported in RasterIO" );
*/
}
}
#ifdef CPL_MSB
GDALSwapWords( panScanline, 2, nBands * nXSize, 2 );
#endif
VSIFWriteL( panScanline, 2, nBands * nXSize, fp );
if( !pfnProgress( iLine / (double)nYSize, NULL, pProgressData ) )
{
VSIFree( (void*)panScanline );
VSIFCloseL( fp );
return NULL;
}
}
VSIFree( panScanline );
VSIFCloseL( fp );
if( !pfnProgress( 1.0, NULL, pProgressData ) )
{
return NULL;
}
/*
** Try to write projection file. *Most* landfire data follows ESRI
**style projection files, so we use the same code as the AAIGrid driver.
*/
const char *pszOriginalProjection;
pszOriginalProjection = (char *)poSrcDS->GetProjectionRef();
if( !EQUAL( pszOriginalProjection, "" ) )
{
char *pszDirname, *pszBasename;
char *pszPrjFilename;
char *pszESRIProjection = NULL;
VSILFILE *fp;
OGRSpatialReference oSRS;
pszDirname = CPLStrdup( CPLGetPath(pszFilename) );
pszBasename = CPLStrdup( CPLGetBasename(pszFilename) );
pszPrjFilename = CPLStrdup( CPLFormFilename( pszDirname, pszBasename, "prj" ) );
fp = VSIFOpenL( pszPrjFilename, "wt" );
if (fp != NULL)
{
oSRS.importFromWkt( (char **) &pszOriginalProjection );
oSRS.morphToESRI();
oSRS.exportToWkt( &pszESRIProjection );
VSIFWriteL( pszESRIProjection, 1, strlen(pszESRIProjection), fp );
VSIFCloseL( fp );
CPLFree( pszESRIProjection );
}
else
{
CPLError( CE_Failure, CPLE_FileIO,
"Unable to create file %s.", pszPrjFilename );
}
CPLFree( pszDirname );
CPLFree( pszBasename );
CPLFree( pszPrjFilename );
}
return (GDALDataset *) GDALOpen( pszFilename, GA_ReadOnly );
}
/************************************************************************/
/* GetProjectionRef() */
/************************************************************************/
const char *LCPDataset::GetProjectionRef()
{
return pszProjection;
}
/************************************************************************/
/* GDALRegister_LCP() */
/************************************************************************/
void GDALRegister_LCP()
{
GDALDriver *poDriver;
if( GDALGetDriverByName( "LCP" ) == NULL )
{
poDriver = new GDALDriver();
poDriver->SetDescription( "LCP" );
poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
"FARSITE v.4 Landscape File (.lcp)" );
poDriver->SetMetadataItem( GDAL_DMD_EXTENSION, "lcp" );
poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
"frmt_lcp.html" );
poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_CREATIONDATATYPES, "Int16" );
poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
"<CreationOptionList>"
" <Option name='ELEVATION_UNIT' type='string-select' default='METERS' description='Elevation units'>"
" <Value>METERS</Value>"
" <Value>FEET</Value>"
" </Option>"
" <Option name='SLOPE_UNIT' type='string-select' default='DEGREES' description='Slope units'>"
" <Value>DEGREES</Value>"
" <Value>PERCENT</Value>"
" </Option>"
" <Option name='ASPECT_UNIT' type='string-select' default='AZIMUTH_DEGREES'>"
" <Value>GRASS_CATEGORIES</Value>"
" <Value>AZIMUTH_DEGREES</Value>"
" <Value>GRASS_DEGREES</Value>"
" </Option>"
" <Option name='FUEL_MODEL_OPTION' type='string-select' default='NO_CUSTOM_AND_NO_FILE'>"
" <Value>NO_CUSTOM_AND_NO_FILE</Value>"
" <Value>CUSTOM_AND_NO_FILE</Value>"
" <Value>NO_CUSTOM_AND_FILE</Value>"
" <Value>CUSTOM_AND_FILE</Value>"
" </Option>"
" <Option name='CANOPY_COV_UNIT' type='string-select' default='PERCENT'>"
" <Value>CATEGORIES</Value>"
" <Value>PERCENT</Value>"
" </Option>"
" <Option name='CANOPY_HT_UNIT' type='string-select' default='METERS_X_10'>"
" <Value>METERS</Value>"
" <Value>FEET</Value>"
" <Value>METERS_X_10</Value>"
" <Value>FEET_X_10</Value>"
" </Option>"
" <Option name='CBH_UNIT' type='string-select' default='METERS_X_10'>"
" <Value>METERS</Value>"
" <Value>FEET</Value>"
" <Value>METERS_X_10</Value>"
" <Value>FEET_X_10</Value>"
" </Option>"
" <Option name='CBD_UNIT' type='string-select' default='KG_PER_CUBIC_METER_X_100'>"
" <Value>KG_PER_CUBIC_METER</Value>"
" <Value>POUND_PER_CUBIC_FOOT</Value>"
" <Value>KG_PER_CUBIC_METER_X_100</Value>"
" <Value>POUND_PER_CUBIC_FOOT_X_1000</Value>"
" </Option>"
" <Option name='DUFF_UNIT' type='string-select' default='MG_PER_HECTARE_X_10'>"
" <Value>MG_PER_HECTARE_X_10</Value>"
" <Value>TONS_PER_ACRE_X_10</Value>"
" </Option>"
/* I don't think we need to override this, but maybe? */
/*" <Option name='CWD_OPTION' type='boolean' default='FALSE' description='Override logic for setting the coarse woody presence'/>" */
" <Option name='CALCULATE_STATS' type='boolean' default='YES' description='Write the stats to the lcp'/>"
" <Option name='CLASSIFY_DATA' type='boolean' default='YES' description='Write the stats to the lcp'/>"
" <Option name='LINEAR_UNIT' type='string-select' default='SET_FROM_SRS' description='Set the linear units in the lcp'>"
" <Value>SET_FROM_SRS</Value>"
" <Value>METER</Value>"
" <Value>FOOT</Value>"
" <Value>KILOMETER</Value>"
" </Option>"
" <Option name='LATITUDE' type='int' default='' description='Set the latitude for the dataset, this overrides the driver trying to set it programmatically in EPSG:4269'/>"
" <Option name='DESCRIPTION' type='string' default='LCP file created by GDAL' description='A short description of the lcp file'/>"
"</CreationOptionList>" );
poDriver->pfnOpen = LCPDataset::Open;
poDriver->pfnCreateCopy = LCPDataset::CreateCopy;
poDriver->pfnIdentify = LCPDataset::Identify;
GetGDALDriverManager()->RegisterDriver( poDriver );
}
}