Skip to content

Commit

Permalink
Allow cancelation of raster stats/histogram operations
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Apr 3, 2017
1 parent b9f1f0e commit e400ab6
Show file tree
Hide file tree
Showing 8 changed files with 41 additions and 43 deletions.
1 change: 1 addition & 0 deletions doc/api_break.dox
Expand Up @@ -1671,6 +1671,7 @@ QgsRasterInterface {#qgis_api_break_3_0_QgsRasterInterface}
- srcDataType() has been renamed to sourceDataType()
- srcInput() has been renamed to sourceInput()
- block() has new "feedback" argument.
- The signature of histogram() and bandStatistics() now takes a QgsRasterBlockFeedback pointer argument.


QgsRasterLayer {#qgis_api_break_3_0_QgsRasterLayer}
Expand Down
31 changes: 6 additions & 25 deletions python/core/raster/qgsrasterinterface.sip
Expand Up @@ -198,39 +198,20 @@ class QgsRasterInterface
*/
virtual QgsRasterBandStats bandStatistics( int bandNo,
int stats = QgsRasterBandStats::All,
const QgsRectangle & extent = QgsRectangle(),
int sampleSize = 0 );

/** \brief Returns true if histogram is available (cached, already calculated). * The parameters are the same as in bandStatistics()
* @return true if statistics are available (ready to use)
*/
const QgsRectangle &extent = QgsRectangle(),
int sampleSize = 0, QgsRasterBlockFeedback *feedback = 0 );
virtual bool hasStatistics( int bandNo,
int stats = QgsRasterBandStats::All,
const QgsRectangle & extent = QgsRectangle(),
const QgsRectangle &extent = QgsRectangle(),
int sampleSize = 0 );

/** \brief Get histogram. Histograms are cached in providers.
* @param bandNo The band (number).
* @param binCount Number of bins (intervals,buckets). If 0, the number of bins is decided automatically according to data type, raster size etc.
* @param minimum Minimum value, if NaN, raster minimum value will be used.
* @param maximum Maximum value, if NaN, raster minimum value will be used.
* @param extent Extent used to calc histogram, if empty, whole raster extent is used.
* @param sampleSize Approximate number of cells in sample. If 0, all cells (whole raster will be used). If raster does not have exact size (WCS without exact size for example), provider decides size of sample.
* @param includeOutOfRange include out of range values
* @return Vector of non NULL cell counts for each bin.
* @note binCount, minimum and maximum not optional in python bindings
*/
virtual QgsRasterHistogram histogram( int bandNo,
int binCount,
double minimum,
double maximum,
const QgsRectangle & extent,
int sampleSize,
bool includeOutOfRange );
const QgsRectangle &extent = QgsRectangle(),
int sampleSize = 0,
bool includeOutOfRange = false, QgsRasterBlockFeedback *feedback = 0 );

/** \brief Returns true if histogram is available (cached, already calculated), the parameters are the same as in histogram()
* @note binCount, minimum and maximum not optional in python bindings
*/
virtual bool hasHistogram( int bandNo,
int binCount,
double minimum,
Expand Down
14 changes: 10 additions & 4 deletions src/core/raster/qgsrasterinterface.cpp
Expand Up @@ -118,7 +118,7 @@ bool QgsRasterInterface::hasStatistics( int bandNo,
QgsRasterBandStats QgsRasterInterface::bandStatistics( int bandNo,
int stats,
const QgsRectangle &extent,
int sampleSize )
int sampleSize, QgsRasterBlockFeedback *feedback )
{
QgsDebugMsgLevel( QString( "theBandNo = %1 stats = %2 sampleSize = %3" ).arg( bandNo ).arg( stats ).arg( sampleSize ), 4 );

Expand Down Expand Up @@ -169,6 +169,9 @@ QgsRasterBandStats QgsRasterInterface::bandStatistics( int bandNo,
{
for ( int myXBlock = 0; myXBlock < myNXBlocks; myXBlock++ )
{
if ( feedback && feedback->isCanceled() )
return myRasterBandStats;

QgsDebugMsgLevel( QString( "myYBlock = %1 myXBlock = %2" ).arg( myYBlock ).arg( myXBlock ), 4 );
int myBlockWidth = qMin( myXBlockSize, myWidth - myXBlock * myXBlockSize );
int myBlockHeight = qMin( myYBlockSize, myHeight - myYBlock * myYBlockSize );
Expand All @@ -180,7 +183,7 @@ QgsRasterBandStats QgsRasterInterface::bandStatistics( int bandNo,

QgsRectangle myPartExtent( xmin, ymin, xmax, ymax );

QgsRasterBlock *blk = block( bandNo, myPartExtent, myBlockWidth, myBlockHeight );
QgsRasterBlock *blk = block( bandNo, myPartExtent, myBlockWidth, myBlockHeight, feedback );

// Collect the histogram counts.
for ( qgssize i = 0; i < ( static_cast< qgssize >( myBlockHeight ) ) * myBlockWidth; i++ )
Expand Down Expand Up @@ -394,7 +397,7 @@ QgsRasterHistogram QgsRasterInterface::histogram( int bandNo,
double minimum, double maximum,
const QgsRectangle &extent,
int sampleSize,
bool includeOutOfRange )
bool includeOutOfRange, QgsRasterBlockFeedback *feedback )
{
QgsDebugMsgLevel( QString( "theBandNo = %1 binCount = %2 minimum = %3 maximum = %4 sampleSize = %5" ).arg( bandNo ).arg( binCount ).arg( minimum ).arg( maximum ).arg( sampleSize ), 4 );

Expand Down Expand Up @@ -452,6 +455,9 @@ QgsRasterHistogram QgsRasterInterface::histogram( int bandNo,
{
for ( int myXBlock = 0; myXBlock < myNXBlocks; myXBlock++ )
{
if ( feedback && feedback->isCanceled() )
return myHistogram;

int myBlockWidth = qMin( myXBlockSize, myWidth - myXBlock * myXBlockSize );
int myBlockHeight = qMin( myYBlockSize, myHeight - myYBlock * myYBlockSize );

Expand All @@ -462,7 +468,7 @@ QgsRasterHistogram QgsRasterInterface::histogram( int bandNo,

QgsRectangle myPartExtent( xmin, ymin, xmax, ymax );

QgsRasterBlock *blk = block( bandNo, myPartExtent, myBlockWidth, myBlockHeight );
QgsRasterBlock *blk = block( bandNo, myPartExtent, myBlockWidth, myBlockHeight, feedback );

// Collect the histogram counts.
for ( qgssize i = 0; i < ( static_cast< qgssize >( myBlockHeight ) ) * myBlockWidth; i++ )
Expand Down
6 changes: 4 additions & 2 deletions src/core/raster/qgsrasterinterface.h
Expand Up @@ -192,12 +192,13 @@ class CORE_EXPORT QgsRasterInterface
* @param stats Requested statistics
* @param extent Extent used to calc statistics, if empty, whole raster extent is used.
* @param sampleSize Approximate number of cells in sample. If 0, all cells (whole raster will be used). If raster does not have exact size (WCS without exact size for example), provider decides size of sample.
* @param feedback optional feedback object
* @return Band statistics.
*/
virtual QgsRasterBandStats bandStatistics( int bandNo,
int stats = QgsRasterBandStats::All,
const QgsRectangle &extent = QgsRectangle(),
int sampleSize = 0 );
int sampleSize = 0, QgsRasterBlockFeedback *feedback = nullptr );

/** \brief Returns true if histogram is available (cached, already calculated). * The parameters are the same as in bandStatistics()
* @return true if statistics are available (ready to use)
Expand All @@ -215,6 +216,7 @@ class CORE_EXPORT QgsRasterInterface
* @param extent Extent used to calc histogram, if empty, whole raster extent is used.
* @param sampleSize Approximate number of cells in sample. If 0, all cells (whole raster will be used). If raster does not have exact size (WCS without exact size for example), provider decides size of sample.
* @param includeOutOfRange include out of range values
* @param feedback optional feedback object
* @return Vector of non NULL cell counts for each bin.
* @note binCount, minimum and maximum not optional in Python bindings
*/
Expand All @@ -224,7 +226,7 @@ class CORE_EXPORT QgsRasterInterface
double maximum = std::numeric_limits<double>::quiet_NaN(),
const QgsRectangle &extent = QgsRectangle(),
int sampleSize = 0,
bool includeOutOfRange = false );
bool includeOutOfRange = false, QgsRasterBlockFeedback *feedback = nullptr );

/** \brief Returns true if histogram is available (cached, already calculated), the parameters are the same as in histogram()
* @note binCount, minimum and maximum not optional in Python bindings
Expand Down
24 changes: 16 additions & 8 deletions src/providers/gdal/qgsgdalprovider.cpp
Expand Up @@ -62,6 +62,7 @@ struct QgsGdalProgress
{
int type;
QgsGdalProvider *provider = nullptr;
QgsRasterBlockFeedback *feedback = nullptr;
};
//
// global callback function
Expand All @@ -87,10 +88,12 @@ int CPL_STDCALL progressCallback( double dfComplete,
{
mypProvider->emitProgress( prog->type, dfComplete * 100, QString( pszMessage ) );
mypProvider->emitProgressUpdate( dfComplete * 100 );
if ( prog->feedback )
prog->feedback->setProgress( dfComplete * 100 );
}
sDfLastComplete = dfComplete;

return true;
return prog->feedback ? !prog->feedback->isCanceled() : true;
}

QgsGdalProvider::QgsGdalProvider( const QString &uri, QgsError error )
Expand Down Expand Up @@ -1295,7 +1298,7 @@ QgsRasterHistogram QgsGdalProvider::histogram( int bandNo,
double minimum, double maximum,
const QgsRectangle &boundingBox,
int sampleSize,
bool includeOutOfRange )
bool includeOutOfRange, QgsRasterBlockFeedback *feedback )
{
QgsDebugMsg( QString( "theBandNo = %1 binCount = %2 minimum = %3 maximum = %4 sampleSize = %5" ).arg( bandNo ).arg( binCount ).arg( minimum ).arg( maximum ).arg( sampleSize ) );

Expand All @@ -1316,13 +1319,13 @@ QgsRasterHistogram QgsGdalProvider::histogram( int bandNo,
!userNoDataValues( bandNo ).isEmpty() )
{
QgsDebugMsg( "Custom no data values, using generic histogram." );
return QgsRasterDataProvider::histogram( bandNo, binCount, minimum, maximum, boundingBox, sampleSize, includeOutOfRange );
return QgsRasterDataProvider::histogram( bandNo, binCount, minimum, maximum, boundingBox, sampleSize, includeOutOfRange, feedback );
}

if ( myHistogram.extent != extent() )
{
QgsDebugMsg( "Not full extent, using generic histogram." );
return QgsRasterDataProvider::histogram( bandNo, binCount, minimum, maximum, boundingBox, sampleSize, includeOutOfRange );
return QgsRasterDataProvider::histogram( bandNo, binCount, minimum, maximum, boundingBox, sampleSize, includeOutOfRange, feedback );
}

QgsDebugMsg( "Computing GDAL histogram" );
Expand All @@ -1345,6 +1348,7 @@ QgsRasterHistogram QgsGdalProvider::histogram( int bandNo,
QgsGdalProgress myProg;
myProg.type = QgsRaster::ProgressHistogram;
myProg.provider = this;
myProg.feedback = feedback;

#if 0 // this is the old method

Expand Down Expand Up @@ -1409,7 +1413,7 @@ QgsRasterHistogram QgsGdalProvider::histogram( int bandNo,
includeOutOfRange, bApproxOK, progressCallback,
&myProg ); //this is the arg for our custom gdal progress callback

if ( myError != CE_None )
if ( myError != CE_None || ( feedback && feedback->isCanceled() ) )
{
QgsDebugMsg( "Cannot get histogram" );
delete [] myHistogramArray;
Expand Down Expand Up @@ -2264,7 +2268,7 @@ bool QgsGdalProvider::hasStatistics( int bandNo,
return false;
}

QgsRasterBandStats QgsGdalProvider::bandStatistics( int bandNo, int stats, const QgsRectangle &boundingBox, int sampleSize )
QgsRasterBandStats QgsGdalProvider::bandStatistics( int bandNo, int stats, const QgsRectangle &boundingBox, int sampleSize, QgsRasterBlockFeedback *feedback )
{
QgsDebugMsg( QString( "theBandNo = %1 sampleSize = %2" ).arg( bandNo ).arg( sampleSize ) );

Expand Down Expand Up @@ -2293,7 +2297,7 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int bandNo, int stats, const
!userNoDataValues( bandNo ).isEmpty() )
{
QgsDebugMsg( "Custom no data values, using generic statistics." );
return QgsRasterDataProvider::bandStatistics( bandNo, stats, boundingBox, sampleSize );
return QgsRasterDataProvider::bandStatistics( bandNo, stats, boundingBox, sampleSize, feedback );
}

int supportedStats = QgsRasterBandStats::Min | QgsRasterBandStats::Max
Expand All @@ -2306,7 +2310,7 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int bandNo, int stats, const
( stats & ( ~supportedStats ) ) )
{
QgsDebugMsg( "Statistics not supported by provider, using generic statistics." );
return QgsRasterDataProvider::bandStatistics( bandNo, stats, boundingBox, sampleSize );
return QgsRasterDataProvider::bandStatistics( bandNo, stats, boundingBox, sampleSize, feedback );
}

QgsDebugMsg( "Using GDAL statistics." );
Expand Down Expand Up @@ -2334,6 +2338,7 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int bandNo, int stats, const
QgsGdalProgress myProg;
myProg.type = QgsRaster::ProgressHistogram;
myProg.provider = this;
myProg.feedback = feedback;

// try to fetch the cached stats (bForce=FALSE)
// GDALGetRasterStatistics() do not work correctly with bApproxOK=false and bForce=false/true
Expand All @@ -2358,6 +2363,9 @@ QgsRasterBandStats QgsGdalProvider::bandStatistics( int bandNo, int stats, const
QgsDebugMsg( "Using GDAL cached statistics" );
}

if ( feedback && feedback->isCanceled() )
return myRasterBandStats;

// if stats are found populate the QgsRasterBandStats object
if ( CE_None == myerval )
{
Expand Down
4 changes: 2 additions & 2 deletions src/providers/gdal/qgsgdalprovider.h
Expand Up @@ -115,7 +115,7 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
QgsRasterBandStats bandStatistics( int bandNo,
int stats = QgsRasterBandStats::All,
const QgsRectangle &boundingBox = QgsRectangle(),
int sampleSize = 0 ) override;
int sampleSize = 0, QgsRasterBlockFeedback *feedback = nullptr ) override;

bool hasHistogram( int bandNo,
int binCount = 0,
Expand All @@ -131,7 +131,7 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase
double maximum = std::numeric_limits<double>::quiet_NaN(),
const QgsRectangle &boundingBox = QgsRectangle(),
int sampleSize = 0,
bool includeOutOfRange = false ) override;
bool includeOutOfRange = false, QgsRasterBlockFeedback *feedback = nullptr ) override;

QString buildPyramids( const QList<QgsRasterPyramid> &rasterPyramidList,
const QString &resamplingMethod = "NEAREST",
Expand Down
2 changes: 1 addition & 1 deletion src/providers/grass/qgsgrassrasterprovider.cpp
Expand Up @@ -284,7 +284,7 @@ void QgsGrassRasterProvider::readBlock( int bandNo, QgsRectangle const &viewExt
memcpy( block, data.data(), size );
}

QgsRasterBandStats QgsGrassRasterProvider::bandStatistics( int bandNo, int stats, const QgsRectangle &boundingBox, int sampleSize )
QgsRasterBandStats QgsGrassRasterProvider::bandStatistics( int bandNo, int stats, const QgsRectangle &boundingBox, int sampleSize, QgsRasterBlockFeedback * )
{
QgsDebugMsg( QString( "theBandNo = %1 sampleSize = %2" ).arg( bandNo ).arg( sampleSize ) );
QgsRasterBandStats myRasterBandStats;
Expand Down
2 changes: 1 addition & 1 deletion src/providers/grass/qgsgrassrasterprovider.h
Expand Up @@ -193,7 +193,7 @@ class GRASS_LIB_EXPORT QgsGrassRasterProvider : public QgsRasterDataProvider
QgsRasterBandStats bandStatistics( int bandNo,
int stats = QgsRasterBandStats::All,
const QgsRectangle &boundingBox = QgsRectangle(),
int sampleSize = 0 ) override;
int sampleSize = 0, QgsRasterBlockFeedback *feedback = nullptr ) override;

QList<QgsColorRampShader::ColorRampItem> colorTable( int bandNo )const override;

Expand Down

0 comments on commit e400ab6

Please sign in to comment.