mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-06-01 14:22:41 -06:00
570 lines
21 KiB
C++
570 lines
21 KiB
C++
/******************************************************************************
|
|
* $Id: gdaloverviewdataset.cpp 29128 2015-05-03 13:21:49Z rouault $
|
|
*
|
|
* Project: GDAL Core
|
|
* Purpose: Implementation of a dataset overview warping class
|
|
* Author: Even Rouault, <even dot rouault at spatialys dot com>
|
|
*
|
|
******************************************************************************
|
|
* Copyright (c) 2014, Even Rouault, <even dot rouault at spatialys 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 "gdal_proxy.h"
|
|
#include "gdal_mdreader.h"
|
|
|
|
CPL_CVSID("$Id: gdaloverviewdataset.cpp 29128 2015-05-03 13:21:49Z rouault $");
|
|
|
|
/** In GDAL, GDALRasterBand::GetOverview() returns a stand-alone band, that
|
|
may have no parent dataset. This can be inconvenient in certain contexts, where
|
|
cross-band processing must be done, or when API expect a fully fledge dataset.
|
|
Furthermore even if overview band has a container dataset, that one often
|
|
fails to declare its projection, geotransform, etc... which make it somehow
|
|
useless. GDALOverviewDataset remedies to those deficiencies.
|
|
*/
|
|
|
|
class GDALOverviewBand;
|
|
|
|
/* ******************************************************************** */
|
|
/* GDALOverviewDataset */
|
|
/* ******************************************************************** */
|
|
|
|
class GDALOverviewDataset : public GDALDataset
|
|
{
|
|
private:
|
|
|
|
friend class GDALOverviewBand;
|
|
|
|
GDALDataset* poMainDS;
|
|
int bOwnDS;
|
|
|
|
GDALDataset* poOvrDS; /* will be often NULL */
|
|
int nOvrLevel;
|
|
int bThisLevelOnly;
|
|
|
|
int nGCPCount;
|
|
GDAL_GCP *pasGCPList;
|
|
char **papszMD_RPC;
|
|
char **papszMD_GEOLOCATION;
|
|
|
|
static void Rescale(char**& papszMD, const char* pszItem,
|
|
double dfRatio, double dfDefaultVal);
|
|
|
|
protected:
|
|
virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
|
|
void *, int, int, GDALDataType,
|
|
int, int *,
|
|
GSpacing, GSpacing, GSpacing,
|
|
GDALRasterIOExtraArg* psExtraArg );
|
|
|
|
public:
|
|
GDALOverviewDataset(GDALDataset* poMainDS, int nOvrLevel,
|
|
int bThisLevelOnly,
|
|
int bOwnDS);
|
|
virtual ~GDALOverviewDataset();
|
|
|
|
virtual const char *GetProjectionRef(void);
|
|
virtual CPLErr GetGeoTransform( double * );
|
|
|
|
virtual int GetGCPCount();
|
|
virtual const char *GetGCPProjection();
|
|
virtual const GDAL_GCP *GetGCPs();
|
|
|
|
virtual char **GetMetadata( const char * pszDomain = "" );
|
|
virtual const char *GetMetadataItem( const char * pszName,
|
|
const char * pszDomain = "" );
|
|
|
|
virtual int CloseDependentDatasets();
|
|
};
|
|
|
|
/* ******************************************************************** */
|
|
/* GDALOverviewBand */
|
|
/* ******************************************************************** */
|
|
|
|
class GDALOverviewBand : public GDALProxyRasterBand
|
|
{
|
|
protected:
|
|
friend class GDALOverviewDataset;
|
|
|
|
GDALRasterBand* poUnderlyingBand;
|
|
virtual GDALRasterBand* RefUnderlyingRasterBand();
|
|
|
|
public:
|
|
GDALOverviewBand(GDALOverviewDataset* poDS, int nBand);
|
|
virtual ~GDALOverviewBand();
|
|
|
|
virtual CPLErr FlushCache();
|
|
|
|
virtual int GetOverviewCount();
|
|
virtual GDALRasterBand *GetOverview(int);
|
|
};
|
|
|
|
/************************************************************************/
|
|
/* GDALCreateOverviewDataset() */
|
|
/************************************************************************/
|
|
|
|
GDALDataset* GDALCreateOverviewDataset(GDALDataset* poMainDS, int nOvrLevel,
|
|
int bThisLevelOnly, int bOwnDS)
|
|
{
|
|
/* Sanity checks */
|
|
int nBands = poMainDS->GetRasterCount();
|
|
if( nBands == 0 )
|
|
return NULL;
|
|
|
|
for(int i = 1; i<= nBands; i++ )
|
|
{
|
|
if( poMainDS->GetRasterBand(i)->GetOverview(nOvrLevel) == NULL )
|
|
{
|
|
return NULL;
|
|
}
|
|
if( poMainDS->GetRasterBand(i)->GetOverview(nOvrLevel)->GetXSize() !=
|
|
poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetXSize() ||
|
|
poMainDS->GetRasterBand(i)->GetOverview(nOvrLevel)->GetYSize() !=
|
|
poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetYSize() )
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return new GDALOverviewDataset(poMainDS, nOvrLevel, bThisLevelOnly, bOwnDS);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALOverviewDataset() */
|
|
/************************************************************************/
|
|
|
|
GDALOverviewDataset::GDALOverviewDataset(GDALDataset* poMainDS,
|
|
int nOvrLevel,
|
|
int bThisLevelOnly,
|
|
int bOwnDS)
|
|
{
|
|
this->poMainDS = poMainDS;
|
|
this->nOvrLevel = nOvrLevel;
|
|
this->bOwnDS = bOwnDS;
|
|
this->bThisLevelOnly = bThisLevelOnly;
|
|
eAccess = poMainDS->GetAccess();
|
|
nRasterXSize = poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetXSize();
|
|
nRasterYSize = poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetYSize();
|
|
poOvrDS = poMainDS->GetRasterBand(1)->GetOverview(nOvrLevel)->GetDataset();
|
|
if( poOvrDS != NULL && poOvrDS == poMainDS )
|
|
{
|
|
CPLDebug("GDAL", "Dataset of overview is the same as the main band. This is not expected");
|
|
poOvrDS = NULL;
|
|
}
|
|
nBands = poMainDS->GetRasterCount();
|
|
for(int i=0;i<nBands;i++)
|
|
{
|
|
SetBand(i+1, new GDALOverviewBand(this, i+1));
|
|
}
|
|
nGCPCount = 0;
|
|
pasGCPList = NULL;
|
|
papszMD_RPC = NULL;
|
|
papszMD_GEOLOCATION = NULL;
|
|
|
|
/* We create a fake driver that has the same name as the original */
|
|
/* one, but we cannot use the real driver object, so that code */
|
|
/* doesn't try to cast the GDALOverviewDataset* as a native dataset */
|
|
/* object */
|
|
if( poMainDS->GetDriver() != NULL )
|
|
{
|
|
poDriver = new GDALDriver();
|
|
poDriver->SetDescription(poMainDS->GetDriver()->GetDescription());
|
|
poDriver->SetMetadata(poMainDS->GetDriver()->GetMetadata());
|
|
}
|
|
|
|
SetDescription( poMainDS->GetDescription() );
|
|
|
|
CPLDebug( "GDAL", "GDALOverviewDataset(%s, this=%p) creation.",
|
|
poMainDS->GetDescription(), this );
|
|
|
|
papszOpenOptions = CSLDuplicate(poMainDS->GetOpenOptions());
|
|
/* Add OVERVIEW_LEVEL if not called from GDALOpenEx() but directly */
|
|
papszOpenOptions = CSLSetNameValue(papszOpenOptions, "OVERVIEW_LEVEL",
|
|
CPLSPrintf("%d", nOvrLevel));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GDALOverviewDataset() */
|
|
/************************************************************************/
|
|
|
|
GDALOverviewDataset::~GDALOverviewDataset()
|
|
{
|
|
FlushCache();
|
|
|
|
CloseDependentDatasets();
|
|
|
|
if( nGCPCount > 0 )
|
|
{
|
|
GDALDeinitGCPs( nGCPCount, pasGCPList );
|
|
CPLFree( pasGCPList );
|
|
}
|
|
CSLDestroy(papszMD_RPC);
|
|
|
|
CSLDestroy(papszMD_GEOLOCATION);
|
|
|
|
delete poDriver;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* CloseDependentDatasets() */
|
|
/************************************************************************/
|
|
|
|
int GDALOverviewDataset::CloseDependentDatasets()
|
|
{
|
|
int bRet = FALSE;
|
|
|
|
if( bOwnDS )
|
|
{
|
|
for(int i=0;i<nBands;i++)
|
|
{
|
|
((GDALOverviewBand*)papoBands[i])->poUnderlyingBand = NULL;
|
|
}
|
|
GDALClose( poMainDS );
|
|
poMainDS = NULL;
|
|
bOwnDS = FALSE;
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* IRasterIO() */
|
|
/* */
|
|
/* The default implementation of IRasterIO() is to pass the */
|
|
/* request off to each band objects rasterio methods with */
|
|
/* appropriate arguments. */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALOverviewDataset::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)
|
|
|
|
{
|
|
int iBandIndex;
|
|
CPLErr eErr = CE_None;
|
|
|
|
/* In case the overview bands are really linked to a dataset, then issue */
|
|
/* the request to that dataset */
|
|
if( poOvrDS != NULL )
|
|
{
|
|
return poOvrDS->RasterIO(
|
|
eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
|
|
eBufType, nBandCount, panBandMap, nPixelSpace, nLineSpace, nBandSpace,
|
|
psExtraArg);
|
|
}
|
|
|
|
GDALProgressFunc pfnProgressGlobal = psExtraArg->pfnProgress;
|
|
void *pProgressDataGlobal = psExtraArg->pProgressData;
|
|
|
|
for( iBandIndex = 0;
|
|
iBandIndex < nBandCount && eErr == CE_None;
|
|
iBandIndex++ )
|
|
{
|
|
GDALOverviewBand *poBand = (GDALOverviewBand*) GetRasterBand(panBandMap[iBandIndex]);
|
|
GByte *pabyBandData;
|
|
|
|
if (poBand == NULL)
|
|
{
|
|
eErr = CE_Failure;
|
|
break;
|
|
}
|
|
|
|
pabyBandData = ((GByte *) pData) + iBandIndex * nBandSpace;
|
|
|
|
psExtraArg->pfnProgress = GDALScaledProgress;
|
|
psExtraArg->pProgressData =
|
|
GDALCreateScaledProgress( 1.0 * iBandIndex / nBandCount,
|
|
1.0 * (iBandIndex + 1) / nBandCount,
|
|
pfnProgressGlobal,
|
|
pProgressDataGlobal );
|
|
|
|
eErr = poBand->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
|
|
(void *) pabyBandData, nBufXSize, nBufYSize,
|
|
eBufType, nPixelSpace, nLineSpace, psExtraArg );
|
|
|
|
GDALDestroyScaledProgress( psExtraArg->pProgressData );
|
|
}
|
|
|
|
psExtraArg->pfnProgress = pfnProgressGlobal;
|
|
psExtraArg->pProgressData = pProgressDataGlobal;
|
|
|
|
return eErr;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetProjectionRef() */
|
|
/************************************************************************/
|
|
|
|
const char *GDALOverviewDataset::GetProjectionRef()
|
|
|
|
{
|
|
return poMainDS->GetProjectionRef();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGeoTransform() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALOverviewDataset::GetGeoTransform( double * padfTransform )
|
|
|
|
{
|
|
double adfGeoTransform[6];
|
|
if( poMainDS->GetGeoTransform(adfGeoTransform) == CE_None )
|
|
{
|
|
if( adfGeoTransform[2] == 0.0 && adfGeoTransform[4] == 0.0 )
|
|
{
|
|
adfGeoTransform[1] *= (double)poMainDS->GetRasterXSize() / nRasterXSize;
|
|
adfGeoTransform[5] *= (double)poMainDS->GetRasterYSize() / nRasterYSize;
|
|
}
|
|
else
|
|
{
|
|
/* If the x and y ratios are not equal, then we cannot really */
|
|
/* compute a geotransform */
|
|
double dfRatio = (double)poMainDS->GetRasterXSize() / nRasterXSize;
|
|
adfGeoTransform[1] *= dfRatio;
|
|
adfGeoTransform[2] *= dfRatio;
|
|
adfGeoTransform[4] *= dfRatio;
|
|
adfGeoTransform[5] *= dfRatio;
|
|
}
|
|
memcpy( padfTransform, adfGeoTransform, sizeof(double)*6 );
|
|
|
|
return CE_None;
|
|
}
|
|
else
|
|
return CE_Failure;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCPCount() */
|
|
/************************************************************************/
|
|
|
|
int GDALOverviewDataset::GetGCPCount()
|
|
|
|
{
|
|
return poMainDS->GetGCPCount();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCPProjection() */
|
|
/************************************************************************/
|
|
|
|
const char *GDALOverviewDataset::GetGCPProjection()
|
|
|
|
{
|
|
return poMainDS->GetGCPProjection();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetGCPs() */
|
|
/************************************************************************/
|
|
|
|
const GDAL_GCP *GDALOverviewDataset::GetGCPs()
|
|
|
|
{
|
|
if( pasGCPList != NULL )
|
|
return pasGCPList;
|
|
|
|
const GDAL_GCP* pasGCPsMain = poMainDS->GetGCPs();
|
|
if( pasGCPsMain == NULL )
|
|
return NULL;
|
|
nGCPCount = poMainDS->GetGCPCount();
|
|
|
|
pasGCPList = GDALDuplicateGCPs( nGCPCount, pasGCPsMain );
|
|
for(int i = 0; i < nGCPCount; i++)
|
|
{
|
|
pasGCPList[i].dfGCPPixel *= (double)nRasterXSize / poMainDS->GetRasterXSize();
|
|
pasGCPList[i].dfGCPLine *= (double)nRasterYSize / poMainDS->GetRasterYSize();
|
|
}
|
|
return pasGCPList;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* Rescale() */
|
|
/************************************************************************/
|
|
|
|
void GDALOverviewDataset::Rescale(char**& papszMD, const char* pszItem,
|
|
double dfRatio, double dfDefaultVal)
|
|
{
|
|
double dfVal = CPLAtofM(CSLFetchNameValueDef(papszMD, pszItem,
|
|
CPLSPrintf("%.18g", dfDefaultVal)));
|
|
dfVal *= dfRatio;
|
|
papszMD = CSLSetNameValue(papszMD, pszItem, CPLSPrintf("%.18g", dfVal));
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMetadata() */
|
|
/************************************************************************/
|
|
|
|
char **GDALOverviewDataset::GetMetadata( const char * pszDomain )
|
|
{
|
|
char** papszMD;
|
|
if (poOvrDS != NULL)
|
|
{
|
|
papszMD = poOvrDS->GetMetadata(pszDomain);
|
|
if( papszMD != NULL )
|
|
return papszMD;
|
|
}
|
|
|
|
papszMD = poMainDS->GetMetadata(pszDomain);
|
|
|
|
/* We may need to rescale some values from the RPC metadata domain */
|
|
if( pszDomain != NULL && EQUAL(pszDomain, MD_DOMAIN_RPC) && papszMD != NULL )
|
|
{
|
|
if( papszMD_RPC )
|
|
return papszMD_RPC;
|
|
papszMD_RPC = CSLDuplicate(papszMD);
|
|
|
|
Rescale(papszMD_RPC, RPC_LINE_OFF,
|
|
(double)nRasterYSize / poMainDS->GetRasterYSize(), 0.0);
|
|
Rescale(papszMD_RPC, RPC_LINE_SCALE,
|
|
(double)nRasterYSize / poMainDS->GetRasterYSize(), 1.0);
|
|
Rescale(papszMD_RPC, RPC_SAMP_OFF,
|
|
(double)nRasterXSize / poMainDS->GetRasterXSize(), 0.0);
|
|
Rescale(papszMD_RPC, RPC_SAMP_SCALE,
|
|
(double)nRasterXSize / poMainDS->GetRasterXSize(), 1.0);
|
|
|
|
papszMD = papszMD_RPC;
|
|
}
|
|
|
|
/* We may need to rescale some values from the GEOLOCATION metadata domain */
|
|
if( pszDomain != NULL && EQUAL(pszDomain, "GEOLOCATION") && papszMD != NULL )
|
|
{
|
|
if( papszMD_GEOLOCATION )
|
|
return papszMD_GEOLOCATION;
|
|
papszMD_GEOLOCATION = CSLDuplicate(papszMD);
|
|
|
|
Rescale(papszMD_GEOLOCATION, "PIXEL_OFFSET",
|
|
(double)poMainDS->GetRasterXSize() / nRasterXSize, 0.0);
|
|
Rescale(papszMD_GEOLOCATION, "LINE_OFFSET",
|
|
(double)poMainDS->GetRasterYSize() / nRasterYSize, 0.0);
|
|
|
|
Rescale(papszMD_GEOLOCATION, "PIXEL_STEP",
|
|
(double)nRasterXSize / poMainDS->GetRasterXSize(), 1.0);
|
|
Rescale(papszMD_GEOLOCATION, "LINE_STEP",
|
|
(double)nRasterYSize / poMainDS->GetRasterYSize(), 1.0);
|
|
|
|
papszMD = papszMD_GEOLOCATION;
|
|
}
|
|
|
|
return papszMD;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetMetadataItem() */
|
|
/************************************************************************/
|
|
|
|
const char *GDALOverviewDataset::GetMetadataItem( const char * pszName,
|
|
const char * pszDomain )
|
|
{
|
|
if (poOvrDS != NULL)
|
|
{
|
|
const char* pszValue = poOvrDS->GetMetadataItem(pszName, pszDomain);
|
|
if( pszValue != NULL )
|
|
return pszValue;
|
|
}
|
|
|
|
if( pszDomain != NULL && (EQUAL(pszDomain, "RPC") ||
|
|
EQUAL(pszDomain, "GEOLOCATION")) )
|
|
{
|
|
char** papszMD = GetMetadata(pszDomain);
|
|
return CSLFetchNameValue(papszMD, pszName);
|
|
}
|
|
|
|
return poMainDS->GetMetadataItem(pszName, pszDomain);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GDALOverviewBand() */
|
|
/************************************************************************/
|
|
|
|
GDALOverviewBand::GDALOverviewBand(GDALOverviewDataset* poDS, int nBand)
|
|
{
|
|
this->poDS = poDS;
|
|
this->nBand = nBand;
|
|
poUnderlyingBand = poDS->poMainDS->GetRasterBand(nBand)->GetOverview(poDS->nOvrLevel);
|
|
nRasterXSize = poDS->nRasterXSize;
|
|
nRasterYSize = poDS->nRasterYSize;
|
|
eDataType = poUnderlyingBand->GetRasterDataType();
|
|
poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* ~GDALOverviewBand() */
|
|
/************************************************************************/
|
|
|
|
GDALOverviewBand::~GDALOverviewBand()
|
|
{
|
|
FlushCache();
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* FlushCache() */
|
|
/************************************************************************/
|
|
|
|
CPLErr GDALOverviewBand::FlushCache()
|
|
{
|
|
if( ((GDALOverviewDataset*)poDS)->poMainDS )
|
|
return poUnderlyingBand->FlushCache();
|
|
return CE_None;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* RefUnderlyingRasterBand() */
|
|
/************************************************************************/
|
|
|
|
GDALRasterBand* GDALOverviewBand::RefUnderlyingRasterBand()
|
|
{
|
|
if( ((GDALOverviewDataset*)poDS)->poMainDS )
|
|
return poUnderlyingBand;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetOverviewCount() */
|
|
/************************************************************************/
|
|
|
|
int GDALOverviewBand::GetOverviewCount()
|
|
{
|
|
GDALOverviewDataset* poOvrDS = (GDALOverviewDataset*)poDS;
|
|
if( poOvrDS->bThisLevelOnly )
|
|
return 0;
|
|
GDALDataset* poMainDS = poOvrDS->poMainDS;
|
|
return poMainDS->GetRasterBand(nBand)->GetOverviewCount() - poOvrDS->nOvrLevel - 1;
|
|
}
|
|
|
|
/************************************************************************/
|
|
/* GetOverview() */
|
|
/************************************************************************/
|
|
|
|
GDALRasterBand *GDALOverviewBand::GetOverview(int iOvr)
|
|
{
|
|
if( iOvr < 0 || iOvr >= GetOverviewCount() )
|
|
return NULL;
|
|
GDALOverviewDataset* poOvrDS = (GDALOverviewDataset*)poDS;
|
|
GDALDataset* poMainDS = poOvrDS->poMainDS;
|
|
return poMainDS->GetRasterBand(nBand)->GetOverview(iOvr + poOvrDS->nOvrLevel + 1);
|
|
}
|