Navigation Menu

Skip to content

Commit

Permalink
Raster MTR improvements - part one (PR #3334)
Browse files Browse the repository at this point in the history
  • Loading branch information
wonder-sk committed Aug 2, 2016
2 parents f9fabb8 + a89fea1 commit 9db9304
Show file tree
Hide file tree
Showing 72 changed files with 429 additions and 140 deletions.
1 change: 1 addition & 0 deletions doc/api_break.dox
Expand Up @@ -414,6 +414,7 @@ be returned instead of a null pointer if no transformation is required.</li>
<ul>
<li>srcDataType() has been renamed to sourceDataType()</li>
<li>srcInput() has been renamed to sourceInput()</li>
<li>block() has new "feedback" argument.</li>
</ul>

\subsection qgis_api_break_3_0_QgsRelation QgsRelation
Expand Down
1 change: 1 addition & 0 deletions python/core/core.sip
Expand Up @@ -54,6 +54,7 @@
%Include qgsfeaturefilterprovider.sip
%Include qgsfeatureiterator.sip
%Include qgsfeaturerequest.sip
%Include qgsfeedback.sip
%Include qgsfield.sip
%Include qgsgeometrysimplifier.sip
%Include qgsgeometryvalidator.sip
Expand Down
43 changes: 43 additions & 0 deletions python/core/qgsfeedback.sip
@@ -0,0 +1,43 @@

/** \ingroup core
* 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 QgsFeedback : QObject
{
%TypeHeaderCode
#include <qgsfeedback.h>
%End

public:
//! Construct a feedback object
QgsFeedback( QObject* parent /TransferThis/ = nullptr );

virtual ~QgsFeedback();

//! Tells the internal routines that the current operation should be cancelled. This should be run by the main thread
void cancel();

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

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

};
4 changes: 4 additions & 0 deletions python/core/qgsmaplayerrenderer.sip
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgsbrightnesscontrastfilter.sip
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgshillshaderenderer.sip
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgshuesaturationfilter.sip
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgsmultibandcolorrenderer.sip
Expand Up @@ -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 );
Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgspalettedrasterrenderer.sip
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions python/core/raster/qgsrasterdataprovider.sip
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions python/core/raster/qgsrasterdrawer.sip
Expand Up @@ -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
Expand Down
17 changes: 16 additions & 1 deletion python/core/raster/qgsrasterinterface.sip
@@ -1,4 +1,18 @@

/** \ingroup core
* Feedback object tailored for raster block reading.
*
* @note added in QGIS 3.0
*/
class QgsRasterBlockFeedback : QgsFeedback
{
%TypeHeaderCode
#include <qgsrasterinterface.h>
%End
// TODO: extend with preview functionality??
};


/** Base class for processing modules.
*
*/
Expand Down Expand Up @@ -129,8 +143,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 */
Expand Down
3 changes: 2 additions & 1 deletion python/core/raster/qgsrasteriterator.sip
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgsrasternuller.sip
Expand Up @@ -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 );

Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgsrasterprojector.sip
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgsrasterrenderer.sip
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgsrasterresamplefilter.sip
Expand Up @@ -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/ );
Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgssinglebandcolordatarenderer.sip
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgssinglebandgrayrenderer.sip
Expand Up @@ -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 );
Expand Down
2 changes: 1 addition & 1 deletion python/core/raster/qgssinglebandpseudocolorrenderer.sip
Expand Up @@ -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/ );
Expand Down
8 changes: 8 additions & 0 deletions src/app/main.cpp
Expand Up @@ -194,6 +194,14 @@ static void dumpBacktrace( unsigned int depth )
depth = 20;

#if ((defined(linux) || defined(__linux__)) && !defined(ANDROID)) || defined(__FreeBSD__)
// Below there is a bunch of operations that are not safe in multi-threaded
// environment (dup()+close() combo, wait(), juggling with file descriptors).
// Maybe some problems could be resolved with dup2() and waitpid(), but it seems
// that if the operations on descriptors are not serialized, things will get nasty.
// That's why there's this lovely mutex here...
static QMutex mutex;
QMutexLocker locker( &mutex );

int stderr_fd = -1;
if ( access( "/usr/bin/c++filt", X_OK ) < 0 )
{
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Expand Up @@ -456,6 +456,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsdataprovider.h
qgsdbfilterproxymodel.h
qgseditformconfig.h
qgsfeedback.h
qgsgeometryvalidator.h
qgsgml.h
qgsgmlschema.h
Expand Down
73 changes: 73 additions & 0 deletions src/core/qgsfeedback.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>

/** \ingroup core
* 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
6 changes: 6 additions & 0 deletions src/core/qgsmaplayerrenderer.h
Expand Up @@ -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
Expand Down Expand Up @@ -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; }

Expand Down
3 changes: 3 additions & 0 deletions src/core/qgsmaprenderercustompainterjob.cpp
Expand Up @@ -15,6 +15,7 @@

#include "qgsmaprenderercustompainterjob.h"

#include "qgsfeedback.h"
#include "qgslabelingenginev2.h"
#include "qgslogger.h"
#include "qgsmaplayerregistry.h"
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 9db9304

Please sign in to comment.