Skip to content
Permalink
Browse files

[FEATURE] Ability to cancel rendering of rasters + WMS/WCS support

The improvement allows immediate cancellation of raster rendering
in progress. Until now, even when map rendering got cancelled
(e.g. by zooming of panning canvas), the GUI got blocked while waiting
for raster layers to finish their rendering (only vector layers have
had support for cancellation). This should allow for much smoother
user experience while browsing maps including rasters.

The cancellation is supported currently by WMS/WMTS and WCS providers.
GDAL provider may also get support thanks to improvements in GDAL 2.

Funded by Land Information New Zealand.
  • Loading branch information
wonder-sk committed Jul 22, 2016
1 parent 0974f0b commit b6b5afd8859e28f3be76c81129a12015c1e17825
Showing with 328 additions and 132 deletions.
  1. +1 −0 python/core/core.sip
  2. +4 −0 python/core/qgsmaplayerrenderer.sip
  3. +1 −1 python/core/raster/qgsbrightnesscontrastfilter.sip
  4. +1 −1 python/core/raster/qgshillshaderenderer.sip
  5. +1 −1 python/core/raster/qgshuesaturationfilter.sip
  6. +1 −1 python/core/raster/qgsmultibandcolorrenderer.sip
  7. +1 −1 python/core/raster/qgspalettedrasterrenderer.sip
  8. +2 −2 python/core/raster/qgsrasterdataprovider.sip
  9. +2 −2 python/core/raster/qgsrasterdrawer.sip
  10. +12 −1 python/core/raster/qgsrasterinterface.sip
  11. +2 −1 python/core/raster/qgsrasteriterator.sip
  12. +1 −1 python/core/raster/qgsrasternuller.sip
  13. +1 −1 python/core/raster/qgsrasterprojector.sip
  14. +1 −1 python/core/raster/qgsrasterrenderer.sip
  15. +1 −1 python/core/raster/qgsrasterresamplefilter.sip
  16. +1 −1 python/core/raster/qgssinglebandcolordatarenderer.sip
  17. +1 −1 python/core/raster/qgssinglebandgrayrenderer.sip
  18. +1 −1 python/core/raster/qgssinglebandpseudocolorrenderer.sip
  19. +1 −0 src/core/CMakeLists.txt
  20. +73 −0 src/core/qgsfeedback.h
  21. +6 −0 src/core/qgsmaplayerrenderer.h
  22. +3 −0 src/core/qgsmaprenderercustompainterjob.cpp
  23. +3 −0 src/core/qgsmaprendererparalleljob.cpp
  24. +2 −2 src/core/raster/qgsbrightnesscontrastfilter.cpp
  25. +1 −1 src/core/raster/qgsbrightnesscontrastfilter.h
  26. +3 −3 src/core/raster/qgshillshaderenderer.cpp
  27. +1 −1 src/core/raster/qgshillshaderenderer.h
  28. +2 −2 src/core/raster/qgshuesaturationfilter.cpp
  29. +1 −1 src/core/raster/qgshuesaturationfilter.h
  30. +2 −2 src/core/raster/qgsmultibandcolorrenderer.cpp
  31. +1 −1 src/core/raster/qgsmultibandcolorrenderer.h
  32. +3 −3 src/core/raster/qgspalettedrasterrenderer.cpp
  33. +1 −1 src/core/raster/qgspalettedrasterrenderer.h
  34. +3 −3 src/core/raster/qgsrasterdataprovider.cpp
  35. +3 −3 src/core/raster/qgsrasterdataprovider.h
  36. +7 −3 src/core/raster/qgsrasterdrawer.cpp
  37. +3 −2 src/core/raster/qgsrasterdrawer.h
  38. +10 −1 src/core/raster/qgsrasterinterface.h
  39. +4 −2 src/core/raster/qgsrasteriterator.cpp
  40. +4 −1 src/core/raster/qgsrasteriterator.h
  41. +9 −2 src/core/raster/qgsrasterlayerrenderer.cpp
  42. +5 −0 src/core/raster/qgsrasterlayerrenderer.h
  43. +2 −2 src/core/raster/qgsrasternuller.cpp
  44. +1 −1 src/core/raster/qgsrasternuller.h
  45. +3 −3 src/core/raster/qgsrasterprojector.cpp
  46. +1 −1 src/core/raster/qgsrasterprojector.h
  47. +1 −1 src/core/raster/qgsrasterrenderer.h
  48. +3 −3 src/core/raster/qgsrasterresamplefilter.cpp
  49. +1 −1 src/core/raster/qgsrasterresamplefilter.h
  50. +2 −2 src/core/raster/qgssinglebandcolordatarenderer.cpp
  51. +1 −1 src/core/raster/qgssinglebandcolordatarenderer.h
  52. +3 −3 src/core/raster/qgssinglebandgrayrenderer.cpp
  53. +1 −1 src/core/raster/qgssinglebandgrayrenderer.h
  54. +3 −3 src/core/raster/qgssinglebandpseudocolorrenderer.cpp
  55. +1 −1 src/core/raster/qgssinglebandpseudocolorrenderer.h
  56. +4 −3 src/providers/gdal/qgsgdalprovider.cpp
  57. +2 −2 src/providers/gdal/qgsgdalprovider.h
  58. +2 −1 src/providers/grass/qgsgrassrasterprovider.cpp
  59. +1 −1 src/providers/grass/qgsgrassrasterprovider.h
  60. +42 −26 src/providers/wcs/qgswcsprovider.cpp
  61. +4 −3 src/providers/wcs/qgswcsprovider.h
  62. +57 −18 src/providers/wms/qgswmsprovider.cpp
  63. +7 −3 src/providers/wms/qgswmsprovider.h
@@ -53,6 +53,7 @@
%Include qgsfeaturefilterprovider.sip
%Include qgsfeatureiterator.sip
%Include qgsfeaturerequest.sip
%Include qgsfeedback.sip
%Include qgsfield.sip
%Include qgsgeometrysimplifier.sip
%Include qgsgeometryvalidator.sip
@@ -12,6 +12,10 @@ class QgsMapLayerRenderer
//! Do the rendering (based on data stored in the class)
virtual bool render() = 0;

//! Access to feedback object of the layer renderer (may be null)
//! @note added in QGIS 3.0
virtual QgsFeedback* feedback() const;

//! Return list of errors (problems) that happened during the rendering
QStringList errors() const;

@@ -15,7 +15,7 @@ class QgsBrightnessContrastFilter : QgsRasterInterface

bool setInput( QgsRasterInterface* input );

QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height );
QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

void setBrightness( int brightness );
int brightness() const;
@@ -31,7 +31,7 @@ class QgsHillshadeRenderer : QgsRasterRenderer
*/
static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input ) /Factory/;

QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height ) /Factory/;
QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

void writeXml( QDomDocument& doc, QDomElement& parentElem ) const;

@@ -25,7 +25,7 @@ class QgsHueSaturationFilter : QgsRasterInterface

bool setInput( QgsRasterInterface* input );

QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height );
QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

void setSaturation( int saturation );
int saturation() const;
@@ -12,7 +12,7 @@ class QgsMultiBandColorRenderer: QgsRasterRenderer

static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterDataProvider* provider ) /Factory/;

QgsRasterBlock* block( int bandNo, const QgsRectangle & extent, int width, int height ) /Factory/;
QgsRasterBlock* block( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

int redBand() const;
void setRedBand( int band );
@@ -10,7 +10,7 @@ class QgsPalettedRasterRenderer : QgsRasterRenderer
virtual QgsPalettedRasterRenderer * clone() const /Factory/;
static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input ) /Factory/;

QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height ) /Factory/;
QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

/** Returns number of colors*/
int nColors() const;
@@ -102,7 +102,7 @@ class QgsRasterDataProvider : QgsDataProvider, QgsRasterInterface
virtual int ySize() const;

/** Read block of data using given extent and size. */
virtual QgsRasterBlock *block( int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight ) / Factory /;
virtual QgsRasterBlock *block( int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight, QgsRasterBlockFeedback* feedback = nullptr ) / Factory /;

/** Return true if source band has no data value */
virtual bool sourceHasNoDataValue( int bandNo ) const;
@@ -306,7 +306,7 @@ class QgsRasterDataProvider : QgsDataProvider, QgsRasterInterface
/** Read block of data using give extent and size
* @note not available in python bindings
*/
//virtual void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data );
//virtual void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data, QgsRasterBlockFeedback* feedback = nullptr );

/** Returns true if user no data contains value */
bool userNoDataValuesContains( int bandNo, double value ) const;
@@ -13,9 +13,9 @@ class QgsRasterDrawer
* @param p destination QPainter
* @param viewPort viewport to render
* @param theQgsMapToPixel map to pixel convertor
* @param ctx render context
* @param feedback optional raster feedback object for cancellation/preview. Added in QGIS 3.0.
*/
void draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsMapToPixel* theQgsMapToPixel, const QgsRenderContext *ctx = nullptr );
void draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsMapToPixel* theQgsMapToPixel, QgsRasterBlockFeedback* feedback = nullptr );

protected:
/** Draws raster part
@@ -1,4 +1,14 @@

/** Feedback object tailored for raster block reading. */
class QgsRasterBlockFeedback : QgsFeedback
{
%TypeHeaderCode
#include <qgsrasterinterface.h>
%End
// TODO: extend with preview functionality??
};


/** Base class for processing modules.
*
*/
@@ -129,8 +139,9 @@ class QgsRasterInterface
* @param extent extent of block
* @param width pixel width of block
* @param height pixel height of block
* @param feedback optional raster feedback object for cancellation/preview. Added in QGIS 3.0.
*/
virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height ) = 0 /Factory/;
virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) = 0 /Factory/;

/** Set input.
* Returns true if set correctly, false if cannot use that input */
@@ -12,8 +12,9 @@ class QgsRasterIterator
@param nCols number of columns
@param nRows number of rows
@param extent area to read
@param feedback optional raster feedback object for cancellation/preview. Added in QGIS 3.0.
*/
void startRasterRead( int bandNumber, int nCols, int nRows, const QgsRectangle& extent );
void startRasterRead( int bandNumber, int nCols, int nRows, const QgsRectangle& extent, QgsRasterBlockFeedback* feedback = nullptr );

/** Fetches next part of raster data, caller takes ownership of the block and
caller should delete the block.
@@ -19,7 +19,7 @@ class QgsRasterNuller : QgsRasterInterface

Qgis::DataType dataType( int bandNo ) const;

QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height ) / Factory /;
QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) / Factory /;

void setNoData( int bandNo, const QgsRasterRangeList& noData );

@@ -74,7 +74,7 @@ class QgsRasterProjector : QgsRasterInterface
// Translated precision mode, for use in ComboBox etc.
static QString precisionLabel( Precision precision );

QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height ) / Factory /;
QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) / Factory /;

/** Calculate destination extent and size from source extent and size */
bool destExtentSize( const QgsRectangle& theSrcExtent, int theSrcXSize, int theSrcYSize,
@@ -38,7 +38,7 @@ class QgsRasterRenderer : QgsRasterInterface

virtual bool setInput( QgsRasterInterface* input );

virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height ) = 0 / Factory /;
virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) = 0 / Factory /;

bool usesTransparency() const;

@@ -19,7 +19,7 @@ class QgsRasterResampleFilter : QgsRasterInterface

bool setInput( QgsRasterInterface* input );

QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height ) /Factory/;
QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

/** Set resampler for zoomed in scales. Takes ownership of the object*/
void setZoomedInResampler( QgsRasterResampler* r /Transfer/ );
@@ -12,7 +12,7 @@ class QgsSingleBandColorDataRenderer: QgsRasterRenderer

bool setInput( QgsRasterInterface* input );

QgsRasterBlock* block( int bandNo, const QgsRectangle & extent, int width, int height ) / Factory /;
QgsRasterBlock* block( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) / Factory /;

void writeXml( QDomDocument& doc, QDomElement& parentElem ) const;

@@ -16,7 +16,7 @@ class QgsSingleBandGrayRenderer: QgsRasterRenderer

static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterDataProvider* provider ) /Factory/;

QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height ) / Factory /;
QgsRasterBlock *block( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) / Factory /;

int grayBand() const;
void setGrayBand( int band );
@@ -11,7 +11,7 @@ class QgsSingleBandPseudoColorRenderer: QgsRasterRenderer

static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterDataProvider* provider ) /Factory/;

QgsRasterBlock* block( int bandNo, const QgsRectangle & extent, int width, int height ) / Factory /;
QgsRasterBlock* block( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) / Factory /;

/** Takes ownership of the shader*/
void setShader( QgsRasterShader* shader /Transfer/ );
@@ -457,6 +457,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsdataprovider.h
qgsdbfilterproxymodel.h
qgseditformconfig.h
qgsfeedback.h
qgsgeometryvalidator.h
qgsgml.h
qgsgmlschema.h
@@ -0,0 +1,73 @@
/***************************************************************************
qgsfeedback.h
--------------------------------------
Date : July 2016
Copyright : (C) 2016 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef QGSFEEDBACK_H
#define QGSFEEDBACK_H

#include <QObject>

/** Base class for feedback objects to be used for cancellation of something running in a worker thread.
*
* The class may be used as is or it may be subclassed for extended functionality
* for a particular operation (e.g. report progress or pass some data for preview).
*
* When cancel() is called, the internal code has two options to check for cancellation state:
* - if the worker thread uses an event loop (e.g. for network communication), the code can
* make a queued connection to cancelled() signal and handle the cancellation in its slot.
* - if the worker thread does not use an event loop, it can poll isCancelled() method regularly
* to see if the operation should be cancelled.
*
* The class is meant to be created and destroyed in the main thread.
*
* For map rendering, the object may be created in constructor of a QgsMapLayerRenderer
* subclass and available with QgsMapLayerRenderer::feedback() method. When a map rendering job
* gets cancelled, the cancel() method is called on the feedback object of all layers.
*
* @note added in QGIS 3.0
*/
class CORE_EXPORT QgsFeedback : public QObject
{
Q_OBJECT
public:
//! Construct a feedback object
QgsFeedback( QObject* parent = nullptr )
: QObject( parent )
, mCancelled( false )
{}

virtual ~QgsFeedback() {}

//! Tells the internal routines that the current operation should be cancelled. This should be run by the main thread
void cancel()
{
if ( mCancelled )
return; // only emit the signal once
mCancelled = true;
emit cancelled();
}

//! Tells whether the operation has been cancelled already
bool isCancelled() const { return mCancelled; }

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

private:
//! Whether the operation has been cancelled already. False by default.
bool mCancelled;
};

#endif // QGSFEEDBACK_H
@@ -18,6 +18,8 @@

#include <QStringList>

class QgsFeedback;

/** \ingroup core
* Base class for utility classes that encapsulate information necessary
* for rendering of map layers. The rendering is typically done in a background
@@ -49,6 +51,10 @@ class CORE_EXPORT QgsMapLayerRenderer
//! Do the rendering (based on data stored in the class)
virtual bool render() = 0;

//! Access to feedback object of the layer renderer (may be null)
//! @note added in QGIS 3.0
virtual QgsFeedback* feedback() const { return nullptr; }

//! Return list of errors (problems) that happened during the rendering
QStringList errors() const { return mErrors; }

@@ -15,6 +15,7 @@

#include "qgsmaprenderercustompainterjob.h"

#include "qgsfeedback.h"
#include "qgslabelingenginev2.h"
#include "qgslogger.h"
#include "qgsmaplayerregistry.h"
@@ -124,6 +125,8 @@ void QgsMapRendererCustomPainterJob::cancel()
for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
{
it->context.setRenderingStopped( true );
if ( it->renderer->feedback() )
it->renderer->feedback()->cancel();
}

QTime t;
@@ -15,6 +15,7 @@

#include "qgsmaprendererparalleljob.h"

#include "qgsfeedback.h"
#include "qgslabelingenginev2.h"
#include "qgslogger.h"
#include "qgsmaplayerrenderer.h"
@@ -91,6 +92,8 @@ void QgsMapRendererParallelJob::cancel()
for ( LayerRenderJobs::iterator it = mLayerJobs.begin(); it != mLayerJobs.end(); ++it )
{
it->context.setRenderingStopped( true );
if ( it->renderer->feedback() )
it->renderer->feedback()->cancel();
}

if ( mStatus == RenderingLayers )
@@ -109,7 +109,7 @@ bool QgsBrightnessContrastFilter::setInput( QgsRasterInterface* input )
return true;
}

QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle const & extent, int width, int height )
QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( bandNo );
QgsDebugMsgLevel( QString( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ), 4 );
@@ -122,7 +122,7 @@ QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle c

// At this moment we know that we read rendered image
int bandNumber = 1;
QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, width, height );
QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, width, height, feedback );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
@@ -39,7 +39,7 @@ class CORE_EXPORT QgsBrightnessContrastFilter : public QgsRasterInterface

bool setInput( QgsRasterInterface* input ) override;

QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height ) override;
QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) override;

void setBrightness( int brightness ) { mBrightness = qBound( -255, brightness, 255 ); }
int brightness() const { return mBrightness; }
@@ -83,7 +83,7 @@ void QgsHillshadeRenderer::writeXml( QDomDocument &doc, QDomElement &parentElem
parentElem.appendChild( rasterRendererElem );
}

QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height )
QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( bandNo );
QgsRasterBlock *outputBlock = new QgsRasterBlock();
@@ -93,7 +93,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext
return outputBlock;
}

QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height );
QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height, feedback );

if ( !inputBlock || inputBlock->isEmpty() )
{
@@ -107,7 +107,7 @@ QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &ext
if ( mAlphaBand > 0 && mBand != mAlphaBand )
{

alphaBlock = mInput->block( mAlphaBand, extent, width, height );
alphaBlock = mInput->block( mAlphaBand, extent, width, height, feedback );
if ( !alphaBlock || alphaBlock->isEmpty() )
{
// TODO: better to render without alpha
@@ -55,7 +55,7 @@ class CORE_EXPORT QgsHillshadeRenderer : public QgsRasterRenderer

void writeXml( QDomDocument& doc, QDomElement& parentElem ) const override;

QgsRasterBlock *block( int bandNo, QgsRectangle const & extent, int width, int height ) override;
QgsRasterBlock *block( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) override;

QList<int> usesBands() const override;

0 comments on commit b6b5afd

Please sign in to comment.
You can’t perform that action at this time.