ultimatepp/bazaar/plugin/gdal/frmts/wcs/wcsdataset.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

2431 lines
91 KiB
C++

/******************************************************************************
* $Id: wcsdataset.cpp 28218 2014-12-25 18:09:13Z goatbar $
*
* Project: WCS Client Driver
* Purpose: Implementation of Dataset and RasterBand classes for WCS.
* Author: Frank Warmerdam, warmerdam@pobox.com
*
******************************************************************************
* Copyright (c) 2006, Frank Warmerdam
* Copyright (c) 2008-2013, Even Rouault <even dot rouault at mines-paris dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include "gdal_pam.h"
#include "cpl_string.h"
#include "cpl_minixml.h"
#include "cpl_http.h"
#include "ogr_spatialref.h"
CPL_CVSID("$Id: wcsdataset.cpp 28218 2014-12-25 18:09:13Z goatbar $");
/************************************************************************/
/* ==================================================================== */
/* WCSDataset */
/* ==================================================================== */
/************************************************************************/
class WCSRasterBand;
class CPL_DLL WCSDataset : public GDALPamDataset
{
friend class WCSRasterBand;
int bServiceDirty;
CPLXMLNode *psService;
char *apszCoverageOfferingMD[2];
char **papszSDSModifiers;
int nVersion; // eg 100 for 1.0.0, 110 for 1.1.0
CPLString osCRS;
char *pszProjection;
double adfGeoTransform[6];
CPLString osBandIdentifier;
CPLString osDefaultTime;
std::vector<CPLString> aosTimePositions;
int TestUseBlockIO( int, int, int, int, int, int );
CPLErr DirectRasterIO( GDALRWFlag, int, int, int, int,
void *, int, int, GDALDataType,
int, int *,
GSpacing nPixelSpace, GSpacing nLineSpace,
GSpacing nBandSpace,
GDALRasterIOExtraArg* psExtraArg);
CPLErr GetCoverage( int nXOff, int nYOff, int nXSize, int nYSize,
int nBufXSize, int nBufYSize,
int nBandCount, int *panBandList,
CPLHTTPResult **ppsResult );
virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
void *, int, int, GDALDataType,
int, int *,
GSpacing nPixelSpace, GSpacing nLineSpace,
GSpacing nBandSpace,
GDALRasterIOExtraArg* psExtraArg);
int DescribeCoverage();
int ExtractGridInfo100();
int ExtractGridInfo();
int EstablishRasterDetails();
int ProcessError( CPLHTTPResult *psResult );
GDALDataset *GDALOpenResult( CPLHTTPResult *psResult );
void FlushMemoryResult();
CPLString osResultFilename;
GByte *pabySavedDataBuffer;
char **papszHttpOptions;
int nMaxCols;
int nMaxRows;
public:
WCSDataset();
~WCSDataset();
static GDALDataset *Open( GDALOpenInfo * );
static int Identify( GDALOpenInfo * );
virtual CPLErr GetGeoTransform( double * );
virtual const char *GetProjectionRef(void);
virtual char **GetFileList(void);
virtual char **GetMetadataDomainList();
virtual char **GetMetadata( const char *pszDomain );
};
/************************************************************************/
/* ==================================================================== */
/* WCSRasterBand */
/* ==================================================================== */
/************************************************************************/
class WCSRasterBand : public GDALPamRasterBand
{
friend class WCSDataset;
int iOverview;
int nResFactor;
WCSDataset *poODS;
int nOverviewCount;
WCSRasterBand **papoOverviews;
virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
void *, int, int, GDALDataType,
GSpacing nPixelSpace, GSpacing nLineSpace,
GDALRasterIOExtraArg* psExtraArg );
public:
WCSRasterBand( WCSDataset *, int nBand, int iOverview );
~WCSRasterBand();
virtual double GetNoDataValue( int *pbSuccess = NULL );
virtual int GetOverviewCount();
virtual GDALRasterBand *GetOverview(int);
virtual CPLErr IReadBlock( int, int, void * );
};
/************************************************************************/
/* WCSRasterBand() */
/************************************************************************/
WCSRasterBand::WCSRasterBand( WCSDataset *poDS, int nBand, int iOverview )
{
poODS = poDS;
this->nBand = nBand;
eDataType = GDALGetDataTypeByName(
CPLGetXMLValue( poDS->psService, "BandType", "Byte" ) );
/* -------------------------------------------------------------------- */
/* Establish resolution reduction for this overview level. */
/* -------------------------------------------------------------------- */
this->iOverview = iOverview;
nResFactor = 1 << (iOverview+1); // iOverview == -1 is base layer
/* -------------------------------------------------------------------- */
/* Establish block size. */
/* -------------------------------------------------------------------- */
nRasterXSize = poDS->GetRasterXSize() / nResFactor;
nRasterYSize = poDS->GetRasterYSize() / nResFactor;
nBlockXSize = atoi(CPLGetXMLValue( poDS->psService, "BlockXSize", "0" ) );
nBlockYSize = atoi(CPLGetXMLValue( poDS->psService, "BlockYSize", "0" ) );
if( nBlockXSize < 1 )
{
if( nRasterXSize > 1800 )
nBlockXSize = 1024;
else
nBlockXSize = nRasterXSize;
}
if( nBlockYSize < 1 )
{
if( nRasterYSize > 900 )
nBlockYSize = 512;
else
nBlockYSize = nRasterYSize;
}
/* -------------------------------------------------------------------- */
/* If this is the base layer, create the overview layers. */
/* -------------------------------------------------------------------- */
if( iOverview == -1 )
{
int i;
nOverviewCount = atoi(CPLGetXMLValue(poODS->psService,"OverviewCount",
"-1"));
if( nOverviewCount < 0 )
{
for( nOverviewCount = 0;
(MAX(nRasterXSize,nRasterYSize) / (1 << nOverviewCount)) > 900;
nOverviewCount++ ) {}
}
else if( nOverviewCount > 30 )
{
/* There's no reason to have more than 30 overviews, because */
/* 2^(30+1) overflows a int32 */
nOverviewCount = 30;
}
papoOverviews = (WCSRasterBand **)
CPLCalloc( nOverviewCount, sizeof(void*) );
for( i = 0; i < nOverviewCount; i++ )
papoOverviews[i] = new WCSRasterBand( poODS, nBand, i );
}
else
{
nOverviewCount = 0;
papoOverviews = NULL;
}
}
/************************************************************************/
/* ~WCSRasterBand() */
/************************************************************************/
WCSRasterBand::~WCSRasterBand()
{
FlushCache();
if( nOverviewCount > 0 )
{
int i;
for( i = 0; i < nOverviewCount; i++ )
delete papoOverviews[i];
CPLFree( papoOverviews );
}
}
/************************************************************************/
/* IReadBlock() */
/************************************************************************/
CPLErr WCSRasterBand::IReadBlock( int nBlockXOff, int nBlockYOff,
void * pImage )
{
CPLErr eErr;
CPLHTTPResult *psResult = NULL;
eErr = poODS->GetCoverage( nBlockXOff * nBlockXSize * nResFactor,
nBlockYOff * nBlockYSize * nResFactor,
nBlockXSize * nResFactor,
nBlockYSize * nResFactor,
nBlockXSize, nBlockYSize,
1, &nBand, &psResult );
if( eErr != CE_None )
return eErr;
/* -------------------------------------------------------------------- */
/* Try and open result as a dataseat. */
/* -------------------------------------------------------------------- */
GDALDataset *poTileDS = poODS->GDALOpenResult( psResult );
if( poTileDS == NULL )
return CE_Failure;
/* -------------------------------------------------------------------- */
/* Verify configuration. */
/* -------------------------------------------------------------------- */
if( poTileDS->GetRasterXSize() != nBlockXSize
|| poTileDS->GetRasterYSize() != nBlockYSize )
{
CPLDebug( "WCS", "Got size=%dx%d instead of %dx%d.",
poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
nBlockXSize, nBlockYSize );
CPLError( CE_Failure, CPLE_AppDefined,
"Returned tile does not match expected configuration.\n"
"Got %dx%d instead of %dx%d.",
poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
nBlockXSize, nBlockYSize );
delete poTileDS;
return CE_Failure;
}
if( (strlen(poODS->osBandIdentifier) && poTileDS->GetRasterCount() != 1)
|| (!strlen(poODS->osBandIdentifier)
&& poTileDS->GetRasterCount() != poODS->GetRasterCount()) )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Returned tile does not match expected band configuration.");
delete poTileDS;
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Process all bands of memory result, copying into pBuffer, or */
/* pushing into cache for other bands. */
/* -------------------------------------------------------------------- */
int iBand;
eErr = CE_None;
for( iBand = 0;
iBand < poTileDS->GetRasterCount() && eErr == CE_None;
iBand++ )
{
GDALRasterBand *poTileBand = poTileDS->GetRasterBand( iBand+1 );
if( iBand+1 == GetBand() || strlen(poODS->osBandIdentifier) )
{
eErr = poTileBand->RasterIO( GF_Read,
0, 0, nBlockXSize, nBlockYSize,
pImage, nBlockXSize, nBlockYSize,
eDataType, 0, 0, NULL );
}
else
{
GDALRasterBand *poTargBand = poODS->GetRasterBand( iBand+1 );
if( iOverview != -1 )
poTargBand = poTargBand->GetOverview( iOverview );
GDALRasterBlock *poBlock = poTargBand->GetLockedBlockRef(
nBlockXOff, nBlockYOff, TRUE );
if( poBlock != NULL )
{
eErr = poTileBand->RasterIO( GF_Read,
0, 0, nBlockXSize, nBlockYSize,
poBlock->GetDataRef(),
nBlockXSize, nBlockYSize,
eDataType, 0, 0, NULL );
poBlock->DropLock();
}
else
eErr = CE_Failure;
}
}
/* -------------------------------------------------------------------- */
/* Cleanup */
/* -------------------------------------------------------------------- */
delete poTileDS;
poODS->FlushMemoryResult();
return eErr;
}
/************************************************************************/
/* IRasterIO() */
/************************************************************************/
CPLErr WCSRasterBand::IRasterIO( GDALRWFlag eRWFlag,
int nXOff, int nYOff, int nXSize, int nYSize,
void * pData, int nBufXSize, int nBufYSize,
GDALDataType eBufType,
GSpacing nPixelSpace, GSpacing nLineSpace,
GDALRasterIOExtraArg* psExtraArg)
{
if( (poODS->nMaxCols > 0 && poODS->nMaxCols < nBufXSize)
|| (poODS->nMaxRows > 0 && poODS->nMaxRows < nBufYSize) )
return CE_Failure;
if( poODS->TestUseBlockIO( nXOff, nYOff, nXSize, nYSize,
nBufXSize,nBufYSize ) )
return GDALPamRasterBand::IRasterIO(
eRWFlag, nXOff, nYOff, nXSize, nYSize,
pData, nBufXSize, nBufYSize, eBufType,
nPixelSpace, nLineSpace, psExtraArg );
else
return poODS->DirectRasterIO(
eRWFlag,
nXOff * nResFactor, nYOff * nResFactor,
nXSize * nResFactor, nYSize * nResFactor,
pData, nBufXSize, nBufYSize, eBufType,
1, &nBand, nPixelSpace, nLineSpace, 0, psExtraArg );
}
/************************************************************************/
/* GetNoDataValue() */
/************************************************************************/
double WCSRasterBand::GetNoDataValue( int *pbSuccess )
{
const char *pszSV = CPLGetXMLValue( poODS->psService, "NoDataValue", NULL);
if( pszSV == NULL )
return GDALPamRasterBand::GetNoDataValue( pbSuccess );
else
{
if( pbSuccess )
*pbSuccess = TRUE;
return CPLAtof(pszSV);
}
}
/************************************************************************/
/* GetOverviewCount() */
/************************************************************************/
int WCSRasterBand::GetOverviewCount()
{
return nOverviewCount;
}
/************************************************************************/
/* GetOverview() */
/************************************************************************/
GDALRasterBand *WCSRasterBand::GetOverview( int iOverview )
{
if( iOverview < 0 || iOverview >= nOverviewCount )
return NULL;
else
return papoOverviews[iOverview];
}
/************************************************************************/
/* ==================================================================== */
/* WCSDataset */
/* ==================================================================== */
/************************************************************************/
/************************************************************************/
/* WCSDataset() */
/************************************************************************/
WCSDataset::WCSDataset()
{
psService = NULL;
bServiceDirty = FALSE;
pszProjection = NULL;
adfGeoTransform[0] = 0.0;
adfGeoTransform[1] = 1.0;
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = 0.0;
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = 1.0;
pabySavedDataBuffer = NULL;
papszHttpOptions = NULL;
nMaxCols = -1;
nMaxRows = -1;
apszCoverageOfferingMD[0] = NULL;
apszCoverageOfferingMD[1] = NULL;
papszSDSModifiers = NULL;
}
/************************************************************************/
/* ~WCSDataset() */
/************************************************************************/
WCSDataset::~WCSDataset()
{
// perhaps this should be moved into a FlushCache() method.
if( bServiceDirty && !EQUALN(GetDescription(),"<WCS_GDAL>",10) )
{
CPLSerializeXMLTreeToFile( psService, GetDescription() );
bServiceDirty = FALSE;
}
CPLDestroyXMLNode( psService );
CPLFree( pszProjection );
pszProjection = NULL;
CSLDestroy( papszHttpOptions );
CSLDestroy( papszSDSModifiers );
CPLFree( apszCoverageOfferingMD[0] );
FlushMemoryResult();
}
/************************************************************************/
/* TestUseBlockIO() */
/* */
/* Check whether we should use blocked IO (true) or direct io */
/* (FALSE) for a given request configuration and environment. */
/************************************************************************/
int WCSDataset::TestUseBlockIO( CPL_UNUSED int nXOff,
CPL_UNUSED int nYOff,
int nXSize,
int nYSize,
int nBufXSize,
int nBufYSize )
{
int bUseBlockedIO = bForceCachedIO;
if( nYSize == 1 || nXSize * ((double) nYSize) < 100.0 )
bUseBlockedIO = TRUE;
if( nBufYSize == 1 || nBufXSize * ((double) nBufYSize) < 100.0 )
bUseBlockedIO = TRUE;
if( bUseBlockedIO
&& CSLTestBoolean( CPLGetConfigOption( "GDAL_ONE_BIG_READ", "NO") ) )
bUseBlockedIO = FALSE;
return bUseBlockedIO;
}
/************************************************************************/
/* IRasterIO() */
/************************************************************************/
CPLErr WCSDataset::IRasterIO( GDALRWFlag eRWFlag,
int nXOff, int nYOff, int nXSize, int nYSize,
void * pData, int nBufXSize, int nBufYSize,
GDALDataType eBufType,
int nBandCount, int *panBandMap,
GSpacing nPixelSpace, GSpacing nLineSpace,
GSpacing nBandSpace,
GDALRasterIOExtraArg* psExtraArg)
{
if( (nMaxCols > 0 && nMaxCols < nBufXSize)
|| (nMaxRows > 0 && nMaxRows < nBufYSize) )
return CE_Failure;
/* -------------------------------------------------------------------- */
/* We need various criteria to skip out to block based methods. */
/* -------------------------------------------------------------------- */
if( TestUseBlockIO( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize ) )
return GDALPamDataset::IRasterIO(
eRWFlag, nXOff, nYOff, nXSize, nYSize,
pData, nBufXSize, nBufYSize, eBufType,
nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg );
else
return DirectRasterIO(
eRWFlag, nXOff, nYOff, nXSize, nYSize,
pData, nBufXSize, nBufYSize, eBufType,
nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace, psExtraArg );
}
/************************************************************************/
/* DirectRasterIO() */
/* */
/* Make exactly one request to the server for this data. */
/************************************************************************/
CPLErr
WCSDataset::DirectRasterIO( CPL_UNUSED GDALRWFlag eRWFlag,
int nXOff,
int nYOff,
int nXSize,
int nYSize,
void * pData,
int nBufXSize,
int nBufYSize,
GDALDataType eBufType,
int nBandCount,
int *panBandMap,
GSpacing nPixelSpace, GSpacing nLineSpace,
GSpacing nBandSpace,
CPL_UNUSED GDALRasterIOExtraArg* psExtraArg)
{
CPLDebug( "WCS", "DirectRasterIO(%d,%d,%d,%d) -> (%d,%d) (%d bands)\n",
nXOff, nYOff, nXSize, nYSize,
nBufXSize, nBufYSize, nBandCount );
/* -------------------------------------------------------------------- */
/* Get the coverage. */
/* -------------------------------------------------------------------- */
CPLHTTPResult *psResult = NULL;
CPLErr eErr =
GetCoverage( nXOff, nYOff, nXSize, nYSize, nBufXSize, nBufYSize,
nBandCount, panBandMap, &psResult );
if( eErr != CE_None )
return eErr;
/* -------------------------------------------------------------------- */
/* Try and open result as a dataseat. */
/* -------------------------------------------------------------------- */
GDALDataset *poTileDS = GDALOpenResult( psResult );
if( poTileDS == NULL )
return CE_Failure;
/* -------------------------------------------------------------------- */
/* Verify configuration. */
/* -------------------------------------------------------------------- */
if( poTileDS->GetRasterXSize() != nBufXSize
|| poTileDS->GetRasterYSize() != nBufYSize )
{
CPLDebug( "WCS", "Got size=%dx%d instead of %dx%d.",
poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
nBufXSize, nBufYSize );
CPLError( CE_Failure, CPLE_AppDefined,
"Returned tile does not match expected configuration.\n"
"Got %dx%d instead of %dx%d.",
poTileDS->GetRasterXSize(), poTileDS->GetRasterYSize(),
nBufXSize, nBufYSize );
delete poTileDS;
return CE_Failure;
}
if( (strlen(osBandIdentifier) && poTileDS->GetRasterCount() != nBandCount)
|| (!strlen(osBandIdentifier) && poTileDS->GetRasterCount() !=
GetRasterCount() ) )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Returned tile does not match expected band count." );
delete poTileDS;
return CE_Failure;
}
/* -------------------------------------------------------------------- */
/* Pull requested bands from the downloaded dataset. */
/* -------------------------------------------------------------------- */
int iBand;
eErr = CE_None;
for( iBand = 0;
iBand < nBandCount && eErr == CE_None;
iBand++ )
{
GDALRasterBand *poTileBand;
if( strlen(osBandIdentifier) )
poTileBand = poTileDS->GetRasterBand( iBand + 1 );
else
poTileBand = poTileDS->GetRasterBand( panBandMap[iBand] );
eErr = poTileBand->RasterIO( GF_Read,
0, 0, nBufXSize, nBufYSize,
((GByte *) pData) +
iBand * nBandSpace, nBufXSize, nBufYSize,
eBufType, nPixelSpace, nLineSpace, NULL );
}
/* -------------------------------------------------------------------- */
/* Cleanup */
/* -------------------------------------------------------------------- */
delete poTileDS;
FlushMemoryResult();
return eErr;
}
/************************************************************************/
/* GetCoverage() */
/* */
/* Issue the appropriate version of request for a given window, */
/* buffer size and band list. */
/************************************************************************/
CPLErr WCSDataset::GetCoverage( int nXOff, int nYOff, int nXSize, int nYSize,
int nBufXSize, int nBufYSize,
int nBandCount, int *panBandList,
CPLHTTPResult **ppsResult )
{
/* -------------------------------------------------------------------- */
/* Figure out the georeferenced extents. */
/* -------------------------------------------------------------------- */
double dfMinX, dfMaxX, dfMinY, dfMaxY;
// WCS 1.0 extents are the outer edges of outer pixels.
dfMinX = adfGeoTransform[0] +
(nXOff) * adfGeoTransform[1];
dfMaxX = adfGeoTransform[0] +
(nXOff + nXSize) * adfGeoTransform[1];
dfMaxY = adfGeoTransform[3] +
(nYOff) * adfGeoTransform[5];
dfMinY = adfGeoTransform[3] +
(nYOff + nYSize) * adfGeoTransform[5];
/* -------------------------------------------------------------------- */
/* Build band list if we have the band identifier. */
/* -------------------------------------------------------------------- */
CPLString osBandList;
int bSelectingBands = FALSE;
if( strlen(osBandIdentifier) && nBandCount > 0 )
{
int iBand;
for( iBand = 0; iBand < nBandCount; iBand++ )
{
if( iBand > 0 )
osBandList += ",";
osBandList += CPLString().Printf( "%d", panBandList[iBand] );
}
bSelectingBands = TRUE;
}
/* -------------------------------------------------------------------- */
/* URL encode strings that could have questionable characters. */
/* -------------------------------------------------------------------- */
CPLString osCoverage, osFormat;
char *pszEncoded;
osCoverage = CPLGetXMLValue( psService, "CoverageName", "" );
pszEncoded = CPLEscapeString( osCoverage, -1, CPLES_URL );
osCoverage = pszEncoded;
CPLFree( pszEncoded );
osFormat = CPLGetXMLValue( psService, "PreferredFormat", "" );
pszEncoded = CPLEscapeString( osFormat, -1, CPLES_URL );
osFormat = pszEncoded;
CPLFree( pszEncoded );
/* -------------------------------------------------------------------- */
/* Do we have a time we want to use? */
/* -------------------------------------------------------------------- */
CPLString osTime;
osTime = CSLFetchNameValueDef( papszSDSModifiers, "time", osDefaultTime );
/* -------------------------------------------------------------------- */
/* Construct a "simple" GetCoverage request (WCS 1.0). */
/* -------------------------------------------------------------------- */
CPLString osRequest;
if( nVersion == 100 )
{
osRequest.Printf(
"%sSERVICE=WCS&VERSION=1.0.0&REQUEST=GetCoverage&COVERAGE=%s"
"&FORMAT=%s&BBOX=%.15g,%.15g,%.15g,%.15g&WIDTH=%d&HEIGHT=%d&CRS=%s%s",
CPLGetXMLValue( psService, "ServiceURL", "" ),
osCoverage.c_str(),
osFormat.c_str(),
dfMinX, dfMinY, dfMaxX, dfMaxY,
nBufXSize, nBufYSize,
osCRS.c_str(),
CPLGetXMLValue( psService, "GetCoverageExtra", "" ) );
if( CPLGetXMLValue( psService, "Resample", NULL ) )
{
osRequest += "&INTERPOLATION=";
osRequest += CPLGetXMLValue( psService, "Resample", "" );
}
if( osTime != "" )
{
osRequest += "&time=";
osRequest += osTime;
}
if( bSelectingBands )
{
osRequest += CPLString().Printf( "&%s=%s",
osBandIdentifier.c_str(),
osBandList.c_str() );
}
}
/* -------------------------------------------------------------------- */
/* Construct a "simple" GetCoverage request (WCS 1.1+). */
/* -------------------------------------------------------------------- */
else
{
CPLString osRangeSubset;
osRangeSubset.Printf("&RangeSubset=%s",
CPLGetXMLValue(psService,"FieldName",""));
if( CPLGetXMLValue( psService, "Resample", NULL ) )
{
osRangeSubset += ":";
osRangeSubset += CPLGetXMLValue( psService, "Resample", "");
}
if( bSelectingBands )
{
osRangeSubset +=
CPLString().Printf( "[%s[%s]]",
osBandIdentifier.c_str(),
osBandList.c_str() );
}
// WCS 1.1 extents are centers of outer pixels.
dfMaxX -= adfGeoTransform[1] * 0.5;
dfMinX += adfGeoTransform[1] * 0.5;
dfMinY -= adfGeoTransform[5] * 0.5;
dfMaxY += adfGeoTransform[5] * 0.5;
// Carefully adjust bounds for pixel centered values at new
// sampling density.
double dfXStep = adfGeoTransform[1];
double dfYStep = adfGeoTransform[5];
if( nBufXSize != nXSize || nBufYSize != nYSize )
{
dfXStep = (nXSize/(double)nBufXSize) * adfGeoTransform[1];
dfYStep = (nYSize/(double)nBufYSize) * adfGeoTransform[5];
dfMinX = nXOff * adfGeoTransform[1] + adfGeoTransform[0]
+ dfXStep * 0.5;
dfMaxX = dfMinX + (nBufXSize - 1) * dfXStep;
dfMaxY = nYOff * adfGeoTransform[5] + adfGeoTransform[3]
+ dfYStep * 0.5;
dfMinY = dfMaxY + (nBufYSize - 1) * dfYStep;
}
osRequest.Printf(
"%sSERVICE=WCS&VERSION=%s&REQUEST=GetCoverage&IDENTIFIER=%s"
"&FORMAT=%s&BOUNDINGBOX=%.15g,%.15g,%.15g,%.15g,%s%s%s",
CPLGetXMLValue( psService, "ServiceURL", "" ),
CPLGetXMLValue( psService, "Version", "" ),
osCoverage.c_str(),
osFormat.c_str(),
dfMinX, dfMinY, dfMaxX, dfMaxY,
osCRS.c_str(),
osRangeSubset.c_str(),
CPLGetXMLValue( psService, "GetCoverageExtra", "" ) );
if( nBufXSize != nXSize || nBufYSize != nYSize )
{
osRequest += CPLString().Printf(
"&GridBaseCRS=%s"
"&GridCS=%s"
"&GridType=urn:ogc:def:method:WCS:1.1:2dGridIn2dCrs"
"&GridOrigin=%.15g,%.15g"
"&GridOffsets=%.15g,%.15g",
osCRS.c_str(),
osCRS.c_str(),
dfMinX, dfMaxY,
dfXStep, dfYStep );
}
}
/* -------------------------------------------------------------------- */
/* Fetch the result. */
/* -------------------------------------------------------------------- */
CPLErrorReset();
*ppsResult = CPLHTTPFetch( osRequest, papszHttpOptions );
if( ProcessError( *ppsResult ) )
return CE_Failure;
else
return CE_None;
}
/************************************************************************/
/* DescribeCoverage() */
/* */
/* Fetch the DescribeCoverage result and attach it to the */
/* service description. */
/************************************************************************/
int WCSDataset::DescribeCoverage()
{
CPLString osRequest;
/* -------------------------------------------------------------------- */
/* Fetch coverage description for this coverage. */
/* -------------------------------------------------------------------- */
if( nVersion == 100 )
osRequest.Printf(
"%sSERVICE=WCS&REQUEST=DescribeCoverage&VERSION=%s&COVERAGE=%s%s",
CPLGetXMLValue( psService, "ServiceURL", "" ),
CPLGetXMLValue( psService, "Version", "1.0.0" ),
CPLGetXMLValue( psService, "CoverageName", "" ),
CPLGetXMLValue( psService, "DescribeCoverageExtra", "" ) );
else
osRequest.Printf(
"%sSERVICE=WCS&REQUEST=DescribeCoverage&VERSION=%s&IDENTIFIERS=%s%s&FORMAT=text/xml",
CPLGetXMLValue( psService, "ServiceURL", "" ),
CPLGetXMLValue( psService, "Version", "1.0.0" ),
CPLGetXMLValue( psService, "CoverageName", "" ),
CPLGetXMLValue( psService, "DescribeCoverageExtra", "" ) );
CPLErrorReset();
CPLHTTPResult *psResult = CPLHTTPFetch( osRequest, papszHttpOptions );
if( ProcessError( psResult ) )
return FALSE;
/* -------------------------------------------------------------------- */
/* Parse result. */
/* -------------------------------------------------------------------- */
CPLXMLNode *psDC = CPLParseXMLString( (const char *) psResult->pabyData );
CPLHTTPDestroyResult( psResult );
if( psDC == NULL )
return FALSE;
CPLStripXMLNamespace( psDC, NULL, TRUE );
/* -------------------------------------------------------------------- */
/* Did we get a CoverageOffering? */
/* -------------------------------------------------------------------- */
CPLXMLNode *psCO;
if( nVersion == 100 )
psCO = CPLGetXMLNode( psDC, "=CoverageDescription.CoverageOffering" );
else
psCO =CPLGetXMLNode( psDC,"=CoverageDescriptions.CoverageDescription");
if( !psCO )
{
CPLDestroyXMLNode( psDC );
CPLError( CE_Failure, CPLE_AppDefined,
"Failed to fetch a <CoverageOffering> back %s.",
osRequest.c_str() );
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Duplicate the coverage offering, and insert into */
/* -------------------------------------------------------------------- */
CPLXMLNode *psNext = psCO->psNext;
psCO->psNext = NULL;
CPLAddXMLChild( psService, CPLCloneXMLTree( psCO ) );
bServiceDirty = TRUE;
psCO->psNext = psNext;
CPLDestroyXMLNode( psDC );
return TRUE;
}
/************************************************************************/
/* ExtractGridInfo100() */
/* */
/* Collect info about grid from describe coverage for WCS 1.0.0 */
/* and above. */
/************************************************************************/
int WCSDataset::ExtractGridInfo100()
{
CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageOffering" );
if( psCO == NULL )
return FALSE;
/* -------------------------------------------------------------------- */
/* We need to strip off name spaces so it is easier to */
/* searchfor plain gml names. */
/* -------------------------------------------------------------------- */
CPLStripXMLNamespace( psCO, NULL, TRUE );
/* -------------------------------------------------------------------- */
/* Verify we have a Rectified Grid. */
/* -------------------------------------------------------------------- */
CPLXMLNode *psRG =
CPLGetXMLNode( psCO, "domainSet.spatialDomain.RectifiedGrid" );
if( psRG == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unable to find RectifiedGrid in CoverageOffering,\n"
"unable to process WCS Coverage." );
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Extract size, geotransform and coordinate system. */
/* -------------------------------------------------------------------- */
if( GDALParseGMLCoverage( psRG, &nRasterXSize, &nRasterYSize,
adfGeoTransform, &pszProjection ) != CE_None )
return FALSE;
/* -------------------------------------------------------------------- */
/* Fallback to nativeCRSs declaration. */
/* -------------------------------------------------------------------- */
const char *pszNativeCRSs =
CPLGetXMLValue( psCO, "supportedCRSs.nativeCRSs", NULL );
if( pszNativeCRSs == NULL )
pszNativeCRSs =
CPLGetXMLValue( psCO, "supportedCRSs.requestResponseCRSs", NULL );
if( pszNativeCRSs == NULL )
pszNativeCRSs =
CPLGetXMLValue( psCO, "supportedCRSs.requestCRSs", NULL );
if( pszNativeCRSs == NULL )
pszNativeCRSs =
CPLGetXMLValue( psCO, "supportedCRSs.responseCRSs", NULL );
if( pszNativeCRSs != NULL
&& (pszProjection == NULL || strlen(pszProjection) == 0) )
{
OGRSpatialReference oSRS;
if( oSRS.SetFromUserInput( pszNativeCRSs ) == OGRERR_NONE )
{
CPLFree( pszProjection );
oSRS.exportToWkt( &pszProjection );
}
else
CPLDebug( "WCS",
"<nativeCRSs> element contents not parsable:\n%s",
pszNativeCRSs );
}
// We should try to use the services name for the CRS if possible.
if( pszNativeCRSs != NULL
&& ( EQUALN(pszNativeCRSs,"EPSG:",5)
|| EQUALN(pszNativeCRSs,"AUTO:",5)
|| EQUALN(pszNativeCRSs,"Image ",6)
|| EQUALN(pszNativeCRSs,"Engineering ",12)
|| EQUALN(pszNativeCRSs,"OGC:",4) ) )
{
osCRS = pszNativeCRSs;
size_t nDivider = osCRS.find( " " );
if( nDivider != std::string::npos )
osCRS.resize( nDivider-1 );
}
/* -------------------------------------------------------------------- */
/* Do we have a coordinate system override? */
/* -------------------------------------------------------------------- */
const char *pszProjOverride = CPLGetXMLValue( psService, "SRS", NULL );
if( pszProjOverride )
{
OGRSpatialReference oSRS;
if( oSRS.SetFromUserInput( pszProjOverride ) != OGRERR_NONE )
{
CPLError( CE_Failure, CPLE_AppDefined,
"<SRS> element contents not parsable:\n%s",
pszProjOverride );
return FALSE;
}
CPLFree( pszProjection );
oSRS.exportToWkt( &pszProjection );
if( EQUALN(pszProjOverride,"EPSG:",5)
|| EQUALN(pszProjOverride,"AUTO:",5)
|| EQUALN(pszProjOverride,"OGC:",4)
|| EQUALN(pszProjOverride,"Image ",6)
|| EQUALN(pszProjOverride,"Engineering ",12) )
osCRS = pszProjOverride;
}
/* -------------------------------------------------------------------- */
/* Build CRS name to use. */
/* -------------------------------------------------------------------- */
OGRSpatialReference oSRS;
const char *pszAuth;
if( pszProjection && strlen(pszProjection) > 0 && osCRS == "" )
{
oSRS.SetFromUserInput( pszProjection );
pszAuth = oSRS.GetAuthorityName(NULL);
if( pszAuth != NULL && EQUAL(pszAuth,"EPSG") )
{
pszAuth = oSRS.GetAuthorityCode(NULL);
if( pszAuth )
{
osCRS = "EPSG:";
osCRS += pszAuth;
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unable to define CRS to use." );
return FALSE;
}
}
}
/* -------------------------------------------------------------------- */
/* Pick a format type if we don't already have one selected. */
/* */
/* We will prefer anything that sounds like TIFF, otherwise */
/* falling back to the first supported format. Should we */
/* consider preferring the nativeFormat if available? */
/* -------------------------------------------------------------------- */
if( CPLGetXMLValue( psService, "PreferredFormat", NULL ) == NULL )
{
CPLXMLNode *psSF = CPLGetXMLNode( psCO, "supportedFormats" );
CPLXMLNode *psNode;
char **papszFormatList = NULL;
CPLString osPreferredFormat;
int iFormat;
if( psSF == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"No <PreferredFormat> tag in service definition file, and no\n"
"<supportedFormats> in coverageOffering." );
return FALSE;
}
for( psNode = psSF->psChild; psNode != NULL; psNode = psNode->psNext )
{
if( psNode->eType == CXT_Element
&& EQUAL(psNode->pszValue,"formats")
&& psNode->psChild != NULL
&& psNode->psChild->eType == CXT_Text )
{
// This check is looking for deprecated WCS 1.0 capabilities
// with multiple formats space delimited in a single <formats>
// element per GDAL ticket 1748 (done by MapServer 4.10 and
// earlier for instance).
if( papszFormatList == NULL
&& psNode->psNext == NULL
&& strstr(psNode->psChild->pszValue," ") != NULL
&& strstr(psNode->psChild->pszValue,";") == NULL )
{
char **papszSubList =
CSLTokenizeString( psNode->psChild->pszValue );
papszFormatList = CSLInsertStrings( papszFormatList,
-1, papszSubList );
CSLDestroy( papszSubList );
}
else
{
papszFormatList = CSLAddString( papszFormatList,
psNode->psChild->pszValue);
}
}
}
for( iFormat = 0;
papszFormatList != NULL && papszFormatList[iFormat] != NULL;
iFormat++ )
{
if( strlen(osPreferredFormat) == 0 )
osPreferredFormat = papszFormatList[iFormat];
if( strstr(papszFormatList[iFormat],"tiff") != NULL
|| strstr(papszFormatList[iFormat],"TIFF") != NULL
|| strstr(papszFormatList[iFormat],"Tiff") != NULL )
{
osPreferredFormat = papszFormatList[iFormat];
break;
}
}
CSLDestroy( papszFormatList );
if( strlen(osPreferredFormat) > 0 )
{
bServiceDirty = TRUE;
CPLCreateXMLElementAndValue( psService, "PreferredFormat",
osPreferredFormat );
}
}
/* -------------------------------------------------------------------- */
/* Try to identify a nodata value. For now we only support the */
/* singleValue mechanism. */
/* -------------------------------------------------------------------- */
if( CPLGetXMLValue( psService, "NoDataValue", NULL ) == NULL )
{
const char *pszSV = CPLGetXMLValue( psCO, "rangeSet.RangeSet.nullValues.singleValue", NULL );
if( pszSV != NULL && (CPLAtof(pszSV) != 0.0 || *pszSV == '0') )
{
bServiceDirty = TRUE;
CPLCreateXMLElementAndValue( psService, "NoDataValue",
pszSV );
}
}
/* -------------------------------------------------------------------- */
/* Do we have a Band range type. For now we look for a fairly */
/* specific configuration. The rangeset my have one axis named */
/* "Band", with a set of ascending numerical values. */
/* -------------------------------------------------------------------- */
osBandIdentifier = CPLGetXMLValue( psService, "BandIdentifier", "" );
CPLXMLNode * psAD = CPLGetXMLNode( psService,
"CoverageOffering.rangeSet.RangeSet.axisDescription.AxisDescription" );
CPLXMLNode *psValues;
if( strlen(osBandIdentifier) == 0
&& psAD != NULL
&& (EQUAL(CPLGetXMLValue(psAD,"name",""),"Band")
|| EQUAL(CPLGetXMLValue(psAD,"name",""),"Bands"))
&& ( (psValues = CPLGetXMLNode( psAD, "values" )) != NULL ) )
{
CPLXMLNode *psSV;
int iBand;
osBandIdentifier = CPLGetXMLValue(psAD,"name","");
for( psSV = psValues->psChild, iBand = 1;
psSV != NULL;
psSV = psSV->psNext, iBand++ )
{
if( psSV->eType != CXT_Element
|| !EQUAL(psSV->pszValue,"singleValue")
|| psSV->psChild == NULL
|| psSV->psChild->eType != CXT_Text
|| atoi(psSV->psChild->pszValue) != iBand )
{
osBandIdentifier = "";
break;
}
}
if( strlen(osBandIdentifier) )
{
bServiceDirty = TRUE;
CPLCreateXMLElementAndValue( psService, "BandIdentifier",
osBandIdentifier );
}
}
/* -------------------------------------------------------------------- */
/* Do we have a temporal domain? If so, try to identify a */
/* default time value. */
/* -------------------------------------------------------------------- */
osDefaultTime = CPLGetXMLValue( psService, "DefaultTime", "" );
CPLXMLNode * psTD =
CPLGetXMLNode( psService, "CoverageOffering.domainSet.temporalDomain" );
CPLString osServiceURL = CPLGetXMLValue( psService, "ServiceURL", "" );
CPLString osCoverageExtra = CPLGetXMLValue( psService, "GetCoverageExtra", "" );
if( psTD != NULL )
{
CPLXMLNode *psTime;
// collect all the allowed time positions.
for( psTime = psTD->psChild; psTime != NULL; psTime = psTime->psNext )
{
if( psTime->eType == CXT_Element
&& EQUAL(psTime->pszValue,"timePosition")
&& psTime->psChild != NULL
&& psTime->psChild->eType == CXT_Text )
aosTimePositions.push_back( psTime->psChild->pszValue );
}
// we will default to the last - likely the most recent - entry.
if( aosTimePositions.size() > 0
&& osDefaultTime == ""
&& osServiceURL.ifind("time=") == std::string::npos
&& osCoverageExtra.ifind("time=") == std::string::npos )
{
osDefaultTime = aosTimePositions[aosTimePositions.size()-1];
bServiceDirty = TRUE;
CPLCreateXMLElementAndValue( psService, "DefaultTime",
osDefaultTime );
}
}
return TRUE;
}
/************************************************************************/
/* ParseBoundingBox() */
/************************************************************************/
static int ParseBoundingBox( CPLXMLNode *psBoundingBox, CPLString &osCRS,
double &dfLowerX, double &dfLowerY,
double &dfUpperX, double &dfUpperY )
{
int nRet = TRUE;
osCRS = CPLGetXMLValue( psBoundingBox, "crs", "" );
char **papszLC = CSLTokenizeStringComplex(
CPLGetXMLValue( psBoundingBox, "LowerCorner", ""),
" ", FALSE, FALSE );
char **papszUC = CSLTokenizeStringComplex(
CPLGetXMLValue( psBoundingBox, "UpperCorner", ""),
" ", FALSE, FALSE );
if( CSLCount(papszLC) >= 2 && CSLCount(papszUC) >= 2 )
{
dfLowerX = CPLAtof(papszLC[0]);
dfLowerY = CPLAtof(papszLC[1]);
dfUpperX = CPLAtof(papszUC[0]);
dfUpperY = CPLAtof(papszUC[1]);
}
else
nRet = FALSE;
CSLDestroy( papszUC );
CSLDestroy( papszLC );
return nRet;
}
/************************************************************************/
/* ExtractGridInfo() */
/* */
/* Collect info about grid from describe coverage for WCS 1.1 */
/* and above. */
/************************************************************************/
int WCSDataset::ExtractGridInfo()
{
if( nVersion == 100 )
return ExtractGridInfo100();
CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageDescription" );
if( psCO == NULL )
return FALSE;
/* -------------------------------------------------------------------- */
/* We need to strip off name spaces so it is easier to */
/* searchfor plain gml names. */
/* -------------------------------------------------------------------- */
CPLStripXMLNamespace( psCO, NULL, TRUE );
/* -------------------------------------------------------------------- */
/* Verify we have a SpatialDomain and GridCRS. */
/* -------------------------------------------------------------------- */
CPLXMLNode *psSD =
CPLGetXMLNode( psCO, "Domain.SpatialDomain" );
CPLXMLNode *psGCRS =
CPLGetXMLNode( psSD, "GridCRS" );
if( psSD == NULL || psGCRS == NULL )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unable to find GridCRS in CoverageDescription,\n"
"unable to process WCS Coverage." );
return FALSE;
}
/* -------------------------------------------------------------------- */
/* Extract Geotransform from GridCRS. */
/* -------------------------------------------------------------------- */
const char *pszGridType = CPLGetXMLValue( psGCRS, "GridType",
"urn:ogc:def:method:WCS::2dSimpleGrid" );
char **papszOriginTokens =
CSLTokenizeStringComplex( CPLGetXMLValue( psGCRS, "GridOrigin", ""),
" ", FALSE, FALSE );
char **papszOffsetTokens =
CSLTokenizeStringComplex( CPLGetXMLValue( psGCRS, "GridOffsets", ""),
" ", FALSE, FALSE );
if( strstr(pszGridType,":2dGridIn2dCrs")
|| strstr(pszGridType,":2dGridin2dCrs") )
{
if( CSLCount(papszOffsetTokens) == 4
&& CSLCount(papszOriginTokens) == 2 )
{
adfGeoTransform[0] = CPLAtof(papszOriginTokens[0]);
adfGeoTransform[1] = CPLAtof(papszOffsetTokens[0]);
adfGeoTransform[2] = CPLAtof(papszOffsetTokens[1]);
adfGeoTransform[3] = CPLAtof(papszOriginTokens[1]);
adfGeoTransform[4] = CPLAtof(papszOffsetTokens[2]);
adfGeoTransform[5] = CPLAtof(papszOffsetTokens[3]);
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"2dGridIn2dCrs does not have expected GridOrigin or\n"
"GridOffsets values - unable to process WCS coverage.");
return FALSE;
}
}
else if( strstr(pszGridType,":2dGridIn3dCrs") )
{
if( CSLCount(papszOffsetTokens) == 6
&& CSLCount(papszOriginTokens) == 3 )
{
adfGeoTransform[0] = CPLAtof(papszOriginTokens[0]);
adfGeoTransform[1] = CPLAtof(papszOffsetTokens[0]);
adfGeoTransform[2] = CPLAtof(papszOffsetTokens[1]);
adfGeoTransform[3] = CPLAtof(papszOriginTokens[1]);
adfGeoTransform[4] = CPLAtof(papszOffsetTokens[3]);
adfGeoTransform[5] = CPLAtof(papszOffsetTokens[4]);
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"2dGridIn3dCrs does not have expected GridOrigin or\n"
"GridOffsets values - unable to process WCS coverage.");
return FALSE;
}
}
else if( strstr(pszGridType,":2dSimpleGrid") )
{
if( CSLCount(papszOffsetTokens) == 2
&& CSLCount(papszOriginTokens) == 2 )
{
adfGeoTransform[0] = CPLAtof(papszOriginTokens[0]);
adfGeoTransform[1] = CPLAtof(papszOffsetTokens[0]);
adfGeoTransform[2] = 0.0;
adfGeoTransform[3] = CPLAtof(papszOriginTokens[1]);
adfGeoTransform[4] = 0.0;
adfGeoTransform[5] = CPLAtof(papszOffsetTokens[1]);
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"2dSimpleGrid does not have expected GridOrigin or\n"
"GridOffsets values - unable to process WCS coverage.");
return FALSE;
}
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unrecognised GridCRS.GridType value '%s',\n"
"unable to process WCS coverage.",
pszGridType );
return FALSE;
}
CSLDestroy( papszOffsetTokens );
CSLDestroy( papszOriginTokens );
// GridOrigin is center of pixel ... offset half pixel to adjust.
adfGeoTransform[0] -= (adfGeoTransform[1]+adfGeoTransform[2]) * 0.5;
adfGeoTransform[3] -= (adfGeoTransform[4]+adfGeoTransform[5]) * 0.5;
/* -------------------------------------------------------------------- */
/* Establish our coordinate system. */
/* -------------------------------------------------------------------- */
osCRS = CPLGetXMLValue( psGCRS, "GridBaseCRS", "" );
if( strlen(osCRS) == 0 )
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unable to find GridCRS.GridBaseCRS" );
return FALSE;
}
else if( strstr(osCRS,":imageCRS") )
{
// raw image.
}
else
{
OGRSpatialReference oSRS;
if( oSRS.importFromURN( osCRS ) == OGRERR_NONE )
{
CPLFree( pszProjection );
oSRS.exportToWkt( &pszProjection );
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unable to interprete GridBaseCRS '%s'.",
osCRS.c_str() );
return FALSE;
}
}
/* -------------------------------------------------------------------- */
/* Search for an ImageCRS for raster size. */
/* -------------------------------------------------------------------- */
CPLXMLNode *psNode;
nRasterXSize = -1;
nRasterYSize = -1;
for( psNode = psSD->psChild;
psNode != NULL && nRasterXSize == -1;
psNode = psNode->psNext )
{
if( psNode->eType != CXT_Element
|| !EQUAL(psNode->pszValue,"BoundingBox") )
continue;
double dfLX, dfLY, dfUX, dfUY;
CPLString osBBCRS;
if( ParseBoundingBox( psNode, osBBCRS, dfLX, dfLY, dfUX, dfUY )
&& strstr(osBBCRS,":imageCRS")
&& dfLX == 0 && dfLY == 0 )
{
nRasterXSize = (int) (dfUX + 1.01);
nRasterYSize = (int) (dfUY + 1.01);
}
}
/* -------------------------------------------------------------------- */
/* Otherwise we search for a bounding box in our coordinate */
/* system and derive the size from that. */
/* -------------------------------------------------------------------- */
for( psNode = psSD->psChild;
psNode != NULL && nRasterXSize == -1;
psNode = psNode->psNext )
{
if( psNode->eType != CXT_Element
|| !EQUAL(psNode->pszValue,"BoundingBox") )
continue;
double dfLX, dfLY, dfUX, dfUY;
CPLString osBBCRS;
if( ParseBoundingBox( psNode, osBBCRS, dfLX, dfLY, dfUX, dfUY )
&& osBBCRS == osCRS
&& adfGeoTransform[2] == 0.0
&& adfGeoTransform[4] == 0.0 )
{
nRasterXSize =
(int) ((dfUX - dfLX) / adfGeoTransform[1] + 1.01);
nRasterYSize =
(int) ((dfUY - dfLY) / fabs(adfGeoTransform[5]) + 1.01);
}
}
/* -------------------------------------------------------------------- */
/* Do we have a coordinate system override? */
/* -------------------------------------------------------------------- */
const char *pszProjOverride = CPLGetXMLValue( psService, "SRS", NULL );
if( pszProjOverride )
{
OGRSpatialReference oSRS;
if( oSRS.SetFromUserInput( pszProjOverride ) != OGRERR_NONE )
{
CPLError( CE_Failure, CPLE_AppDefined,
"<SRS> element contents not parsable:\n%s",
pszProjOverride );
return FALSE;
}
CPLFree( pszProjection );
oSRS.exportToWkt( &pszProjection );
}
/* -------------------------------------------------------------------- */
/* Pick a format type if we don't already have one selected. */
/* */
/* We will prefer anything that sounds like TIFF, otherwise */
/* falling back to the first supported format. Should we */
/* consider preferring the nativeFormat if available? */
/* -------------------------------------------------------------------- */
if( CPLGetXMLValue( psService, "PreferredFormat", NULL ) == NULL )
{
CPLXMLNode *psNode;
CPLString osPreferredFormat;
for( psNode = psCO->psChild; psNode != NULL; psNode = psNode->psNext )
{
if( psNode->eType == CXT_Element
&& EQUAL(psNode->pszValue,"SupportedFormat")
&& psNode->psChild
&& psNode->psChild->eType == CXT_Text )
{
if( strlen(osPreferredFormat) == 0 )
osPreferredFormat = psNode->psChild->pszValue;
if( strstr(psNode->psChild->pszValue,"tiff") != NULL
|| strstr(psNode->psChild->pszValue,"TIFF") != NULL
|| strstr(psNode->psChild->pszValue,"Tiff") != NULL )
{
osPreferredFormat = psNode->psChild->pszValue;
break;
}
}
}
if( strlen(osPreferredFormat) > 0 )
{
bServiceDirty = TRUE;
CPLCreateXMLElementAndValue( psService, "PreferredFormat",
osPreferredFormat );
}
}
/* -------------------------------------------------------------------- */
/* Try to identify a nodata value. For now we only support the */
/* singleValue mechanism. */
/* -------------------------------------------------------------------- */
if( CPLGetXMLValue( psService, "NoDataValue", NULL ) == NULL )
{
const char *pszSV =
CPLGetXMLValue( psCO, "Range.Field.NullValue", NULL );
if( pszSV != NULL && (CPLAtof(pszSV) != 0.0 || *pszSV == '0') )
{
bServiceDirty = TRUE;
CPLCreateXMLElementAndValue( psService, "NoDataValue",
pszSV );
}
}
/* -------------------------------------------------------------------- */
/* Grab the field name, if possible. */
/* -------------------------------------------------------------------- */
if( CPLGetXMLValue( psService, "FieldName", NULL ) == NULL )
{
CPLString osFieldName =
CPLGetXMLValue( psCO, "Range.Field.Identifier", "" );
if( strlen(osFieldName) > 0 )
{
bServiceDirty = TRUE;
CPLCreateXMLElementAndValue( psService, "FieldName",
osFieldName );
}
else
{
CPLError( CE_Failure, CPLE_AppDefined,
"Unable to find required Identifier name %s for Range Field.",
osCRS.c_str() );
return FALSE;
}
}
/* -------------------------------------------------------------------- */
/* Do we have a "Band" axis? If so try to grab the bandcount */
/* and data type from it. */
/* -------------------------------------------------------------------- */
CPLXMLNode * psAxis = CPLGetXMLNode(
psService, "CoverageDescription.Range.Field.Axis" );
if( (EQUAL(CPLGetXMLValue(psAxis,"Identifier",""),"Band")
|| EQUAL(CPLGetXMLValue(psAxis,"Identifier",""),"Bands"))
&& CPLGetXMLNode(psAxis,"AvailableKeys") != NULL )
{
osBandIdentifier = CPLGetXMLValue(psAxis,"Identifier","");
// verify keys are ascending starting at 1
CPLXMLNode *psValues = CPLGetXMLNode(psAxis,"AvailableKeys");
CPLXMLNode *psSV;
int iBand;
for( psSV = psValues->psChild, iBand = 1;
psSV != NULL;
psSV = psSV->psNext, iBand++ )
{
if( psSV->eType != CXT_Element
|| !EQUAL(psSV->pszValue,"Key")
|| psSV->psChild == NULL
|| psSV->psChild->eType != CXT_Text
|| atoi(psSV->psChild->pszValue) != iBand )
{
osBandIdentifier = "";
break;
}
}
if( strlen(osBandIdentifier) )
{
bServiceDirty = TRUE;
if( CPLGetXMLValue(psService,"BandIdentifier",NULL) == NULL )
CPLCreateXMLElementAndValue( psService, "BandIdentifier",
osBandIdentifier );
if( CPLGetXMLValue(psService,"BandCount",NULL) == NULL )
CPLCreateXMLElementAndValue( psService, "BandCount",
CPLString().Printf("%d",iBand-1));
}
// Is this an ESRI server returning a GDAL recognised data type?
CPLString osDataType = CPLGetXMLValue( psAxis, "DataType", "" );
if( GDALGetDataTypeByName(osDataType) != GDT_Unknown
&& CPLGetXMLValue(psService,"BandType",NULL) == NULL )
{
bServiceDirty = TRUE;
CPLCreateXMLElementAndValue( psService, "BandType", osDataType );
}
}
return TRUE;
}
/************************************************************************/
/* ProcessError() */
/* */
/* Process an HTTP error, reporting it via CPL, and destroying */
/* the HTTP result object. Returns TRUE if there was an error, */
/* or FALSE if the result seems ok. */
/************************************************************************/
int WCSDataset::ProcessError( CPLHTTPResult *psResult )
{
/* -------------------------------------------------------------------- */
/* There isn't much we can do in this case. Hopefully an error */
/* was already issued by CPLHTTPFetch() */
/* -------------------------------------------------------------------- */
if( psResult == NULL || psResult->nDataLen == 0 )
{
CPLHTTPDestroyResult( psResult );
return TRUE;
}
/* -------------------------------------------------------------------- */
/* If we got an html document, we presume it is an error */
/* message and report it verbatim up to a certain size limit. */
/* -------------------------------------------------------------------- */
if( psResult->pszContentType != NULL
&& strstr(psResult->pszContentType, "html") != NULL )
{
CPLString osErrorMsg = (char *) psResult->pabyData;
if( osErrorMsg.size() > 2048 )
osErrorMsg.resize( 2048 );
CPLError( CE_Failure, CPLE_AppDefined,
"Malformed Result:\n%s",
osErrorMsg.c_str() );
CPLHTTPDestroyResult( psResult );
return TRUE;
}
/* -------------------------------------------------------------------- */
/* Does this look like a service exception? We would like to */
/* check based on the Content-type, but this seems quite */
/* undependable, even from MapServer! */
/* -------------------------------------------------------------------- */
if( strstr((const char *)psResult->pabyData, "ServiceException")
|| strstr((const char *)psResult->pabyData, "ExceptionReport") )
{
CPLXMLNode *psTree = CPLParseXMLString( (const char *)
psResult->pabyData );
const char *pszMsg = NULL;
CPLStripXMLNamespace( psTree, NULL, TRUE );
// VERSION 1.0.0
if( psTree != NULL )
pszMsg = CPLGetXMLValue(psTree,
"=ServiceExceptionReport.ServiceException",
NULL );
// VERSION 1.1.0
if( pszMsg == NULL )
pszMsg = CPLGetXMLValue(psTree,
"=ExceptionReport.Exception.ExceptionText",
NULL );
if( pszMsg )
CPLError( CE_Failure, CPLE_AppDefined,
"%s", pszMsg );
else
CPLError( CE_Failure, CPLE_AppDefined,
"Corrupt Service Exception:\n%s",
(const char *) psResult->pabyData );
CPLDestroyXMLNode( psTree );
CPLHTTPDestroyResult( psResult );
return TRUE;
}
/* -------------------------------------------------------------------- */
/* Hopefully the error already issued by CPLHTTPFetch() is */
/* sufficient. */
/* -------------------------------------------------------------------- */
if( CPLGetLastErrorNo() != 0 )
return TRUE;
return FALSE;
}
/************************************************************************/
/* EstablishRasterDetails() */
/* */
/* Do a "test" coverage query to work out the number of bands, */
/* and pixel data type of the remote coverage. */
/************************************************************************/
int WCSDataset::EstablishRasterDetails()
{
CPLXMLNode * psCO = CPLGetXMLNode( psService, "CoverageOffering" );
const char* pszCols = CPLGetXMLValue( psCO, "dimensionLimit.columns", NULL );
const char* pszRows = CPLGetXMLValue( psCO, "dimensionLimit.rows", NULL );
if( pszCols && pszRows )
{
nMaxCols = atoi(pszCols);
nMaxRows = atoi(pszRows);
SetMetadataItem("MAXNCOLS", pszCols, "IMAGE_STRUCTURE" );
SetMetadataItem("MAXNROWS", pszRows, "IMAGE_STRUCTURE" );
}
/* -------------------------------------------------------------------- */
/* Do we already have bandcount and pixel type settings? */
/* -------------------------------------------------------------------- */
if( CPLGetXMLValue( psService, "BandCount", NULL ) != NULL
&& CPLGetXMLValue( psService, "BandType", NULL ) != NULL )
return TRUE;
/* -------------------------------------------------------------------- */
/* Fetch a small block of raster data. */
/* -------------------------------------------------------------------- */
CPLHTTPResult *psResult = NULL;
CPLErr eErr;
eErr = GetCoverage( 0, 0, 2, 2, 2, 2, 0, NULL, &psResult );
if( eErr != CE_None )
return FALSE;
/* -------------------------------------------------------------------- */
/* Try and open result as a dataseat. */
/* -------------------------------------------------------------------- */
GDALDataset *poDS = GDALOpenResult( psResult );
if( poDS == NULL )
return FALSE;
const char* pszPrj = poDS->GetProjectionRef();
if( pszPrj && strlen(pszPrj) > 0 )
{
if( pszProjection )
CPLFree( pszProjection );
pszProjection = CPLStrdup( pszPrj );
}
/* -------------------------------------------------------------------- */
/* Record details. */
/* -------------------------------------------------------------------- */
if( poDS->GetRasterCount() < 1 )
{
delete poDS;
return FALSE;
}
if( CPLGetXMLValue(psService,"BandCount",NULL) == NULL )
CPLCreateXMLElementAndValue(
psService, "BandCount",
CPLString().Printf("%d",poDS->GetRasterCount()));
CPLCreateXMLElementAndValue(
psService, "BandType",
GDALGetDataTypeName(poDS->GetRasterBand(1)->GetRasterDataType()) );
bServiceDirty = TRUE;
/* -------------------------------------------------------------------- */
/* Cleanup */
/* -------------------------------------------------------------------- */
delete poDS;
FlushMemoryResult();
return TRUE;
}
/************************************************************************/
/* FlushMemoryResult() */
/* */
/* This actually either cleans up the in memory /vsimem/ */
/* temporary file, or the on disk temporary file. */
/************************************************************************/
void WCSDataset::FlushMemoryResult()
{
if( strlen(osResultFilename) > 0 )
{
VSIUnlink( osResultFilename );
osResultFilename = "";
}
if( pabySavedDataBuffer )
{
CPLFree( pabySavedDataBuffer );
pabySavedDataBuffer = NULL;
}
}
/************************************************************************/
/* GDALOpenResult() */
/* */
/* Open a CPLHTTPResult as a GDALDataset (if possible). First */
/* attempt is to open handle it "in memory". Eventually we */
/* will add support for handling it on file if necessary. */
/* */
/* This method will free CPLHTTPResult, the caller should not */
/* access it after the call. */
/************************************************************************/
GDALDataset *WCSDataset::GDALOpenResult( CPLHTTPResult *psResult )
{
FlushMemoryResult();
CPLDebug( "WCS", "GDALOpenResult() on content-type: %s",
psResult->pszContentType );
/* -------------------------------------------------------------------- */
/* If this is multipart/related content type, we should search */
/* for the second part. */
/* -------------------------------------------------------------------- */
GByte *pabyData = psResult->pabyData;
int nDataLen = psResult->nDataLen;
if( psResult->pszContentType
&& strstr(psResult->pszContentType,"multipart")
&& CPLHTTPParseMultipartMime(psResult) )
{
if( psResult->nMimePartCount > 1 )
{
pabyData = psResult->pasMimePart[1].pabyData;
nDataLen = psResult->pasMimePart[1].nDataLen;
if (CSLFindString(psResult->pasMimePart[1].papszHeaders,
"Content-Transfer-Encoding: base64") != -1)
{
nDataLen = CPLBase64DecodeInPlace(pabyData);
}
}
}
/* -------------------------------------------------------------------- */
/* Create a memory file from the result. */
/* -------------------------------------------------------------------- */
// Eventually we should be looking at mime info and stuff to figure
// out an optimal filename, but for now we just use a fixed one.
osResultFilename.Printf( "/vsimem/wcs/%p/wcsresult.dat",
this );
VSILFILE *fp = VSIFileFromMemBuffer( osResultFilename, pabyData, nDataLen,
FALSE );
if( fp == NULL )
{
CPLHTTPDestroyResult(psResult);
return NULL;
}
VSIFCloseL( fp );
/* -------------------------------------------------------------------- */
/* Try opening this result as a gdaldataset. */
/* -------------------------------------------------------------------- */
GDALDataset *poDS = (GDALDataset *)
GDALOpen( osResultFilename, GA_ReadOnly );
/* -------------------------------------------------------------------- */
/* If opening it in memory didn't work, perhaps we need to */
/* write to a temp file on disk? */
/* -------------------------------------------------------------------- */
if( poDS == NULL )
{
CPLString osTempFilename;
VSILFILE *fpTemp;
osTempFilename.Printf( "/tmp/%p_wcs.dat", this );
fpTemp = VSIFOpenL( osTempFilename, "wb" );
if( fpTemp == NULL )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Failed to create temporary file:%s",
osTempFilename.c_str() );
}
else
{
if( VSIFWriteL( pabyData, nDataLen, 1, fpTemp )
!= 1 )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Failed to write temporary file:%s",
osTempFilename.c_str() );
VSIFCloseL( fpTemp );
VSIUnlink( osTempFilename );
}
else
{
VSIFCloseL( fpTemp );
VSIUnlink( osResultFilename );
osResultFilename = osTempFilename;
poDS = (GDALDataset *)
GDALOpen( osResultFilename, GA_ReadOnly );
}
}
}
/* -------------------------------------------------------------------- */
/* Steal the memory buffer from HTTP result. */
/* -------------------------------------------------------------------- */
pabySavedDataBuffer = psResult->pabyData;
psResult->pabyData = NULL;
psResult->nDataLen = psResult->nDataAlloc = 0;
if( poDS == NULL )
FlushMemoryResult();
CPLHTTPDestroyResult(psResult);
return poDS;
}
/************************************************************************/
/* Identify() */
/************************************************************************/
int WCSDataset::Identify( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* Is this a WCS_GDAL service description file or "in url" */
/* equivelent? */
/* -------------------------------------------------------------------- */
if( poOpenInfo->nHeaderBytes == 0
&& EQUALN((const char *) poOpenInfo->pszFilename,"<WCS_GDAL>",10) )
return TRUE;
else if( poOpenInfo->nHeaderBytes >= 10
&& EQUALN((const char *) poOpenInfo->pabyHeader,"<WCS_GDAL>",10) )
return TRUE;
/* -------------------------------------------------------------------- */
/* Is this apparently a WCS subdataset reference? */
/* -------------------------------------------------------------------- */
else if( EQUALN((const char *) poOpenInfo->pszFilename,"WCS_SDS:",8)
&& poOpenInfo->nHeaderBytes == 0 )
return TRUE;
else
return FALSE;
}
/************************************************************************/
/* Open() */
/************************************************************************/
GDALDataset *WCSDataset::Open( GDALOpenInfo * poOpenInfo )
{
char **papszModifiers = NULL;
/* -------------------------------------------------------------------- */
/* Is this a WCS_GDAL service description file or "in url" */
/* equivelent? */
/* -------------------------------------------------------------------- */
CPLXMLNode *psService = NULL;
if( poOpenInfo->nHeaderBytes == 0
&& EQUALN((const char *) poOpenInfo->pszFilename,"<WCS_GDAL>",10) )
{
psService = CPLParseXMLString( poOpenInfo->pszFilename );
}
else if( poOpenInfo->nHeaderBytes >= 10
&& EQUALN((const char *) poOpenInfo->pabyHeader,"<WCS_GDAL>",10) )
{
psService = CPLParseXMLFile( poOpenInfo->pszFilename );
}
/* -------------------------------------------------------------------- */
/* Is this apparently a subdataset? */
/* -------------------------------------------------------------------- */
else if( EQUALN((const char *) poOpenInfo->pszFilename,"WCS_SDS:",8)
&& poOpenInfo->nHeaderBytes == 0 )
{
int iLast;
papszModifiers = CSLTokenizeString2( poOpenInfo->pszFilename+8, ",",
CSLT_HONOURSTRINGS );
iLast = CSLCount(papszModifiers)-1;
if( iLast >= 0 )
{
psService = CPLParseXMLFile( papszModifiers[iLast] );
CPLFree( papszModifiers[iLast] );
papszModifiers[iLast] = NULL;
}
}
/* -------------------------------------------------------------------- */
/* Success so far? */
/* -------------------------------------------------------------------- */
if( psService == NULL )
{
CSLDestroy( papszModifiers );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Confirm the requested access is supported. */
/* -------------------------------------------------------------------- */
if( poOpenInfo->eAccess == GA_Update )
{
CSLDestroy( papszModifiers );
CPLDestroyXMLNode( psService );
CPLError( CE_Failure, CPLE_NotSupported,
"The WCS driver does not support update access to existing"
" datasets.\n" );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Check for required minimum fields. */
/* -------------------------------------------------------------------- */
if( !CPLGetXMLValue( psService, "ServiceURL", NULL )
|| !CPLGetXMLValue( psService, "CoverageName", NULL ) )
{
CSLDestroy( papszModifiers );
CPLError( CE_Failure, CPLE_OpenFailed,
"Missing one or both of ServiceURL and CoverageName elements.\n"
"See WCS driver documentation for details on service description file format." );
CPLDestroyXMLNode( psService );
return NULL;
}
/* -------------------------------------------------------------------- */
/* What version are we working with? */
/* -------------------------------------------------------------------- */
const char *pszVersion = CPLGetXMLValue( psService, "Version", "1.0.0" );
int nVersion;
if (EQUAL(pszVersion, "1.1.2") )
nVersion = 112;
else if( EQUAL(pszVersion,"1.1.1") )
nVersion = 111;
else if( EQUAL(pszVersion,"1.1.0") )
nVersion = 110;
else if( EQUAL(pszVersion,"1.0.0") )
nVersion = 100;
else
{
CSLDestroy( papszModifiers );
CPLError( CE_Failure, CPLE_AppDefined,
"WCS Version '%s' not supported.", pszVersion );
CPLDestroyXMLNode( psService );
return NULL;
}
/* -------------------------------------------------------------------- */
/* Create a corresponding GDALDataset. */
/* -------------------------------------------------------------------- */
WCSDataset *poDS;
poDS = new WCSDataset();
poDS->psService = psService;
poDS->SetDescription( poOpenInfo->pszFilename );
poDS->nVersion = nVersion;
poDS->papszSDSModifiers = papszModifiers;
/* -------------------------------------------------------------------- */
/* Capture HTTP parameters. */
/* -------------------------------------------------------------------- */
const char *pszParm;
poDS->papszHttpOptions =
CSLSetNameValue(poDS->papszHttpOptions,
"TIMEOUT",
CPLGetXMLValue( psService, "Timeout", "30" ) );
pszParm = CPLGetXMLValue( psService, "HTTPAUTH", NULL );
if( pszParm )
poDS->papszHttpOptions =
CSLSetNameValue( poDS->papszHttpOptions,
"HTTPAUTH", pszParm );
pszParm = CPLGetXMLValue( psService, "USERPWD", NULL );
if( pszParm )
poDS->papszHttpOptions =
CSLSetNameValue( poDS->papszHttpOptions,
"USERPWD", pszParm );
/* -------------------------------------------------------------------- */
/* If we don't have the DescribeCoverage result for this */
/* coverage, fetch it now. */
/* -------------------------------------------------------------------- */
if( CPLGetXMLNode( psService, "CoverageOffering" ) == NULL
&& CPLGetXMLNode( psService, "CoverageDescription" ) == NULL )
{
if( !poDS->DescribeCoverage() )
{
delete poDS;
return NULL;
}
}
/* -------------------------------------------------------------------- */
/* Extract coordinate system, grid size, and geotransform from */
/* the coverage description and/or service description */
/* information. */
/* -------------------------------------------------------------------- */
if( !poDS->ExtractGridInfo() )
{
delete poDS;
return NULL;
}
if( !poDS->EstablishRasterDetails() )
{
delete poDS;
return NULL;
}
/* -------------------------------------------------------------------- */
/* Create band information objects. */
/* -------------------------------------------------------------------- */
int nBandCount = atoi(CPLGetXMLValue(psService,"BandCount","1"));
int iBand;
if (!GDALCheckBandCount(nBandCount, 0))
{
delete poDS;
return NULL;
}
for( iBand = 0; iBand < nBandCount; iBand++ )
poDS->SetBand( iBand+1, new WCSRasterBand( poDS, iBand+1, -1 ) );
/* -------------------------------------------------------------------- */
/* Set time metadata on the dataset if we are selecting a */
/* temporal slice. */
/* -------------------------------------------------------------------- */
CPLString osTime = CSLFetchNameValueDef( poDS->papszSDSModifiers, "time",
poDS->osDefaultTime );
if( osTime != "" )
poDS->GDALMajorObject::SetMetadataItem( "TIME_POSITION",
osTime.c_str() );
/* -------------------------------------------------------------------- */
/* Do we have a band identifier to select only a subset of bands? */
/* -------------------------------------------------------------------- */
poDS->osBandIdentifier = CPLGetXMLValue(psService,"BandIdentifier","");
/* -------------------------------------------------------------------- */
/* Do we have time based subdatasets? If so, record them in */
/* metadata. Note we don't do subdatasets if this is a */
/* subdataset or if this is an all-in-memory service. */
/* -------------------------------------------------------------------- */
if( !EQUALN(poOpenInfo->pszFilename,"WCS_SDS:",8)
&& !EQUALN(poOpenInfo->pszFilename,"<WCS_GDAL>",10)
&& poDS->aosTimePositions.size() > 0 )
{
char **papszSubdatasets = NULL;
int iTime;
for( iTime = 0; iTime < (int)poDS->aosTimePositions.size(); iTime++ )
{
CPLString osName;
CPLString osValue;
osName.Printf( "SUBDATASET_%d_NAME", iTime+1 );
osValue.Printf( "WCS_SDS:time=\"%s\",%s",
poDS->aosTimePositions[iTime].c_str(),
poOpenInfo->pszFilename );
papszSubdatasets = CSLSetNameValue( papszSubdatasets,
osName, osValue );
CPLString osCoverage =
CPLGetXMLValue( poDS->psService, "CoverageName", "" );
osName.Printf( "SUBDATASET_%d_DESC", iTime+1 );
osValue.Printf( "Coverage %s at time %s",
osCoverage.c_str(),
poDS->aosTimePositions[iTime].c_str() );
papszSubdatasets = CSLSetNameValue( papszSubdatasets,
osName, osValue );
}
poDS->GDALMajorObject::SetMetadata( papszSubdatasets,
"SUBDATASETS" );
CSLDestroy( papszSubdatasets );
}
/* -------------------------------------------------------------------- */
/* Initialize any PAM information. */
/* -------------------------------------------------------------------- */
poDS->TryLoadXML();
return( poDS );
}
/************************************************************************/
/* GetGeoTransform() */
/************************************************************************/
CPLErr WCSDataset::GetGeoTransform( double * padfTransform )
{
memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
return( CE_None );
}
/************************************************************************/
/* GetProjectionRef() */
/************************************************************************/
const char *WCSDataset::GetProjectionRef()
{
const char* pszPrj = GDALPamDataset::GetProjectionRef();
if( pszPrj && strlen(pszPrj) > 0 )
return pszPrj;
if ( pszProjection && strlen(pszProjection) > 0 )
return pszProjection;
return( "" );
}
/************************************************************************/
/* GetFileList() */
/************************************************************************/
char **WCSDataset::GetFileList()
{
char **papszFileList = GDALPamDataset::GetFileList();
/* -------------------------------------------------------------------- */
/* ESRI also wishes to include service urls in the file list */
/* though this is not currently part of the general definition */
/* of GetFileList() for GDAL. */
/* -------------------------------------------------------------------- */
#ifdef ESRI_BUILD
CPLString file;
file.Printf( "%s%s",
CPLGetXMLValue( psService, "ServiceURL", "" ),
CPLGetXMLValue( psService, "CoverageName", "" ) );
papszFileList = CSLAddString( papszFileList, file.c_str() );
#endif /* def ESRI_BUILD */
return papszFileList;
}
/************************************************************************/
/* GetMetadataDomainList() */
/************************************************************************/
char **WCSDataset::GetMetadataDomainList()
{
return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
TRUE,
"xml:CoverageOffering", NULL);
}
/************************************************************************/
/* GetMetadata() */
/************************************************************************/
char **WCSDataset::GetMetadata( const char *pszDomain )
{
if( pszDomain == NULL
|| !EQUAL(pszDomain,"xml:CoverageOffering") )
return GDALPamDataset::GetMetadata( pszDomain );
CPLXMLNode *psNode = CPLGetXMLNode( psService, "CoverageOffering" );
if( psNode == NULL )
psNode = CPLGetXMLNode( psService, "CoverageDescription" );
if( psNode == NULL )
return NULL;
if( apszCoverageOfferingMD[0] == NULL )
{
CPLXMLNode *psNext = psNode->psNext;
psNode->psNext = NULL;
apszCoverageOfferingMD[0] = CPLSerializeXMLTree( psNode );
psNode->psNext = psNext;
}
return apszCoverageOfferingMD;
}
/************************************************************************/
/* GDALRegister_WCS() */
/************************************************************************/
void GDALRegister_WCS()
{
GDALDriver *poDriver;
if( GDALGetDriverByName( "WCS" ) == NULL )
{
poDriver = new GDALDriver();
poDriver->SetDescription( "WCS" );
poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
"OGC Web Coverage Service" );
poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
"frmt_wcs.html" );
poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
poDriver->SetMetadataItem( GDAL_DMD_SUBDATASETS, "YES" );
poDriver->pfnOpen = WCSDataset::Open;
poDriver->pfnIdentify = WCSDataset::Identify;
GetGDALDriverManager()->RegisterDriver( poDriver );
}
}