Skip to content
Permalink
Browse files
Calculate unique raster values in background thread
To keep UI nice and responsive
  • Loading branch information
nyalldawson committed Apr 3, 2017
1 parent e400ab6 commit 1cbe971372c880f23291c02e695dc74f5b8d41ee
@@ -401,19 +401,25 @@ QString QgsPalettedRasterRenderer::classDataToString( const QgsPalettedRasterRen
return out.join( '\n' );
}

QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromRaster( QgsRasterInterface *raster, int bandNumber, QgsColorRamp *ramp )
QgsPalettedRasterRenderer::ClassData QgsPalettedRasterRenderer::classDataFromRaster( QgsRasterInterface *raster, int bandNumber, QgsColorRamp *ramp, QgsRasterBlockFeedback *feedback )
{
if ( !raster )
return ClassData();

// get min and max value from raster
QgsRasterBandStats stats = raster->bandStatistics( bandNumber, QgsRasterBandStats::Min | QgsRasterBandStats::Max );
QgsRasterBandStats stats = raster->bandStatistics( bandNumber, QgsRasterBandStats::Min | QgsRasterBandStats::Max, QgsRectangle(), 0, feedback );
if ( feedback && feedback->isCanceled() )
return ClassData();

double min = stats.minimumValue;
double max = stats.maximumValue;
// need count of every individual value
int bins = ceil( max - min ) + 1;

QgsRasterHistogram histogram = raster->histogram( bandNumber, bins, min, max );
QgsRasterHistogram histogram = raster->histogram( bandNumber, bins, min, max, QgsRectangle(), 0, false, feedback );
if ( feedback && feedback->isCanceled() )
return ClassData();

double interval = ( histogram.maximum - histogram.minimum + 1 ) / histogram.binCount;

ClassData data;
@@ -140,7 +140,8 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
* color \a ramp can be specified to automatically assign colors from the ramp.
* @note added in QGIS 3.0
*/
static QgsPalettedRasterRenderer::ClassData classDataFromRaster( QgsRasterInterface *raster, int bandNumber, QgsColorRamp *ramp = nullptr );
static QgsPalettedRasterRenderer::ClassData classDataFromRaster( QgsRasterInterface *raster, int bandNumber, QgsColorRamp *ramp = nullptr,
QgsRasterBlockFeedback *feedback = nullptr );

private:

@@ -21,6 +21,7 @@
#include "qgsrasterlayer.h"
#include "qgscolordialog.h"
#include "qgssettings.h"
#include "qgsproject.h"

#include <QColorDialog>
#include <QInputDialog>
@@ -36,6 +37,9 @@ QgsPalettedRendererWidget::QgsPalettedRendererWidget( QgsRasterLayer *layer, con
{
setupUi( this );

mCalculatingProgressBar->hide();
mCancelButton->hide();

contextMenu = new QMenu( tr( "Options" ), this );
contextMenu->addAction( tr( "Change color" ), this, SLOT( changeColor() ) );
contextMenu->addAction( tr( "Change transparency" ), this, SLOT( changeTransparency() ) );
@@ -105,6 +109,17 @@ QgsPalettedRendererWidget::QgsPalettedRendererWidget( QgsRasterLayer *layer, con
{
mButtonLoadFromLayer->setEnabled( false );
}

connect( QgsProject::instance(), static_cast < void ( QgsProject::* )( QgsMapLayer * ) >( &QgsProject::layerWillBeRemoved ), this, &QgsPalettedRendererWidget::layerWillBeRemoved );
}

QgsPalettedRendererWidget::~QgsPalettedRendererWidget()
{
if ( mGatherer )
{
mGatherer->stop();
mGatherer->wait(); // mGatherer is deleted when wait completes
}
}

QgsRasterRenderer *QgsPalettedRendererWidget::renderer()
@@ -383,10 +398,24 @@ void QgsPalettedRendererWidget::classify()
return;
}

std::unique_ptr< QgsColorRamp > ramp( btnColorRamp->colorRamp() );
QgsPalettedRasterRenderer::ClassData data = QgsPalettedRasterRenderer::classDataFromRaster( provider, mBandComboBox->currentData().toInt(), ramp.get() );
mModel->setClassData( data );
emit widgetChanged();
if ( mGatherer )
{
mGatherer->stop();
return;
}

mGatherer = new QgsPalettedRendererClassGatherer( mRasterLayer, mBandComboBox->currentData().toInt(), btnColorRamp->colorRamp() );

connect( mGatherer, &QgsPalettedRendererClassGatherer::progressChanged, mCalculatingProgressBar, &QProgressBar::setValue );
mCalculatingProgressBar->show();
mCancelButton->show();
connect( mCancelButton, &QPushButton::clicked, mGatherer, &QgsPalettedRendererClassGatherer::stop );

connect( mGatherer, &QgsPalettedRendererClassGatherer::collectedClasses, this, &QgsPalettedRendererWidget::gatheredClasses );
connect( mGatherer, &QgsPalettedRendererClassGatherer::finished, this, &QgsPalettedRendererWidget::gathererThreadFinished );
mClassifyButton->setText( tr( "Calculating..." ) );
mClassifyButton->setEnabled( false );
mGatherer->start();
}
}

@@ -402,6 +431,34 @@ void QgsPalettedRendererWidget::loadFromLayer()
}
}

void QgsPalettedRendererWidget::gatheredClasses()
{
if ( mGatherer && mGatherer->wasCanceled() )
return;

mModel->setClassData( mGatherer->classes() );
emit widgetChanged();
}

void QgsPalettedRendererWidget::gathererThreadFinished()
{
mGatherer->deleteLater();
mGatherer = nullptr;
mClassifyButton->setText( tr( "Add Unique Values" ) );
mClassifyButton->setEnabled( true );
mCalculatingProgressBar->hide();
mCancelButton->hide();
}

void QgsPalettedRendererWidget::layerWillBeRemoved( QgsMapLayer *layer )
{
if ( mGatherer && mRasterLayer == layer )
{
mGatherer->stop();
mGatherer->wait();
}
}

//
// QgsPalettedRendererModel
//
@@ -21,12 +21,90 @@
#include "qgsrasterrendererwidget.h"
#include "qgspalettedrasterrenderer.h"
#include "qgscolorschemelist.h"
#include "qgsrasterlayer.h"
#include "qgsrasterdataprovider.h"
#include "ui_qgspalettedrendererwidgetbase.h"
#include "qgis_gui.h"

class QgsRasterLayer;

/// @cond PRIVATE

/** \class QgsPalettedRendererClassGatherer
* Calculated raster stats for paletted renderer in a thread
*/
class QgsPalettedRendererClassGatherer: public QThread
{
Q_OBJECT

public:
QgsPalettedRendererClassGatherer( QgsRasterLayer *layer, int bandNumber, QgsColorRamp *ramp = nullptr )
: mLayer( layer )
, mBandNumber( bandNumber )
, mRamp( ramp )
, mFeedback( nullptr )
, mWasCanceled( false )
{}

virtual void run() override
{
mWasCanceled = false;

// allow responsive cancelation
mFeedback = new QgsRasterBlockFeedback();
connect( mFeedback, &QgsRasterBlockFeedback::progressChanged, this, &QgsPalettedRendererClassGatherer::progressChanged );

mClasses = QgsPalettedRasterRenderer::classDataFromRaster( mLayer->dataProvider(), mBandNumber, mRamp.get(), mFeedback );
// be overly cautious - it's *possible* stop() might be called between deleting mFeedback and nulling it
mFeedbackMutex.lock();
delete mFeedback;
mFeedback = nullptr;
mFeedbackMutex.unlock();

emit collectedClasses();
}

//! Informs the gatherer to immediately stop collecting values
void stop()
{
// be cautious, in case gatherer stops naturally just as we are canceling it and mFeedback gets deleted
mFeedbackMutex.lock();
if ( mFeedback )
mFeedback->cancel();
mFeedbackMutex.unlock();

mWasCanceled = true;
}

//! Returns true if collection was canceled before completion
bool wasCanceled() const { return mWasCanceled; }

QgsPalettedRasterRenderer::ClassData classes() const { return mClasses; }

signals:

/** Emitted when classes have been collected
*/
void collectedClasses();

signals:
//! Internal routines can connect to this signal if they use event loop
void canceled();

void progressChanged( double progress );

private:

QgsRasterLayer *mLayer = nullptr;
int mBandNumber;
std::unique_ptr< QgsColorRamp > mRamp;
QString mSubstring;
QgsPalettedRasterRenderer::ClassData mClasses;
QgsRasterBlockFeedback *mFeedback = nullptr;
QMutex mFeedbackMutex;
bool mWasCanceled;
};

class QgsPalettedRendererModel : public QAbstractItemModel
{
Q_OBJECT
@@ -85,6 +163,7 @@ class GUI_EXPORT QgsPalettedRendererWidget: public QgsRasterRendererWidget, priv
public:

QgsPalettedRendererWidget( QgsRasterLayer *layer, const QgsRectangle &extent = QgsRectangle() );
~QgsPalettedRendererWidget();
static QgsRasterRendererWidget *create( QgsRasterLayer *layer, const QgsRectangle &extent ) { return new QgsPalettedRendererWidget( layer, extent ); }

QgsRasterRenderer *renderer() override;
@@ -97,6 +176,9 @@ class GUI_EXPORT QgsPalettedRendererWidget: public QgsRasterRendererWidget, priv
QgsPalettedRendererModel *mModel = nullptr;
QgsColorSwatchDelegate *mSwatchDelegate = nullptr;

//! Background class gatherer thread
QgsPalettedRendererClassGatherer *mGatherer = nullptr;

void setSelectionColor( const QItemSelection &selection, const QColor &color );

private slots:
@@ -112,6 +194,10 @@ class GUI_EXPORT QgsPalettedRendererWidget: public QgsRasterRendererWidget, priv
void classify();
void loadFromLayer();

void gatheredClasses();
void gathererThreadFinished();
void layerWillBeRemoved( QgsMapLayer *layer );

};

#endif // QGSPALETTEDRENDERERWIDGET_H
@@ -93,7 +93,10 @@ int CPL_STDCALL progressCallback( double dfComplete,
}
sDfLastComplete = dfComplete;

return prog->feedback ? !prog->feedback->isCanceled() : true;
if ( prog->feedback && prog->feedback->isCanceled() )
return false;

return true;
}

QgsGdalProvider::QgsGdalProvider( const QString &uri, QgsError error )
Loading

0 comments on commit 1cbe971

Please sign in to comment.