Skip to content

Commit

Permalink
[FEATURE] Preview for WMTS + added XYZ tile layers
Browse files Browse the repository at this point in the history
This is a port of the functionality from master (to be QGIS 3.0) to 2.x that keeps backward API compatibility.

- added support for XYZ tile layers to WMS provider
- rendering of raster layers can be interrupted, support in WMS provider
- temporary preview of tiled layers (WMTS / XYZ) in WMS provider

WMTS improvements funded by Land Information New Zealand.

XYZ tile layer funded by Lutra Consulting.
  • Loading branch information
wonder-sk committed Sep 16, 2016
1 parent 85b6cba commit f5b657d
Show file tree
Hide file tree
Showing 89 changed files with 2,046 additions and 669 deletions.
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 2.18
*/
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 2.18
virtual QgsFeedback* feedback() const;

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

Expand Down
3 changes: 2 additions & 1 deletion python/core/qgsmapsettings.sip
Expand Up @@ -116,7 +116,8 @@ class QgsMapSettings
UseRenderingOptimization, //!< Enable vector simplification and other rendering optimizations
DrawSelection, //!< Whether vector selections should be shown in the rendered map
DrawSymbolBounds, //!< Draw bounds of symbols (for debugging/testing)
RenderMapTile //!< Draw map such that there are no problems between adjacent tiles
RenderMapTile, //!< Draw map such that there are no problems between adjacent tiles
RenderPartialOutput, //!< Whether to make extra effort to update map image with partially rendered layers (better for interactive map canvas). Added in QGIS 2.18
};
typedef QFlags<QgsMapSettings::Flag> Flags;

Expand Down
1 change: 1 addition & 0 deletions python/core/qgsrendercontext.sip
Expand Up @@ -23,6 +23,7 @@ class QgsRenderContext
DrawSymbolBounds, //!< Draw bounds of symbols (for debugging/testing)
RenderMapTile, //!< Draw map such that there are no problems between adjacent tiles
Antialiasing, //!< Use antialiasing while drawing
RenderPartialOutput, //!< Whether to make extra effort to update map image with partially rendered layers (better for interactive map canvas). Added in QGIS 2.18
};
typedef QFlags<QgsRenderContext::Flag> Flags;

Expand Down
3 changes: 2 additions & 1 deletion python/core/raster/qgsbrightnesscontrastfilter.sip
Expand Up @@ -15,7 +15,8 @@ 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 ) /Factory/;
QgsRasterBlock *block2( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

void setBrightness( int brightness );
int brightness() const;
Expand Down
1 change: 1 addition & 0 deletions python/core/raster/qgshillshaderenderer.sip
Expand Up @@ -32,6 +32,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 *block2( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

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

Expand Down
3 changes: 2 additions & 1 deletion python/core/raster/qgshuesaturationfilter.sip
Expand Up @@ -25,7 +25,8 @@ 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 ) /Factory/;
QgsRasterBlock *block2( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

void setSaturation( int saturation );
int saturation() const;
Expand Down
1 change: 1 addition & 0 deletions python/core/raster/qgsmultibandcolorrenderer.sip
Expand Up @@ -13,6 +13,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* block2( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

int redBand() const;
void setRedBand( int band );
Expand Down
1 change: 1 addition & 0 deletions python/core/raster/qgspalettedrasterrenderer.sip
Expand Up @@ -11,6 +11,7 @@ class QgsPalettedRasterRenderer : QgsRasterRenderer
static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input ) /Factory/;

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

/** Returns number of colors*/
int nColors() const;
Expand Down
3 changes: 2 additions & 1 deletion python/core/raster/qgsrasterdataprovider.sip
Expand Up @@ -103,6 +103,7 @@ class QgsRasterDataProvider : QgsDataProvider, QgsRasterInterface

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

/** Return true if source band has no data value */
virtual bool srcHasNoDataValue( int bandNo ) const;
Expand Down Expand Up @@ -306,7 +307,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
3 changes: 2 additions & 1 deletion python/core/raster/qgsrasterdrawer.sip
Expand Up @@ -14,8 +14,9 @@ class QgsRasterDrawer
* @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 2.18.
*/
void draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsMapToPixel* theQgsMapToPixel, const QgsRenderContext *ctx = nullptr );
void draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsMapToPixel* theQgsMapToPixel, const QgsRenderContext *ctx = nullptr, QgsRasterBlockFeedback* feedback = nullptr );

protected:
/** Draws raster part
Expand Down
51 changes: 51 additions & 0 deletions python/core/raster/qgsrasterinterface.sip
@@ -1,4 +1,41 @@

/** \ingroup core
* Feedback object tailored for raster block reading.
*
* @note added in QGIS 2.18
*/
class QgsRasterBlockFeedback : QgsFeedback
{
%TypeHeaderCode
#include <qgsrasterinterface.h>
%End

public:
//! Construct a new raster block feedback object
QgsRasterBlockFeedback( QObject* parent = nullptr );

//! May be emitted by raster data provider to indicate that some partial data are available
//! and a new preview image may be produced
virtual void onNewData();

//! Whether the raster provider should return only data that are already available
//! without waiting for full result. By default this flag is not enabled.
//! @see setPreviewOnly()
bool isPreviewOnly() const;
//! set flag whether the block request is for preview purposes only
//! @see isPreviewOnly()
void setPreviewOnly( bool preview );

//! Whether our painter is drawing to a temporary image used just by this layer
//! @see setRenderPartialOutput()
bool renderPartialOutput() const;
//! Set whether our painter is drawing to a temporary image used just by this layer
//! @see renderPartialOutput()
void setRenderPartialOutput( bool enable );

};


/** Base class for processing modules.
*
*/
Expand Down Expand Up @@ -132,6 +169,19 @@ class QgsRasterInterface
*/
virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height ) = 0 /Factory/;

/** Read block of data using given extent and size.
* Returns pointer to data.
* Caller is responsible to free the memory returned.
* @param bandNo band number
* @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
* @note This is extended version of block() method. Default implementation falls back to calling block().
* @note Added in QGIS 2.18
*/
virtual QgsRasterBlock *block2( int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) /Factory/;

/** Set input.
* Returns true if set correctly, false if cannot use that input */
virtual bool setInput( QgsRasterInterface* input );
Expand Down Expand Up @@ -241,5 +291,6 @@ class QgsRasterInterface
int theStats = QgsRasterBandStats::All,
const QgsRectangle & theExtent = QgsRectangle(),
int theBinCount = 0 );

};

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 2.18
*/
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
1 change: 1 addition & 0 deletions python/core/raster/qgsrasternuller.sip
Expand Up @@ -20,6 +20,7 @@ class QgsRasterNuller : QgsRasterInterface
QGis::DataType dataType( int bandNo ) const;

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

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

Expand Down
28 changes: 15 additions & 13 deletions python/core/raster/qgsrasterprojector.sip
@@ -1,6 +1,10 @@

/** Raster projector */

/** \ingroup core
* \brief QgsRasterProjector implements approximate projection support for
* it calculates grid of points in source CRS for target CRS + extent
* which are used to calculate affine transformation matrices.
* \class QgsRasterProjector
*/
class QgsRasterProjector : QgsRasterInterface
{
%TypeHeaderCode
Expand All @@ -18,11 +22,7 @@ class QgsRasterProjector : QgsRasterInterface
Exact, //!< Exact, precise but slow
};

/** \brief QgsRasterProjector implements approximate projection support for
* it calculates grid of points in source CRS for target CRS + extent
* which are used to calculate affine transformation matrices.
*/

//! @deprecated since 2.18: use default constructor
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
int theSrcDatumTransform,
Expand All @@ -31,20 +31,21 @@ class QgsRasterProjector : QgsRasterInterface
int theDestRows, int theDestCols,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);

) /Deprecated/;
//! @deprecated since 2.18: use default constructor
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
const QgsRectangle& theDestExtent,
int theDestRows, int theDestCols,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);
) /Deprecated/;
//! @deprecated since 2.18: use default constructor
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);
) /Deprecated/;
QgsRasterProjector();

/** \brief The destructor */
Expand All @@ -66,15 +67,16 @@ class QgsRasterProjector : QgsRasterInterface
/** \brief Get destination CRS */
QgsCoordinateReferenceSystem destCrs() const;

/** \brief set maximum source resolution */
void setMaxSrcRes( double theMaxSrcXRes, double theMaxSrcYRes );
/** @deprecated since 2.18, does nothing */
void setMaxSrcRes( double theMaxSrcXRes, double theMaxSrcYRes ) /Deprecated/;

Precision precision() const;
void setPrecision( Precision precision );
// 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 *block2( 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: 0 additions & 2 deletions python/core/raster/qgsrasterrenderer.sip
Expand Up @@ -38,8 +38,6 @@ class QgsRasterRenderer : QgsRasterInterface

virtual bool setInput( QgsRasterInterface* input );

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

bool usesTransparency() const;

void setOpacity( double opacity );
Expand Down
1 change: 1 addition & 0 deletions python/core/raster/qgsrasterresamplefilter.sip
Expand Up @@ -20,6 +20,7 @@ class QgsRasterResampleFilter : QgsRasterInterface
bool setInput( QgsRasterInterface* input );

QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height ) /Factory/;
QgsRasterBlock *block2( 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
1 change: 1 addition & 0 deletions python/core/raster/qgssinglebandcolordatarenderer.sip
Expand Up @@ -13,6 +13,7 @@ class QgsSingleBandColorDataRenderer: QgsRasterRenderer
bool setInput( QgsRasterInterface* input );

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

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

Expand Down
1 change: 1 addition & 0 deletions python/core/raster/qgssinglebandgrayrenderer.sip
Expand Up @@ -17,6 +17,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 *block2( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) / Factory /;

int grayBand() const;
void setGrayBand( int band );
Expand Down
1 change: 1 addition & 0 deletions python/core/raster/qgssinglebandpseudocolorrenderer.sip
Expand Up @@ -12,6 +12,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* block2( 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

0 comments on commit f5b657d

Please sign in to comment.