42 changes: 25 additions & 17 deletions src/app/qgslayerstylingwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#include "qgsrendererv2.h"
#include "qgsrendererv2registry.h"
#include "qgsmaplayerregistry.h"
#include "qgsrasterdataprovider.h"
#include "qgsrasterlayer.h"
#include "qgsmaplayerconfigwidget.h"
#include "qgsmaplayerstylemanagerwidget.h"
Expand Down Expand Up @@ -170,10 +171,14 @@ void QgsLayerStylingWidget::setLayer( QgsMapLayer *layer )
transparencyItem->setToolTip( tr( "Transparency" ) );
transparencyItem->setData( Qt::UserRole, RasterTransparency );
mOptionsListWidget->addItem( transparencyItem );
QListWidgetItem* histogramItem = new QListWidgetItem( QgsApplication::getThemeIcon( "propertyicons/histogram.png" ), QString() );
histogramItem->setData( Qt::UserRole, RasterHistogram );
mOptionsListWidget->addItem( histogramItem );
histogramItem->setToolTip( tr( "Histogram" ) );

if ( static_cast<QgsRasterLayer*>( layer )->dataProvider()->capabilities() & QgsRasterDataProvider::Size )
{
QListWidgetItem* histogramItem = new QListWidgetItem( QgsApplication::getThemeIcon( "propertyicons/histogram.png" ), QString() );
histogramItem->setData( Qt::UserRole, RasterHistogram );
mOptionsListWidget->addItem( histogramItem );
histogramItem->setToolTip( tr( "Histogram" ) );
}
}

Q_FOREACH ( QgsMapLayerConfigWidgetFactory* factory, mPageFactories )
Expand Down Expand Up @@ -391,21 +396,24 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer()
}
case 2: // Histogram
{
if ( mRasterStyleWidget )
if ( rlayer->dataProvider()->capabilities() & QgsRasterDataProvider::Size )
{
mRasterStyleWidget->deleteLater();
delete mRasterStyleWidget;
if ( mRasterStyleWidget )
{
mRasterStyleWidget->deleteLater();
delete mRasterStyleWidget;
}
mRasterStyleWidget = new QgsRendererRasterPropertiesWidget( rlayer, mMapCanvas, mWidgetStack );
mRasterStyleWidget->syncToLayer( rlayer );
connect( mRasterStyleWidget, SIGNAL( widgetChanged() ), this, SLOT( autoApply() ) );

QgsRasterHistogramWidget* widget = new QgsRasterHistogramWidget( rlayer, mWidgetStack );
connect( widget, SIGNAL( widgetChanged() ), this, SLOT( autoApply() ) );
QString name = mRasterStyleWidget->currentRenderWidget()->renderer()->type();
widget->setRendererWidget( name, mRasterStyleWidget->currentRenderWidget() );

mWidgetStack->addMainPanel( widget );
}
mRasterStyleWidget = new QgsRendererRasterPropertiesWidget( rlayer, mMapCanvas, mWidgetStack );
mRasterStyleWidget->syncToLayer( rlayer );
connect( mRasterStyleWidget, SIGNAL( widgetChanged() ), this, SLOT( autoApply() ) );

QgsRasterHistogramWidget* widget = new QgsRasterHistogramWidget( rlayer, mWidgetStack );
connect( widget, SIGNAL( widgetChanged() ), this, SLOT( autoApply() ) );
QString name = mRasterStyleWidget->currentRenderWidget()->renderer()->type();
widget->setRendererWidget( name, mRasterStyleWidget->currentRenderWidget() );

mWidgetStack->addMainPanel( widget );
break;
}
default:
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,7 @@ SET(QGIS_CORE_MOC_HDRS
qgsdataprovider.h
qgsdbfilterproxymodel.h
qgseditformconfig.h
qgsfeedback.h
qgsfield.h
qgsgeometryvalidator.h
qgsgml.h
Expand Down
13 changes: 13 additions & 0 deletions src/core/qgsdataitemproviderregistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "qgslogger.h"
#include "qgsproviderregistry.h"

typedef QList<QgsDataItemProvider*> dataItemProviders_t();


/**
* \ingroup core
Expand Down Expand Up @@ -64,6 +66,17 @@ QgsDataItemProviderRegistry::QgsDataItemProviderRegistry()
if ( !library )
continue;

// new / better way of returning data items from providers

dataItemProviders_t* dataItemProvidersFn = reinterpret_cast< dataItemProviders_t * >( cast_to_fptr( library->resolve( "dataItemProviders" ) ) );
if ( dataItemProvidersFn )
{
// the function is a factory - we keep ownership of the returned providers
mProviders << dataItemProvidersFn();
}

// legacy support - using dataItem() and dataCapabilities() methods

dataCapabilities_t * dataCapabilities = reinterpret_cast< dataCapabilities_t * >( cast_to_fptr( library->resolve( "dataCapabilities" ) ) );
if ( !dataCapabilities )
{
Expand Down
73 changes: 73 additions & 0 deletions src/core/qgsfeedback.h
Original file line number Diff line number Diff line change
@@ -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 2.18
*/
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
4 changes: 3 additions & 1 deletion src/core/qgsmaplayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ bool QgsMapLayer::readLayerXML( const QDomElement& layerElement )
// This is modified version of old QgsWmsProvider::parseUri
// The new format has always params crs,format,layers,styles and that params
// should not appear in old format url -> use them to identify version
if ( !mDataSource.contains( "crs=" ) && !mDataSource.contains( "format=" ) )
// XYZ tile layers do not need to contain crs,format params, but they have type=xyz
if ( !mDataSource.contains( "type=" ) &&
!mDataSource.contains( "crs=" ) && !mDataSource.contains( "format=" ) )
{
QgsDebugMsg( "Old WMS URI format detected -> converting to new format" );
QgsDataSourceURI uri;
Expand Down
6 changes: 6 additions & 0 deletions src/core/qgsmaplayerrenderer.h
Original file line number Diff line number Diff line change
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 2.18
virtual QgsFeedback* feedback() const { return nullptr; }

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

Expand Down
9 changes: 9 additions & 0 deletions src/core/qgsmaprenderercustompainterjob.cpp
Original file line number Diff line number Diff line change
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 @@ -138,6 +139,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 Expand Up @@ -408,6 +411,12 @@ bool QgsMapRendererJob::needTemporaryImage( QgsMapLayer* ml )
return true;
}
}
else if ( ml->type() == QgsMapLayer::RasterLayer )
{
// preview of intermediate raster rendering results requires a temporary output image
if ( mSettings.testFlag( QgsMapSettings::RenderPartialOutput ) )
return true;
}

return false;
}
Expand Down
3 changes: 3 additions & 0 deletions src/core/qgsmaprendererparalleljob.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "qgsmaprendererparalleljob.h"

#include "qgsfeedback.h"
#include "qgslabelingenginev2.h"
#include "qgslogger.h"
#include "qgsmaplayerrenderer.h"
Expand Down Expand Up @@ -105,6 +106,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 )
Expand Down
3 changes: 2 additions & 1 deletion src/core/qgsmapsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,8 @@ class CORE_EXPORT QgsMapSettings
UseRenderingOptimization = 0x20, //!< Enable vector simplification and other rendering optimizations
DrawSelection = 0x40, //!< Whether vector selections should be shown in the rendered map
DrawSymbolBounds = 0x80, //!< Draw bounds of symbols (for debugging/testing)
RenderMapTile = 0x100 //!< Draw map such that there are no problems between adjacent tiles
RenderMapTile = 0x100, //!< Draw map such that there are no problems between adjacent tiles
RenderPartialOutput = 0x200, //!< Whether to make extra effort to update map image with partially rendered layers (better for interactive map canvas). Added in QGIS 2.18
// TODO: ignore scale-based visibility (overview)
};
Q_DECLARE_FLAGS( Flags, Flag )
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsrendercontext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ QgsRenderContext QgsRenderContext::fromMapSettings( const QgsMapSettings& mapSet
ctx.setFlag( DrawSymbolBounds, mapSettings.testFlag( QgsMapSettings::DrawSymbolBounds ) );
ctx.setFlag( RenderMapTile, mapSettings.testFlag( QgsMapSettings::RenderMapTile ) );
ctx.setFlag( Antialiasing, mapSettings.testFlag( QgsMapSettings::Antialiasing ) );
ctx.setFlag( RenderPartialOutput, mapSettings.testFlag( QgsMapSettings::RenderPartialOutput ) );
ctx.setRasterScaleFactor( 1.0 );
ctx.setScaleFactor( mapSettings.outputDpi() / 25.4 ); // = pixels per mm
ctx.setRendererScale( mapSettings.scale() );
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsrendercontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ class CORE_EXPORT QgsRenderContext
DrawSymbolBounds = 0x20, //!< Draw bounds of symbols (for debugging/testing)
RenderMapTile = 0x40, //!< Draw map such that there are no problems between adjacent tiles
Antialiasing = 0x80, //!< Use antialiasing while drawing
RenderPartialOutput = 0x100, //!< Whether to make extra effort to update map image with partially rendered layers (better for interactive map canvas). Added in QGIS 2.18
};
Q_DECLARE_FLAGS( Flags, Flag )

Expand Down
7 changes: 6 additions & 1 deletion src/core/raster/qgsbrightnesscontrastfilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ bool QgsBrightnessContrastFilter::setInput( QgsRasterInterface* input )
}

QgsRasterBlock * QgsBrightnessContrastFilter::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock * QgsBrightnessContrastFilter::block2( 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 );
Expand All @@ -122,7 +127,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->block2( bandNumber, extent, width, height, feedback );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgsbrightnesscontrastfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,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 *block2( 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; }
Expand Down
9 changes: 7 additions & 2 deletions src/core/raster/qgshillshaderenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ void QgsHillshadeRenderer::writeXML( QDomDocument &doc, QDomElement &parentElem
}

QgsRasterBlock *QgsHillshadeRenderer::block( int bandNo, const QgsRectangle &extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

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

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

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

alphaBlock = mInput->block( mAlphaBand, extent, width, height );
alphaBlock = mInput->block2( mAlphaBand, extent, width, height, feedback );
if ( !alphaBlock || alphaBlock->isEmpty() )
{
// TODO: better to render without alpha
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgshillshaderenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,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 *block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) override;

QList<int> usesBands() const override;

Expand Down
7 changes: 6 additions & 1 deletion src/core/raster/qgshuesaturationfilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,11 @@ bool QgsHueSaturationFilter::setInput( QgsRasterInterface* input )
}

QgsRasterBlock * QgsHueSaturationFilter::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock * QgsHueSaturationFilter::block2( 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 );
Expand All @@ -131,7 +136,7 @@ QgsRasterBlock * QgsHueSaturationFilter::block( int bandNo, QgsRectangle const

// At this moment we know that we read rendered image
int bandNumber = 1;
QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, width, height );
QgsRasterBlock *inputBlock = mInput->block2( bandNumber, extent, width, height, feedback );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgshuesaturationfilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class CORE_EXPORT QgsHueSaturationFilter : public QgsRasterInterface
bool setInput( QgsRasterInterface* input ) override;

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

void setSaturation( int saturation );
int saturation() const { return mSaturation; }
Expand Down
7 changes: 6 additions & 1 deletion src/core/raster/qgsmultibandcolorrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ QgsRasterRenderer* QgsMultiBandColorRenderer::create( const QDomElement& elem, Q
}

QgsRasterBlock* QgsMultiBandColorRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock* QgsMultiBandColorRenderer::block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( bandNo );
QgsRasterBlock *outputBlock = new QgsRasterBlock();
Expand Down Expand Up @@ -185,7 +190,7 @@ QgsRasterBlock* QgsMultiBandColorRenderer::block( int bandNo, QgsRectangle cons
bandIt = bands.constBegin();
for ( ; bandIt != bands.constEnd(); ++bandIt )
{
bandBlocks[*bandIt] = mInput->block( *bandIt, extent, width, height );
bandBlocks[*bandIt] = mInput->block2( *bandIt, extent, width, height, feedback );
if ( !bandBlocks[*bandIt] )
{
// We should free the alloced mem from block().
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgsmultibandcolorrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class CORE_EXPORT QgsMultiBandColorRenderer: public QgsRasterRenderer
static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input );

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

int redBand() const { return mRedBand; }
void setRedBand( int band ) { mRedBand = band; }
Expand Down
9 changes: 7 additions & 2 deletions src/core/raster/qgspalettedrasterrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,19 @@ void QgsPalettedRasterRenderer::setLabel( int idx, const QString& label )
}

QgsRasterBlock * QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock * QgsPalettedRasterRenderer::block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
{
QgsRasterBlock *outputBlock = new QgsRasterBlock();
if ( !mInput || mNColors == 0 )
{
return outputBlock;
}

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

if ( !inputBlock || inputBlock->isEmpty() )
{
Expand All @@ -176,7 +181,7 @@ QgsRasterBlock * QgsPalettedRasterRenderer::block( int bandNo, QgsRectangle con

if ( mAlphaBand > 0 && mAlphaBand != mBand )
{
alphaBlock = mInput->block( mAlphaBand, extent, width, height );
alphaBlock = mInput->block2( mAlphaBand, extent, width, height, feedback );
if ( !alphaBlock || alphaBlock->isEmpty() )
{
delete inputBlock;
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgspalettedrasterrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class CORE_EXPORT QgsPalettedRasterRenderer: public QgsRasterRenderer
static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input );

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

/** Returns number of colors*/
int nColors() const { return mNColors; }
Expand Down
9 changes: 7 additions & 2 deletions src/core/raster/qgsrasterdataprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ void QgsRasterDataProvider::setUseSrcNoDataValue( int bandNo, bool use )
}

QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle const & theExtent, int theWidth, int theHeight )
{
return block2( theBandNo, theExtent, theWidth, theHeight );
}

QgsRasterBlock * QgsRasterDataProvider::block2( int theBandNo, QgsRectangle const & theExtent, int theWidth, int theHeight, QgsRasterBlockFeedback* feedback )
{
QgsDebugMsgLevel( QString( "theBandNo = %1 theWidth = %2 theHeight = %3" ).arg( theBandNo ).arg( theWidth ).arg( theHeight ), 4 );
QgsDebugMsgLevel( QString( "theExtent = %1" ).arg( theExtent.toString() ), 4 );
Expand Down Expand Up @@ -154,7 +159,7 @@ QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle cons
tmpBlock = new QgsRasterBlock( dataType( theBandNo ), tmpWidth, tmpHeight );
}

readBlock( theBandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits() );
readBlock( theBandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits(), feedback );

int pixelSize = dataTypeSize( theBandNo );

Expand Down Expand Up @@ -204,7 +209,7 @@ QgsRasterBlock * QgsRasterDataProvider::block( int theBandNo, QgsRectangle cons
}
else
{
readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits() );
readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits(), feedback );
}

// apply scale and offset
Expand Down
5 changes: 3 additions & 2 deletions src/core/raster/qgsrasterdataprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast

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

/** Return true if source band has no data value */
virtual bool srcHasNoDataValue( int bandNo ) const { return mSrcHasNoDataValue.value( bandNo -1 ); }
Expand Down Expand Up @@ -446,8 +447,8 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast
/** 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 )
{ Q_UNUSED( bandNo ); Q_UNUSED( viewExtent ); Q_UNUSED( width ); Q_UNUSED( height ); Q_UNUSED( data ); }
virtual void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data, QgsRasterBlockFeedback* feedback = nullptr )
{ Q_UNUSED( bandNo ); Q_UNUSED( viewExtent ); Q_UNUSED( width ); Q_UNUSED( height ); Q_UNUSED( data ); Q_UNUSED( feedback ); }

/** Returns true if user no data contains value */
bool userNoDataValuesContains( int bandNo, double value ) const;
Expand Down
22 changes: 20 additions & 2 deletions src/core/raster/qgsrasterdrawer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "qgslogger.h"
#include "qgsrasterdrawer.h"
#include "qgsrasterinterface.h"
#include "qgsrasteriterator.h"
#include "qgsrasterviewport.h"
#include "qgsmaptopixel.h"
Expand All @@ -29,7 +30,7 @@ QgsRasterDrawer::QgsRasterDrawer( QgsRasterIterator* iterator ): mIterator( iter
{
}

void QgsRasterDrawer::draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsMapToPixel* theQgsMapToPixel, const QgsRenderContext* ctx )
void QgsRasterDrawer::draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsMapToPixel* theQgsMapToPixel, const QgsRenderContext *ctx, QgsRasterBlockFeedback* feedback )
{
QgsDebugMsgLevel( "Entered", 4 );
if ( !p || !mIterator || !viewPort || !theQgsMapToPixel )
Expand All @@ -39,7 +40,7 @@ void QgsRasterDrawer::draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsM

// last pipe filter has only 1 band
int bandNumber = 1;
mIterator->startRasterRead( bandNumber, viewPort->mWidth, viewPort->mHeight, viewPort->mDrawnExtent );
mIterator->startRasterRead( bandNumber, viewPort->mWidth, viewPort->mHeight, viewPort->mDrawnExtent, feedback );

//number of cols/rows in output pixels
int nCols = 0;
Expand Down Expand Up @@ -86,9 +87,26 @@ void QgsRasterDrawer::draw( QPainter* p, QgsRasterViewPort* viewPort, const QgsM
}
}

if ( feedback && feedback->renderPartialOutput() )
{
// there could have been partial preview written before
// so overwrite anything with the resulting image.
// (we are guaranteed to have a temporary image for this layer, see QgsMapRendererJob::needTemporaryImage)
p->setCompositionMode( QPainter::CompositionMode_Source );
}

drawImage( p, viewPort, img, topLeftCol, topLeftRow, theQgsMapToPixel );

delete block;

p->setCompositionMode( QPainter::CompositionMode_SourceOver ); // go back to the default composition mode

// ok this does not matter much anyway as the tile size quite big so most of the time
// there would be just one tile for the whole display area, but it won't hurt...
if ( feedback && feedback->isCancelled() )
break;

// for compatibility
if ( ctx && ctx->renderingStopped() )
break;
}
Expand Down
4 changes: 3 additions & 1 deletion src/core/raster/qgsrasterdrawer.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class QImage;
class QgsMapToPixel;
class QgsRenderContext;
struct QgsRasterViewPort;
class QgsRasterBlockFeedback;
class QgsRasterIterator;

/** \ingroup core
Expand All @@ -41,8 +42,9 @@ class CORE_EXPORT QgsRasterDrawer
* @param viewPort viewport to render
* @param theQgsMapToPixel map to pixel converter
* @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
59 changes: 59 additions & 0 deletions src/core/raster/qgsrasterinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,53 @@
#include <QCoreApplication> // for tr()
#include <QImage>

#include "qgsfeedback.h"
#include "qgslogger.h"
#include "qgsrasterbandstats.h"
#include "qgsrasterblock.h"
#include "qgsrasterhistogram.h"
#include "qgsrectangle.h"

/** \ingroup core
* Feedback object tailored for raster block reading.
*
* @note added in QGIS 2.18
*/
class CORE_EXPORT QgsRasterBlockFeedback : public QgsFeedback
{
public:
//! Construct a new raster block feedback object
QgsRasterBlockFeedback( QObject* parent = nullptr ) : QgsFeedback( parent ), mPreviewOnly( false ), mRenderPartialOutput( false ) {}

//! 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 { return mPreviewOnly; }
//! set flag whether the block request is for preview purposes only
//! @see isPreviewOnly()
void setPreviewOnly( bool preview ) { mPreviewOnly = preview; }

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

private:
//! Whether the raster provider should return only data that are already available
//! without waiting for full result
bool mPreviewOnly;

//! Whether our painter is drawing to a temporary image used just by this layer
bool mRenderPartialOutput;
};


/** \ingroup core
* Base class for processing filters like renderers, reprojector, resampler etc.
*/
Expand Down Expand Up @@ -112,6 +153,23 @@ class CORE_EXPORT QgsRasterInterface
*/
virtual QgsRasterBlock *block( int bandNo, const QgsRectangle &extent, int width, int height ) = 0;

/** 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 )
{
Q_UNUSED( feedback );
return block( bandNo, extent, width, height );
}

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

};

#endif
Expand Down
6 changes: 4 additions & 2 deletions src/core/raster/qgsrasteriterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,21 @@

QgsRasterIterator::QgsRasterIterator( QgsRasterInterface* input )
: mInput( input )
, mFeedback( nullptr )
, mMaximumTileWidth( 2000 )
, mMaximumTileHeight( 2000 )
{
}

void QgsRasterIterator::startRasterRead( int bandNumber, int nCols, int nRows, const QgsRectangle& extent )
void QgsRasterIterator::startRasterRead( int bandNumber, int nCols, int nRows, const QgsRectangle& extent, QgsRasterBlockFeedback* feedback )
{
if ( !mInput )
{
return;
}

mExtent = extent;
mFeedback = feedback;

//remove any previous part on that band
removePartInfo( bandNumber );
Expand Down Expand Up @@ -93,7 +95,7 @@ bool QgsRasterIterator::readNextRasterPart( int bandNumber,
double ymax = viewPortExtent.yMaximum() - pInfo.currentRow / static_cast< double >( pInfo.nRows ) * viewPortExtent.height();
QgsRectangle blockRect( xmin, ymin, xmax, ymax );

*block = mInput->block( bandNumber, blockRect, nCols, nRows );
*block = mInput->block2( bandNumber, blockRect, nCols, nRows, mFeedback );
topLeftCol = pInfo.currentCol;
topLeftRow = pInfo.currentRow;

Expand Down
5 changes: 4 additions & 1 deletion src/core/raster/qgsrasteriterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

class QgsMapToPixel;
class QgsRasterBlock;
class QgsRasterBlockFeedback;
class QgsRasterInterface;
class QgsRasterProjector;
struct QgsRasterViewPort;
Expand All @@ -38,8 +39,9 @@ class CORE_EXPORT 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 Expand Up @@ -79,6 +81,7 @@ class CORE_EXPORT QgsRasterIterator
QgsRasterInterface* mInput;
QMap<int, RasterPartInfo> mRasterPartInfos;
QgsRectangle mExtent;
QgsRasterBlockFeedback* mFeedback;

int mMaximumTileWidth;
int mMaximumTileHeight;
Expand Down
43 changes: 41 additions & 2 deletions src/core/raster/qgsrasterlayerrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRender
, mRasterViewPort( nullptr )
, mPipe( nullptr )
, mContext( rendererContext )
, mFeedback( new Feedback( this ) )
{

mPainter = rendererContext.painter();
const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
mMapToPixel = &theQgsMapToPixel;
Expand Down Expand Up @@ -178,6 +178,8 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRender

QgsRasterLayerRenderer::~QgsRasterLayerRenderer()
{
delete mFeedback;

delete mRasterViewPort;
delete mPipe;
}
Expand Down Expand Up @@ -210,9 +212,46 @@ bool QgsRasterLayerRenderer::render()
// Drawer to pipe?
QgsRasterIterator iterator( mPipe->last() );
QgsRasterDrawer drawer( &iterator );
drawer.draw( mPainter, mRasterViewPort, mMapToPixel, &mContext );
drawer.draw( mPainter, mRasterViewPort, mMapToPixel, nullptr, mFeedback );

QgsDebugMsgLevel( QString( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );

return true;
}

QgsFeedback* QgsRasterLayerRenderer::feedback() const
{
return mFeedback;
}

QgsRasterLayerRenderer::Feedback::Feedback( QgsRasterLayerRenderer *r )
: mR( r )
, mMinimalPreviewInterval( 250 )
{
setRenderPartialOutput( r->mContext.testFlag( QgsRenderContext::RenderPartialOutput ) );
}

void QgsRasterLayerRenderer::Feedback::onNewData()
{
if ( !renderPartialOutput() )
return; // we were not asked for partial renders and we may not have a temporary image for overwriting...

// update only once upon a time
// (preview itself takes some time)
if ( mLastPreview.isValid() && mLastPreview.msecsTo( QTime::currentTime() ) < mMinimalPreviewInterval )
return;

// TODO: update only the area that got new data

QgsDebugMsg( QString( "new raster preview! %1" ).arg( mLastPreview.msecsTo( QTime::currentTime() ) ) );
QTime t;
t.start();
QgsRasterBlockFeedback feedback;
feedback.setPreviewOnly( true );
feedback.setRenderPartialOutput( true );
QgsRasterIterator iterator( mR->mPipe->last() );
QgsRasterDrawer drawer( &iterator );
drawer.draw( mR->mPainter, mR->mRasterViewPort, mR->mMapToPixel, nullptr, &feedback );
QgsDebugMsg( QString( "total raster preview time: %1 ms" ).arg( t.elapsed() ) );
mLastPreview = QTime::currentTime();
}
31 changes: 31 additions & 0 deletions src/core/raster/qgsrasterlayerrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@
class QPainter;

class QgsMapToPixel;
class QgsRasterBlockFeedback;
class QgsRasterLayer;
class QgsRasterPipe;
struct QgsRasterViewPort;
class QgsRenderContext;

class QgsRasterLayerRenderer;

#include "qgsrasterinterface.h"


/** \ingroup core
* Implementation of threaded rendering for raster layers.
*
Expand All @@ -40,6 +46,8 @@ class QgsRasterLayerRenderer : public QgsMapLayerRenderer

virtual bool render() override;

virtual QgsFeedback* feedback() const override;

protected:

QPainter* mPainter;
Expand All @@ -48,6 +56,29 @@ class QgsRasterLayerRenderer : public QgsMapLayerRenderer

QgsRasterPipe* mPipe;
QgsRenderContext& mContext;

/** \ingroup core
* Specific internal feedback class to provide preview of raster layer rendering.
* @note added in 2.18
* @note not available in Python bindings
*/
class Feedback : public QgsRasterBlockFeedback
{
public:
//! Create feedback object based on our layer renderer
explicit Feedback( QgsRasterLayerRenderer* r );

//! when notified of new data in data provider it launches a preview draw of the raster
virtual void onNewData() override;
private:
QgsRasterLayerRenderer* mR; //!< parent renderer instance
int mMinimalPreviewInterval; //!< in miliseconds
QTime mLastPreview; //!< when last preview has been generated
};

//! feedback class for cancellation and preview generation
Feedback* mFeedback;
};


#endif // QGSRASTERLAYERRENDERER_H
7 changes: 6 additions & 1 deletion src/core/raster/qgsrasternuller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,19 @@ QGis::DataType QgsRasterNuller::dataType( int bandNo ) const
}

QgsRasterBlock * QgsRasterNuller::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock * QgsRasterNuller::block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
{
QgsDebugMsgLevel( "Entered", 4 );
if ( !mInput )
{
return new QgsRasterBlock();
}

QgsRasterBlock *inputBlock = mInput->block( bandNo, extent, width, height );
QgsRasterBlock *inputBlock = mInput->block2( bandNo, extent, width, height, feedback );
if ( !inputBlock )
{
return new QgsRasterBlock();
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgsrasternuller.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class CORE_EXPORT QgsRasterNuller : public QgsRasterInterface
QGis::DataType dataType( int bandNo ) const override;

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

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

Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgsrasterpipe.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#define QGSRASTERPIPE_H

#include <QImage>
#include <QMap>
#include <QObject>

#include "qgsbrightnesscontrastfilter.h"
Expand Down
256 changes: 105 additions & 151 deletions src/core/raster/qgsrasterprojector.cpp

Large diffs are not rendered by default.

107 changes: 60 additions & 47 deletions src/core/raster/qgsrasterprojector.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
class QgsPoint;

/** \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 CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
Expand All @@ -50,11 +53,8 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
Exact = 1, //!< 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
Q_DECL_DEPRECATED
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
int theSrcDatumTransform,
Expand All @@ -64,24 +64,30 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);

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

/** \brief Copy constructor
* @deprecated since 2.18: use clone()
*/
// To avoid synthesized which fails on copy of QgsCoordinateTransform
// (QObject child) in Python bindings
QgsRasterProjector( const QgsRasterProjector &projector );
Q_DECL_DEPRECATED QgsRasterProjector( const QgsRasterProjector &projector );

/** \brief The destructor */
~QgsRasterProjector();
Expand All @@ -104,19 +110,16 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
/** \brief Get destination CRS */
QgsCoordinateReferenceSystem destCrs() const { return mDestCRS; }

/** \brief set maximum source resolution */
void setMaxSrcRes( double theMaxSrcXRes, double theMaxSrcYRes )
{
mMaxSrcXRes = theMaxSrcXRes;
mMaxSrcYRes = theMaxSrcYRes;
}
/** @deprecated since 2.18, does nothing */
Q_DECL_DEPRECATED void setMaxSrcRes( double theMaxSrcXRes, double theMaxSrcYRes ) { Q_UNUSED( theMaxSrcXRes ); Q_UNUSED( theMaxSrcYRes ); }

Precision precision() const { return mPrecision; }
void setPrecision( Precision precision ) { mPrecision = 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 ) override;
QgsRasterBlock *block2( int bandNo, const QgsRectangle & extent, int width, int height, QgsRasterBlockFeedback* feedback = nullptr ) override;

/** Calculate destination extent and size from source extent and size */
bool destExtentSize( const QgsRectangle& theSrcExtent, int theSrcXSize, int theSrcYSize,
Expand All @@ -128,43 +131,62 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
QgsRectangle& theDestExtent, int& theDestXSize, int& theDestYSize );

private:
/** Get source extent */
QgsRectangle srcExtent() { return mSrcExtent; }

/** Get/set source width/height */
int srcRows() { return mSrcRows; }
int srcCols() { return mSrcCols; }
void setSrcRows( int theRows ) { mSrcRows = theRows; mSrcXRes = mSrcExtent.height() / mSrcRows; }
void setSrcCols( int theCols ) { mSrcCols = theCols; mSrcYRes = mSrcExtent.width() / mSrcCols; }
/** Source CRS */
QgsCoordinateReferenceSystem mSrcCRS;

/** Destination CRS */
QgsCoordinateReferenceSystem mDestCRS;

/** Source datum transformation id (or -1 if none) */
int mSrcDatumTransform;

/** Destination datum transformation id (or -1 if none) */
int mDestDatumTransform;

/** Requested precision */
Precision mPrecision;

};

/// @cond PRIVATE

/**
* Internal class for reprojection of rasters - either exact or approximate.
* QgsRasterProjector creates it and then keeps calling srcRowCol() to get source pixel position
* for every destination pixel position.
*/
class ProjectorData
{
public:
/** Initialize reprojector and calculate matrix */
ProjectorData( const QgsRectangle &extent, int width, int height, QgsRasterInterface *input, const QgsCoordinateTransform *inverseCt, QgsRasterProjector::Precision precision );
~ProjectorData();

/** \brief Get source row and column indexes for current source extent and resolution
If source pixel is outside source extent theSrcRow and theSrcCol are left unchanged.
@return true if inside source
*/
bool srcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol, const QgsCoordinateTransform* ct );
bool srcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol );

int dstRows() const { return mDestRows; }
int dstCols() const { return mDestCols; }
QgsRectangle srcExtent() const { return mSrcExtent; }
int srcRows() const { return mSrcRows; }
int srcCols() const { return mSrcCols; }

private:
/** \brief get destination point for _current_ destination position */
void destPointOnCPMatrix( int theRow, int theCol, double *theX, double *theY );

/** \brief Get matrix upper left row/col indexes for destination row/col */
int matrixRow( int theDestRow );
int matrixCol( int theDestCol );

/** \brief get destination point for _current_ matrix position */
QgsPoint srcPoint( int theRow, int theCol );

/** \brief Get precise source row and column indexes for current source extent and resolution */
inline bool preciseSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol, const QgsCoordinateTransform* ct );
inline bool preciseSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol );

/** \brief Get approximate source row and column indexes for current source extent and resolution */
inline bool approximateSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol );

/** \brief Calculate matrix */
void calc();

/** \brief insert rows to matrix */
void insertRows( const QgsCoordinateTransform* ct );

Expand Down Expand Up @@ -203,17 +225,12 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
/** Get mCPMatrix as string */
QString cpToString();

/** Source CRS */
QgsCoordinateReferenceSystem mSrcCRS;

/** Destination CRS */
QgsCoordinateReferenceSystem mDestCRS;

/** Source datum transformation id (or -1 if none) */
int mSrcDatumTransform;
/** Use approximation (requested precision is Approximate and it is possible to calculate
* an approximation matrix with a sufficient precision) */
bool mApproximate;

/** Destination datum transformation id (or -1 if none) */
int mDestDatumTransform;
/** Transformation from destination CRS to source CRS */
QgsCoordinateTransform* mInverseCt;

/** Destination extent */
QgsRectangle mDestExtent;
Expand Down Expand Up @@ -284,13 +301,9 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
double mMaxSrcXRes;
double mMaxSrcYRes;

/** Requested precision */
Precision mPrecision;

/** Use approximation (requested precision is Approximate and it is possible to calculate
* an approximation matrix with a sufficient precision) */
bool mApproximate;
};

/// @endcond

#endif

2 changes: 0 additions & 2 deletions src/core/raster/qgsrasterrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ class CORE_EXPORT QgsRasterRenderer : public QgsRasterInterface

virtual bool setInput( QgsRasterInterface* input ) override;

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

bool usesTransparency() const;

void setOpacity( double opacity ) { mOpacity = opacity; }
Expand Down
9 changes: 7 additions & 2 deletions src/core/raster/qgsrasterresamplefilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ void QgsRasterResampleFilter::setZoomedOutResampler( QgsRasterResampler* r )
}

QgsRasterBlock * QgsRasterResampleFilter::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock * QgsRasterResampleFilter::block2( 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 );
Expand Down Expand Up @@ -169,7 +174,7 @@ QgsRasterBlock * QgsRasterResampleFilter::block( int bandNo, QgsRectangle const
{
QgsDebugMsgLevel( "No oversampling.", 4 );
delete outputBlock;
return mInput->block( bandNumber, extent, width, height );
return mInput->block2( bandNumber, extent, width, height, feedback );
}

//effective oversampling factors are different to global one because of rounding
Expand All @@ -181,7 +186,7 @@ QgsRasterBlock * QgsRasterResampleFilter::block( int bandNo, QgsRectangle const
int resWidth = width * oversamplingX;
int resHeight = height * oversamplingY;

QgsRasterBlock *inputBlock = mInput->block( bandNumber, extent, resWidth, resHeight );
QgsRasterBlock *inputBlock = mInput->block2( bandNumber, extent, resWidth, resHeight, feedback );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgsrasterresamplefilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class CORE_EXPORT QgsRasterResampleFilter : public QgsRasterInterface
bool setInput( QgsRasterInterface* input ) override;

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

/** Set resampler for zoomed in scales. Takes ownership of the object*/
void setZoomedInResampler( QgsRasterResampler* r );
Expand Down
7 changes: 6 additions & 1 deletion src/core/raster/qgssinglebandcolordatarenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ QgsRasterRenderer* QgsSingleBandColorDataRenderer::create( const QDomElement& el
}

QgsRasterBlock* QgsSingleBandColorDataRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock* QgsSingleBandColorDataRenderer::block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( bandNo );

Expand All @@ -62,7 +67,7 @@ QgsRasterBlock* QgsSingleBandColorDataRenderer::block( int bandNo, QgsRectangle
return outputBlock;
}

QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height );
QgsRasterBlock *inputBlock = mInput->block2( mBand, extent, width, height, feedback );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgssinglebandcolordatarenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class CORE_EXPORT QgsSingleBandColorDataRenderer: public QgsRasterRenderer
bool setInput( QgsRasterInterface* input ) override;

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

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

Expand Down
9 changes: 7 additions & 2 deletions src/core/raster/qgssinglebandgrayrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ void QgsSingleBandGrayRenderer::setContrastEnhancement( QgsContrastEnhancement*
}

QgsRasterBlock* QgsSingleBandGrayRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock* QgsSingleBandGrayRenderer::block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( bandNo );
QgsDebugMsgLevel( QString( "width = %1 height = %2" ).arg( width ).arg( height ), 4 );
Expand All @@ -89,7 +94,7 @@ QgsRasterBlock* QgsSingleBandGrayRenderer::block( int bandNo, QgsRectangle cons
return outputBlock;
}

QgsRasterBlock *inputBlock = mInput->block( mGrayBand, extent, width, height );
QgsRasterBlock *inputBlock = mInput->block2( mGrayBand, extent, width, height, feedback );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
Expand All @@ -101,7 +106,7 @@ QgsRasterBlock* QgsSingleBandGrayRenderer::block( int bandNo, QgsRectangle cons

if ( mAlphaBand > 0 && mGrayBand != mAlphaBand )
{
alphaBlock = mInput->block( mAlphaBand, extent, width, height );
alphaBlock = mInput->block2( mAlphaBand, extent, width, height, feedback );
if ( !alphaBlock || alphaBlock->isEmpty() )
{
// TODO: better to render without alpha
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgssinglebandgrayrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class CORE_EXPORT QgsSingleBandGrayRenderer: public QgsRasterRenderer
static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input );

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

int grayBand() const { return mGrayBand; }
void setGrayBand( int band ) { mGrayBand = band; }
Expand Down
9 changes: 7 additions & 2 deletions src/core/raster/qgssinglebandpseudocolorrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ QgsRasterRenderer* QgsSingleBandPseudoColorRenderer::create( const QDomElement&
}

QgsRasterBlock* QgsSingleBandPseudoColorRenderer::block( int bandNo, QgsRectangle const & extent, int width, int height )
{
return block2( bandNo, extent, width, height );
}

QgsRasterBlock* QgsSingleBandPseudoColorRenderer::block2( int bandNo, QgsRectangle const & extent, int width, int height, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( bandNo );

Expand All @@ -118,7 +123,7 @@ QgsRasterBlock* QgsSingleBandPseudoColorRenderer::block( int bandNo, QgsRectangl
}


QgsRasterBlock *inputBlock = mInput->block( mBand, extent, width, height );
QgsRasterBlock *inputBlock = mInput->block2( mBand, extent, width, height, feedback );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
Expand All @@ -132,7 +137,7 @@ QgsRasterBlock* QgsSingleBandPseudoColorRenderer::block( int bandNo, QgsRectangl
QgsRasterBlock *alphaBlock = nullptr;
if ( mAlphaBand > 0 && mAlphaBand != mBand )
{
alphaBlock = mInput->block( mAlphaBand, extent, width, height );
alphaBlock = mInput->block2( mAlphaBand, extent, width, height, feedback );
if ( !alphaBlock || alphaBlock->isEmpty() )
{
delete inputBlock;
Expand Down
1 change: 1 addition & 0 deletions src/core/raster/qgssinglebandpseudocolorrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class CORE_EXPORT QgsSingleBandPseudoColorRenderer: public QgsRasterRenderer
static QgsRasterRenderer* create( const QDomElement& elem, QgsRasterInterface* input );

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

/** Takes ownership of the shader*/
void setShader( QgsRasterShader* shader );
Expand Down
1 change: 1 addition & 0 deletions src/gui/qgsmapcanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ QgsMapCanvas::QgsMapCanvas( QWidget * parent, const char *name )

mSettings.setFlag( QgsMapSettings::DrawEditingInfo );
mSettings.setFlag( QgsMapSettings::UseRenderingOptimization );
mSettings.setFlag( QgsMapSettings::RenderPartialOutput );

//segmentation parameters
QSettings settings;
Expand Down
7 changes: 7 additions & 0 deletions src/gui/raster/qgsrastertransparencywidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ void QgsRasterTransparencyWidget::syncToLayer()
QgsRasterRenderer* renderer = mRasterLayer->renderer();
if ( provider )
{
if ( provider->dataType( 1 ) == QGis::ARGB32
|| provider->dataType( 1 ) == QGis::ARGB32_Premultiplied )
{
gboxNoDataValue->setEnabled( false );
gboxCustomTransparency->setEnabled( false );
}

cboxTransparencyBand->addItem( tr( "None" ), -1 );
int nBands = provider->bandCount();
QString bandName;
Expand Down
4 changes: 3 additions & 1 deletion src/providers/arcgisrest/qgsamsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,8 +437,10 @@ QgsRasterIdentifyResult QgsAmsProvider::identify( const QgsPoint & thePoint, Qgs
return QgsRasterIdentifyResult( theFormat, entries );
}

void QgsAmsProvider::readBlock( int /*bandNo*/, const QgsRectangle & viewExtent, int width, int height, void *data )
void QgsAmsProvider::readBlock( int /*bandNo*/, const QgsRectangle & viewExtent, int width, int height, void *data, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( feedback ); // TODO: make use of the feedback object

// TODO: optimize to avoid writing to QImage
// returned image is actually mCachedImage, no need to delete
QImage *image = draw( viewExtent, width, height );
Expand Down
2 changes: 1 addition & 1 deletion src/providers/arcgisrest/qgsamsprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class QgsAmsProvider : public QgsRasterDataProvider
QgsRasterIdentifyResult identify( const QgsPoint & thePoint, QgsRaster::IdentifyFormat theFormat, const QgsRectangle &theExtent = QgsRectangle(), int theWidth = 0, int theHeight = 0, int theDpi = 96 ) override;

protected:
void readBlock( int bandNo, const QgsRectangle & viewExtent, int width, int height, void *data ) override;
void readBlock( int bandNo, const QgsRectangle & viewExtent, int width, int height, void *data, QgsRasterBlockFeedback* feedback = nullptr ) override;

private:
bool mValid;
Expand Down
12 changes: 9 additions & 3 deletions src/providers/gdal/qgsgdalprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,11 @@ QImage* QgsGdalProvider::draw( QgsRectangle const & viewExtent, int pixelWidth,
}

QgsRasterBlock* QgsGdalProvider::block( int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight )
{
return block2( theBandNo, theExtent, theWidth, theHeight );
}

QgsRasterBlock* QgsGdalProvider::block2( int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight, QgsRasterBlockFeedback* feedback )
{
//QgsRasterBlock *block = new QgsRasterBlock( dataType( theBandNo ), theWidth, theHeight, noDataValue( theBandNo ) );
QgsRasterBlock *block;
Expand All @@ -411,7 +416,7 @@ QgsRasterBlock* QgsGdalProvider::block( int theBandNo, const QgsRectangle &theEx
QRect subRect = QgsRasterBlock::subRect( theExtent, theWidth, theHeight, mExtent );
block->setIsNoDataExcept( subRect );
}
readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits() );
readBlock( theBandNo, theExtent, theWidth, theHeight, block->bits(), feedback );
// apply scale and offset
block->applyScaleOffset( bandScale( theBandNo ), bandOffset( theBandNo ) );
block->applyNoDataValues( userNoDataValues( theBandNo ) );
Expand All @@ -435,7 +440,7 @@ void QgsGdalProvider::readBlock( int theBandNo, int xBlock, int yBlock, void *bl
gdalRasterIO( myGdalBand, GF_Read, xOff, yOff, mXBlockSize, mYBlockSize, block, mXBlockSize, mYBlockSize, ( GDALDataType ) mGdalDataType.at( theBandNo - 1 ), 0, 0 );
}

void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent, int thePixelWidth, int thePixelHeight, void *theBlock )
void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent, int thePixelWidth, int thePixelHeight, void *theBlock, QgsRasterBlockFeedback* feedback )
{
QgsDebugMsg( "thePixelWidth = " + QString::number( thePixelWidth ) );
QgsDebugMsg( "thePixelHeight = " + QString::number( thePixelHeight ) );
Expand Down Expand Up @@ -607,11 +612,12 @@ void QgsGdalProvider::readBlock( int theBandNo, QgsRectangle const & theExtent,
GDALRasterBandH gdalBand = GDALGetRasterBand( mGdalDataset, theBandNo );
GDALDataType type = ( GDALDataType )mGdalDataType.at( theBandNo - 1 );
CPLErrorReset();

CPLErr err = gdalRasterIO( gdalBand, GF_Read,
srcLeft, srcTop, srcWidth, srcHeight,
( void * )tmpBlock,
tmpWidth, tmpHeight, type,
0, 0 );
0, 0, feedback );

if ( err != CPLE_None )
{
Expand Down
3 changes: 2 additions & 1 deletion src/providers/gdal/qgsgdalprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,10 @@ class QgsGdalProvider : public QgsRasterDataProvider, QgsGdalProviderBase

/** Reimplemented from QgsRasterDataProvider to bypass second resampling (more efficient for local file based sources)*/
QgsRasterBlock *block( int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight ) override;
QgsRasterBlock *block2( int theBandNo, const QgsRectangle &theExtent, int theWidth, int theHeight, QgsRasterBlockFeedback* feedback = nullptr ) override;

void readBlock( int bandNo, int xBlock, int yBlock, void *data ) override;
void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data ) override;
void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data, QgsRasterBlockFeedback* feedback = nullptr ) override;

/** Read band scale for raster value
* @@note added in 2.3 */
Expand Down
29 changes: 28 additions & 1 deletion src/providers/gdal/qgsgdalproviderbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,17 @@ GDALDatasetH QgsGdalProviderBase::gdalOpen( const char *pszFilename, GDALAccess
return hDS;
}

CPLErr QgsGdalProviderBase::gdalRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace )
int CPL_STDCALL _gdalProgressFnWithFeedback( double dfComplete, const char *pszMessage, void *pProgressArg )
{
Q_UNUSED( dfComplete );
Q_UNUSED( pszMessage );

QgsRasterBlockFeedback* feedback = static_cast<QgsRasterBlockFeedback*>( pProgressArg );
return !feedback->isCancelled();
}


CPLErr QgsGdalProviderBase::gdalRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace, QgsRasterBlockFeedback* feedback )
{
// See http://hub.qgis.org/issues/8356 and http://trac.osgeo.org/gdal/ticket/5170
#if GDAL_VERSION_MAJOR == 1 && ( (GDAL_VERSION_MINOR == 9 && GDAL_VERSION_REV <= 2) || (GDAL_VERSION_MINOR == 10 && GDAL_VERSION_REV <= 0) )
Expand All @@ -289,7 +299,24 @@ CPLErr QgsGdalProviderBase::gdalRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWF
QgsDebugMsg( "Disabled VSI_CACHE" );
#endif

#if GDAL_VERSION_MAJOR >= 2
GDALRasterIOExtraArg extra;
INIT_RASTERIO_EXTRA_ARG( extra );
if ( 0 && feedback ) // disabled!
{
// Currently the cancellation is disabled... When RasterIO call is cancelled,
// GDAL returns CE_Failure with error code = 0 (CPLE_None), however one would
// expect to get CPLE_UserInterrupt to clearly identify that the failure was
// caused by the cancellation and not that something dodgy is going on.
// Are both error codes acceptable?
extra.pfnProgress = _gdalProgressFnWithFeedback;
extra.pProgressData = ( void* ) feedback;
}
CPLErr err = GDALRasterIOEx( hBand, eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace, &extra );
#else
Q_UNUSED( feedback );
CPLErr err = GDALRasterIO( hBand, eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, eBufType, nPixelSpace, nLineSpace );
#endif

#if GDAL_VERSION_MAJOR == 1 && ( (GDAL_VERSION_MINOR == 9 && GDAL_VERSION_REV <= 2) || (GDAL_VERSION_MINOR == 10 && GDAL_VERSION_REV <= 0) )
CPLSetThreadLocalConfigOption( "VSI_CACHE", pszOldVal );
Expand Down
2 changes: 1 addition & 1 deletion src/providers/gdal/qgsgdalproviderbase.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class QgsGdalProviderBase
static GDALDatasetH gdalOpen( const char *pszFilename, GDALAccess eAccess );

/** Wrapper function for GDALRasterIO to get around possible bugs in GDAL */
static CPLErr gdalRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace );
static CPLErr gdalRasterIO( GDALRasterBandH hBand, GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize, void * pData, int nBufXSize, int nBufYSize, GDALDataType eBufType, int nPixelSpace, int nLineSpace, QgsRasterBlockFeedback* feedback = nullptr );

/** Wrapper function for GDALRasterIO to get around possible bugs in GDAL */
static int gdalGetOverviewCount( GDALRasterBandH hBand );
Expand Down
3 changes: 2 additions & 1 deletion src/providers/grass/qgsgrassrasterprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,9 @@ void QgsGrassRasterProvider::readBlock( int bandNo, int xBlock, int yBlock, void
memcpy( block, data.data(), size );
}

void QgsGrassRasterProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, void *block )
void QgsGrassRasterProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, void *block, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( feedback );
QgsDebugMsg( "pixelWidth = " + QString::number( pixelWidth ) );
QgsDebugMsg( "pixelHeight = " + QString::number( pixelHeight ) );
QgsDebugMsg( "viewExtent: " + viewExtent.toString() );
Expand Down
2 changes: 1 addition & 1 deletion src/providers/grass/qgsgrassrasterprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class GRASS_LIB_EXPORT QgsGrassRasterProvider : public QgsRasterDataProvider
int ySize() const override;

void readBlock( int bandNo, int xBlock, int yBlock, void *data ) override;
void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data ) override;
void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data, QgsRasterBlockFeedback* feedback = nullptr ) override;

QgsRasterBandStats bandStatistics( int theBandNo,
int theStats = QgsRasterBandStats::All,
Expand Down
68 changes: 42 additions & 26 deletions src/providers/wcs/qgswcsprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -508,9 +508,8 @@ void QgsWcsProvider::setQueryItem( QUrl &url, const QString& item, const QString
url.addQueryItem( item, value );
}

void QgsWcsProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, void *block )
void QgsWcsProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, void *block, QgsRasterBlockFeedback* feedback )
{

// TODO: set block to null values, move that to function and call only if fails
memset( block, 0, pixelWidth * pixelHeight * QgsRasterBlock::typeSize( dataType( bandNo ) ) );

Expand All @@ -528,7 +527,7 @@ void QgsWcsProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, in
mCachedViewWidth != pixelWidth ||
mCachedViewHeight != pixelHeight )
{
getCache( bandNo, viewExtent, pixelWidth, pixelHeight );
getCache( bandNo, viewExtent, pixelWidth, pixelHeight, QString(), feedback );
}

if ( mCachedGdalDataset )
Expand Down Expand Up @@ -627,7 +626,7 @@ void QgsWcsProvider::readBlock( int bandNo, QgsRectangle const & viewExtent, in
}
}

void QgsWcsProvider::getCache( int bandNo, QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, QString crs )
void QgsWcsProvider::getCache( int bandNo, QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, QString crs, QgsRasterBlockFeedback* feedback )
{
Q_UNUSED( bandNo );
// delete cached data
Expand Down Expand Up @@ -780,7 +779,7 @@ void QgsWcsProvider::getCache( int bandNo, QgsRectangle const & viewExtent, int

emit statusChanged( tr( "Getting map via WCS." ) );

QgsWcsDownloadHandler handler( url, mAuth, mCacheLoadControl, mCachedData, mCapabilities.version(), mCachedError );
QgsWcsDownloadHandler handler( url, mAuth, mCacheLoadControl, mCachedData, mCapabilities.version(), mCachedError, feedback );
handler.blockingDownload();

QgsDebugMsg( QString( "%1 bytes received" ).arg( mCachedData.size() ) );
Expand Down Expand Up @@ -848,7 +847,7 @@ void QgsWcsProvider::readBlock( int theBandNo, int xBlock, int yBlock, void *blo

QgsRectangle extent( xMin, yMin, xMax, yMax );

readBlock( theBandNo, extent, mXBlockSize, mYBlockSize, block );
readBlock( theBandNo, extent, mXBlockSize, mYBlockSize, block, nullptr );
}


Expand Down Expand Up @@ -1657,7 +1656,7 @@ QGISEXTERN bool isProvider()

int QgsWcsDownloadHandler::sErrors = 0;

QgsWcsDownloadHandler::QgsWcsDownloadHandler( const QUrl& url, QgsWcsAuthorization& auth, QNetworkRequest::CacheLoadControl cacheLoadControl, QByteArray& cachedData, const QString& wcsVersion, QgsError& cachedError )
QgsWcsDownloadHandler::QgsWcsDownloadHandler( const QUrl& url, QgsWcsAuthorization& auth, QNetworkRequest::CacheLoadControl cacheLoadControl, QByteArray& cachedData, const QString& wcsVersion, QgsError& cachedError, QgsRasterBlockFeedback* feedback )
: mAuth( auth )
, mEventLoop( new QEventLoop )
, mCacheReply( nullptr )
Expand All @@ -1678,6 +1677,9 @@ QgsWcsDownloadHandler::QgsWcsDownloadHandler( const QUrl& url, QgsWcsAuthorizati
mCacheReply = QgsNetworkAccessManager::instance()->get( request );
connect( mCacheReply, SIGNAL( finished() ), this, SLOT( cacheReplyFinished() ) );
connect( mCacheReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( cacheReplyProgress( qint64, qint64 ) ) );

if ( feedback )
connect( feedback, SIGNAL( cancelled() ), this, SLOT( cancelled() ), Qt::QueuedConnection );
}

QgsWcsDownloadHandler::~QgsWcsDownloadHandler()
Expand Down Expand Up @@ -1865,30 +1867,34 @@ void QgsWcsDownloadHandler::cacheReplyFinished()
}
else
{
// Resend request if AlwaysCache
QNetworkRequest request = mCacheReply->request();
if ( request.attribute( QNetworkRequest::CacheLoadControlAttribute ).toInt() == QNetworkRequest::AlwaysCache )
// report any errors except for the one we have caused by cancelling the request
if ( mCacheReply->error() != QNetworkReply::OperationCanceledError )
{
QgsDebugMsg( "Resend request with PreferCache" );
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );
// Resend request if AlwaysCache
QNetworkRequest request = mCacheReply->request();
if ( request.attribute( QNetworkRequest::CacheLoadControlAttribute ).toInt() == QNetworkRequest::AlwaysCache )
{
QgsDebugMsg( "Resend request with PreferCache" );
request.setAttribute( QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache );

mCacheReply->deleteLater();
mCacheReply->deleteLater();

mCacheReply = QgsNetworkAccessManager::instance()->get( request );
connect( mCacheReply, SIGNAL( finished() ), this, SLOT( cacheReplyFinished() ), Qt::DirectConnection );
connect( mCacheReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( cacheReplyProgress( qint64, qint64 ) ), Qt::DirectConnection );
mCacheReply = QgsNetworkAccessManager::instance()->get( request );
connect( mCacheReply, SIGNAL( finished() ), this, SLOT( cacheReplyFinished() ), Qt::DirectConnection );
connect( mCacheReply, SIGNAL( downloadProgress( qint64, qint64 ) ), this, SLOT( cacheReplyProgress( qint64, qint64 ) ), Qt::DirectConnection );

return;
}
return;
}

sErrors++;
if ( sErrors < 100 )
{
QgsMessageLog::logMessage( tr( "Map request failed [error:%1 url:%2]" ).arg( mCacheReply->errorString(), mCacheReply->url().toString() ), tr( "WCS" ) );
}
else if ( sErrors == 100 )
{
QgsMessageLog::logMessage( tr( "Not logging more than 100 request errors." ), tr( "WCS" ) );
sErrors++;
if ( sErrors < 100 )
{
QgsMessageLog::logMessage( tr( "Map request failed [error:%1 url:%2]" ).arg( mCacheReply->errorString(), mCacheReply->url().toString() ), tr( "WCS" ) );
}
else if ( sErrors == 100 )
{
QgsMessageLog::logMessage( tr( "Not logging more than 100 request errors." ), tr( "WCS" ) );
}
}

mCacheReply->deleteLater();
Expand All @@ -1904,3 +1910,13 @@ void QgsWcsDownloadHandler::cacheReplyProgress( qint64 bytesReceived, qint64 byt
Q_UNUSED( bytesTotal );
QgsDebugMsgLevel( tr( "%1 of %2 bytes of map downloaded." ).arg( bytesReceived ).arg( bytesTotal < 0 ? QString( "unknown number of" ) : QString::number( bytesTotal ) ), 3 );
}

void QgsWcsDownloadHandler::cancelled()
{
QgsDebugMsg( "Caught cancelled() signal" );
if ( mCacheReply )
{
QgsDebugMsg( "Aborting WCS network request" );
mCacheReply->abort();
}
}
7 changes: 4 additions & 3 deletions src/providers/wcs/qgswcsprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,12 @@ class QgsWcsProvider : public QgsRasterDataProvider, QgsGdalProviderBase
*/
QImage *draw( QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight ) override;

void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data ) override;
void readBlock( int bandNo, QgsRectangle const & viewExtent, int width, int height, void *data, QgsRasterBlockFeedback* feedback = nullptr ) override;

void readBlock( int theBandNo, int xBlock, int yBlock, void *block ) override;

/** Download cache */
void getCache( int bandNo, QgsRectangle const & viewExtent, int width, int height, QString crs = "" );
void getCache( int bandNo, QgsRectangle const & viewExtent, int width, int height, QString crs = "", QgsRasterBlockFeedback* feedback = nullptr );

/** Return the extent for this data layer
*/
Expand Down Expand Up @@ -419,14 +419,15 @@ class QgsWcsDownloadHandler : public QObject
{
Q_OBJECT
public:
QgsWcsDownloadHandler( const QUrl& url, QgsWcsAuthorization& auth, QNetworkRequest::CacheLoadControl cacheLoadControl, QByteArray& cachedData, const QString& wcsVersion, QgsError& cachedError );
QgsWcsDownloadHandler( const QUrl& url, QgsWcsAuthorization& auth, QNetworkRequest::CacheLoadControl cacheLoadControl, QByteArray& cachedData, const QString& wcsVersion, QgsError& cachedError, QgsRasterBlockFeedback* feedback );
~QgsWcsDownloadHandler();

void blockingDownload();

protected slots:
void cacheReplyFinished();
void cacheReplyProgress( qint64, qint64 );
void cancelled();

protected:
void finish() { QMetaObject::invokeMethod( mEventLoop, "quit", Qt::QueuedConnection ); }
Expand Down
2 changes: 2 additions & 0 deletions src/providers/wms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ SET (WMS_SRCS
qgswmssourceselect.cpp
qgswmsconnection.cpp
qgswmsdataitems.cpp
qgstilecache.cpp
qgstilescalewidget.cpp
qgswmtsdimensions.cpp
qgsxyzconnection.cpp
)
SET (WMS_MOC_HDRS
qgswmscapabilities.h
Expand Down
57 changes: 57 additions & 0 deletions src/providers/wms/qgstilecache.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/***************************************************************************
qgstilecache.h
--------------------------------------
Date : September 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. *
* *
***************************************************************************/

#include "qgstilecache.h"

#include "qgsnetworkaccessmanager.h"

#include <QAbstractNetworkCache>
#include <QImage>

QCache<QUrl, QImage> QgsTileCache::sTileCache( 256 );
QMutex QgsTileCache::sTileCacheMutex;


void QgsTileCache::insertTile( const QUrl& url, const QImage& image )
{
QMutexLocker locker( &sTileCacheMutex );
sTileCache.insert( url, new QImage( image ) );
}

bool QgsTileCache::tile( const QUrl& url, QImage& image )
{
QMutexLocker locker( &sTileCacheMutex );
if ( QImage* i = sTileCache.object( url ) )
{
image = *i;
return true;
}
else if ( QgsNetworkAccessManager::instance()->cache()->metaData( url ).isValid() )
{
if ( QIODevice* data = QgsNetworkAccessManager::instance()->cache()->data( url ) )
{
QByteArray imageData = data->readAll();
delete data;

image = QImage::fromData( imageData );

// cache it as well (mutex is already locked)
sTileCache.insert( url, new QImage( image ) );

return true;
}
}
return false;
}
56 changes: 56 additions & 0 deletions src/providers/wms/qgstilecache.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/***************************************************************************
qgstilecache.h
--------------------------------------
Date : September 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 QGSTILECACHE_H
#define QGSTILECACHE_H


#include <QCache>
#include <QMutex>

class QImage;
class QUrl;

/** A simple tile cache implementation. Tiles are cached according to their URL.
* There is a small in-memory cache and a secondary caching in the local disk.
* The in-memory cache is there to save CPU time otherwise wasted to read and
* uncompress data saved on the disk.
*
* The class is thread safe (its methods can be called from any thread).
*/
class QgsTileCache
{
public:

//! Add a tile image with given URL to the cache
static void insertTile( const QUrl& url, const QImage& image );

//! Try to access a tile and load it into "image" argument
//! @returns true if the tile exists in the cache
static bool tile( const QUrl& url, QImage& image );

//! how many tiles are stored in the in-memory cache
static int totalCost() { return sTileCache.totalCost(); }
//! how many tiles can be stored in the in-memory cache
static int maxCost() { return sTileCache.maxCost(); }

private:
//! in-memory cache
static QCache<QUrl, QImage> sTileCache;
//! mutex to protect the in-memory cache
static QMutex sTileCacheMutex;
};

#endif // QGSTILECACHE_H
136 changes: 133 additions & 3 deletions src/providers/wms/qgswmscapabilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,37 @@ bool QgsWmsSettings::parseUri( const QString& uriString )
QgsDataSourceURI uri;
uri.setEncodedUri( uriString );

mXyz = false; // assume WMS / WMTS

if ( uri.param( "type" ) == "xyz" )
{
// for XYZ tiles most of the things do not apply
mTiled = true;
mXyz = true;
mTileDimensionValues.clear();
mTileMatrixSetId = "tms0";
mMaxWidth = 0;
mMaxHeight = 0;
mHttpUri = uri.param( "url" );
mBaseUrl = mHttpUri;
mAuth.mUserName.clear();
mAuth.mPassword.clear();
mAuth.mReferer.clear();
mAuth.mAuthCfg.clear();
mIgnoreGetMapUrl = false;
mIgnoreGetFeatureInfoUrl = false;
mSmoothPixmapTransform = true;
mDpiMode = dpiNone; // does not matter what we set here
mActiveSubLayers = QStringList( "xyz" ); // just a placeholder to have one sub-layer
mActiveSubStyles = QStringList( "xyz" ); // just a placeholder to have one sub-style
mActiveSubLayerVisibility.clear();
mFeatureCount = 0;
mImageMimeType.clear();
mCrsId = "EPSG:3857";
mEnableContextualLegend = false;
return true;
}

mTiled = false;
mTileDimensionValues.clear();

Expand Down Expand Up @@ -1259,6 +1290,7 @@ void QgsWmsCapabilities::parseTileSetProfile( QDomElement const &e )
m.matrixWidth = ceil( l.boundingBoxes.at( 0 ).box.width() / m.tileWidth / r );
m.matrixHeight = ceil( l.boundingBoxes.at( 0 ).box.height() / m.tileHeight / r );
m.topLeft = QgsPoint( l.boundingBoxes.at( 0 ).box.xMinimum(), l.boundingBoxes.at( 0 ).box.yMinimum() + m.matrixHeight * m.tileHeight * r );
m.tres = r;
ms.tileMatrices.insert( r, m );
i++;
}
Expand Down Expand Up @@ -1341,17 +1373,19 @@ void QgsWmsCapabilities::parseWMTSContents( QDomElement const &e )
m.matrixWidth = e1.firstChildElement( "MatrixWidth" ).text().toInt();
m.matrixHeight = e1.firstChildElement( "MatrixHeight" ).text().toInt();

double res = m.scaleDenom * 0.00028 / metersPerUnit;
// the magic number below is "standardized rendering pixel size" defined
// in WMTS (and WMS 1.3) standard, being 0.28 pixel
m.tres = m.scaleDenom * 0.00028 / metersPerUnit;

QgsDebugMsg( QString( " %1: scale=%2 res=%3 tile=%4x%5 matrix=%6x%7 topLeft=%8" )
.arg( m.identifier )
.arg( m.scaleDenom ).arg( res )
.arg( m.scaleDenom ).arg( m.tres )
.arg( m.tileWidth ).arg( m.tileHeight )
.arg( m.matrixWidth ).arg( m.matrixHeight )
.arg( m.topLeft.toString() )
);

s.tileMatrices.insert( res, m );
s.tileMatrices.insert( m.tres, m );
}

mTileMatrixSets.insert( s.identifier, s );
Expand Down Expand Up @@ -1798,6 +1832,8 @@ bool QgsWmsCapabilities::detectTileLayerBoundingBox( QgsWmtsTileLayer& l )

const QgsWmtsTileMatrix& tm = *tmIt;
double metersPerUnit = QgsUnitTypes::fromUnitToUnitFactor( crs.mapUnits(), QGis::Meters );
// the magic number below is "standardized rendering pixel size" defined
// in WMTS (and WMS 1.3) standard, being 0.28 pixel
double res = tm.scaleDenom * 0.00028 / metersPerUnit;
QgsPoint bottomRight( tm.topLeft.x() + res * tm.tileWidth * tm.matrixWidth,
tm.topLeft.y() - res * tm.tileHeight * tm.matrixHeight );
Expand Down Expand Up @@ -2050,3 +2086,97 @@ void QgsWmsCapabilitiesDownload::capabilitiesReplyFinished()

emit downloadFinished();
}

QRectF QgsWmtsTileMatrix::tileRect( int col, int row ) const
{
double twMap = tileWidth * tres;
double thMap = tileHeight * tres;
return QRectF( topLeft.x() + col * twMap, topLeft.y() - ( row + 1 ) * thMap, twMap, thMap );
}

QgsRectangle QgsWmtsTileMatrix::tileBBox( int col, int row ) const
{
double twMap = tileWidth * tres;
double thMap = tileHeight * tres;
return QgsRectangle(
topLeft.x() + col * twMap,
topLeft.y() - ( row + 1 ) * thMap,
topLeft.x() + ( col + 1 ) * twMap,
topLeft.y() - row * thMap );
}

void QgsWmtsTileMatrix::viewExtentIntersection( const QgsRectangle &viewExtent, const QgsWmtsTileMatrixLimits* tml, int &col0, int &row0, int &col1, int &row1 ) const
{
double twMap = tileWidth * tres;
double thMap = tileHeight * tres;

int minTileCol = 0;
int maxTileCol = matrixWidth - 1;
int minTileRow = 0;
int maxTileRow = matrixHeight - 1;

if ( tml )
{
minTileCol = tml->minTileCol;
maxTileCol = tml->maxTileCol;
minTileRow = tml->minTileRow;
maxTileRow = tml->maxTileRow;
//QgsDebugMsg( QString( "%1 %2: TileMatrixLimits col %3-%4 row %5-%6" )
// .arg( tileMatrixSet->identifier, identifier )
// .arg( minTileCol ).arg( maxTileCol )
// .arg( minTileRow ).arg( maxTileRow ) );
}

col0 = qBound( minTileCol, ( int ) floor(( viewExtent.xMinimum() - topLeft.x() ) / twMap ), maxTileCol );
row0 = qBound( minTileRow, ( int ) floor(( topLeft.y() - viewExtent.yMaximum() ) / thMap ), maxTileRow );
col1 = qBound( minTileCol, ( int ) floor(( viewExtent.xMaximum() - topLeft.x() ) / twMap ), maxTileCol );
row1 = qBound( minTileRow, ( int ) floor(( topLeft.y() - viewExtent.yMinimum() ) / thMap ), maxTileRow );
}

const QgsWmtsTileMatrix* QgsWmtsTileMatrixSet::findNearestResolution( double vres ) const
{
QMap<double, QgsWmtsTileMatrix>::const_iterator prev, it = tileMatrices.constBegin();
while ( it != tileMatrices.constEnd() && it.key() < vres )
{
//QgsDebugMsg( QString( "res:%1 >= %2" ).arg( it.key() ).arg( vres ) );
prev = it;
++it;
}

if ( it == tileMatrices.constEnd() ||
( it != tileMatrices.constBegin() && vres - prev.key() < it.key() - vres ) )
{
//QgsDebugMsg( "back to previous res" );
it = prev;
}

return &it.value();
}

const QgsWmtsTileMatrix *QgsWmtsTileMatrixSet::findOtherResolution( double tres, int offset ) const
{
QMap<double, QgsWmtsTileMatrix>::const_iterator it = tileMatrices.constFind( tres );
if ( it == tileMatrices.constEnd() )
return nullptr;
while ( 1 )
{
if ( offset > 0 )
{
++it;
--offset;
}
else if ( offset < 0 )
{
if ( it == tileMatrices.constBegin() )
return nullptr;
--it;
++offset;
}
else
break;

if ( it == tileMatrices.constEnd() )
return nullptr;
}
return &it.value();
}
73 changes: 54 additions & 19 deletions src/providers/wms/qgswmscapabilities.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,30 +308,54 @@ struct QgsWmtsTheme
~QgsWmtsTheme() { delete subTheme; }
};

struct QgsWmtsTileMatrixLimits;

struct QgsWmtsTileMatrix
{
QString identifier;
QString title, abstract;
QStringList keywords;
double scaleDenom;
QgsPoint topLeft;
int tileWidth;
int tileHeight;
int matrixWidth;
int matrixHeight;
QgsPoint topLeft; //!< top-left corner of the tile matrix in map units
int tileWidth; //!< width of a tile in pixels
int tileHeight; //!< height of a tile in pixels
int matrixWidth; //!< number of tiles horizontally
int matrixHeight; //!< number of tiles vertically
double tres; //!< pixel span in map units

//! Returns extent of a tile in map coordinates.
//! (same function as tileBBox() but returns QRectF instead of QgsRectangle)
QRectF tileRect( int col, int row ) const;

//! Returns extent of a tile in map coordinates
//! (same function as tileRect() but returns QgsRectangle instead of QRectF)
QgsRectangle tileBBox( int col, int row ) const;

//! Returns range of tiles that intersects with the view extent
//! (tml may be null)
void viewExtentIntersection( const QgsRectangle& viewExtent, const QgsWmtsTileMatrixLimits* tml, int& col0, int& row0, int& col1, int& row1 ) const;

};

struct QgsWmtsTileMatrixSet
{
QString identifier;
QString title, abstract;
QStringList keywords;
QString crs;
QString wkScaleSet;
QString identifier; //!< tile matrix set identifier
QString title; //!< human readable tile matrix set name
QString abstract; //!< brief description of the tile matrix set
QStringList keywords; //!< list of words/phrases to describe the dataset
QString crs; //!< CRS of the tile matrix set
QString wkScaleSet; //!< optional reference to a well-known scale set
//! available tile matrixes (key = pixel span in map units)
QMap<double, QgsWmtsTileMatrix> tileMatrices;

//! Returns closest tile resolution to the requested one. (resolution = width [map units] / with [pixels])
const QgsWmtsTileMatrix* findNearestResolution( double vres ) const;

//! Return tile matrix for other near resolution from given tres (positive offset = lower resolution tiles)
const QgsWmtsTileMatrix* findOtherResolution( double tres, int offset ) const;
};

enum QgsTileMode { WMTS, WMSC };
enum QgsTileMode { WMTS, WMSC, XYZ };

struct QgsWmtsTileMatrixLimits
{
Expand Down Expand Up @@ -363,16 +387,22 @@ struct QgsWmtsStyle
QList<QgsWmtsLegendURL> legendURLs;
};

/**
* In case of multi-dimensional data, the service metadata can describe their multi-
* dimensionality and tiles can be requested at specific values in these dimensions.
* Examples of dimensions are Time, Elevation and Band.
*/
struct QgsWmtsDimension
{
QString identifier;
QString title, abstract;
QStringList keywords;
QString UOM;
QString unitSymbol;
QString defaultValue;
bool current;
QStringList values;
QString identifier; //!< name of the dimensional axis
QString title; //!< human readable name
QString abstract; //!< brief description of the dimension
QStringList keywords; //!< list of words/phrases to describe the dataset
QString UOM; //!< units of measure of dimensional axis
QString unitSymbol; //!< symbol of the units
QString defaultValue; //!< default value to be used if value is not specified in request
bool current; //!< indicates whether temporal data are normally kept current
QStringList values; //!< available values for this dimension
};

struct QgsWmtsTileLayer
Expand All @@ -385,6 +415,7 @@ struct QgsWmtsTileLayer
QStringList formats;
QStringList infoFormats;
QString defaultStyle;
//! available dimensions (optional, for multi-dimensional data)
QHash<QString, QgsWmtsDimension> dimensions;
QHash<QString, QgsWmtsStyle> styles;
QHash<QString, QgsWmtsTileMatrixSetLink> setLinks;
Expand Down Expand Up @@ -513,7 +544,11 @@ class QgsWmsSettings

//! layer is tiled, tile layer and active matrix set
bool mTiled;
//! whether we actually work with XYZ tiles instead of WMS / WMTS
bool mXyz;
//! chosen values for dimensions in case of multi-dimensional data (key=dim id, value=dim value)
QHash<QString, QString> mTileDimensionValues;
//! name of the chosen tile matrix set
QString mTileMatrixSetId;

/**
Expand Down
94 changes: 89 additions & 5 deletions src/providers/wms/qgswmsdataitems.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,17 @@

#include "qgslogger.h"

#include "qgsdataitemproviderregistry.h"
#include "qgsdatasourceuri.h"
#include "qgswmscapabilities.h"
#include "qgswmsconnection.h"
#include "qgswmssourceselect.h"
#include "qgsnewhttpconnection.h"
#include "qgstilescalewidget.h"
#include "qgscrscache.h"
#include "qgsxyzconnection.h"

#include <QInputDialog>

// ---------------------------------------------------------------------------
QgsWMSConnectionItem::QgsWMSConnectionItem( QgsDataItem* parent, QString name, QString path, QString uri )
Expand Down Expand Up @@ -424,12 +428,8 @@ QGISEXTERN QgsWMSSourceSelect * selectWidget( QWidget * parent, Qt::WindowFlags
return new QgsWMSSourceSelect( parent, fl );
}

QGISEXTERN int dataCapabilities()
{
return QgsDataProvider::Net;
}

QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem )
QgsDataItem* QgsWmsDataItemProvider::createDataItem( const QString& thePath, QgsDataItem *parentItem )
{
QgsDebugMsg( "thePath = " + thePath );
if ( thePath.isEmpty() )
Expand All @@ -451,3 +451,87 @@ QGISEXTERN QgsDataItem * dataItem( QString thePath, QgsDataItem* parentItem )
return nullptr;
}

QGISEXTERN QList<QgsDataItemProvider*> dataItemProviders()
{
return QList<QgsDataItemProvider*>()
<< new QgsWmsDataItemProvider
<< new QgsXyzTileDataItemProvider;
}

// ---------------------------------------------------------------------------


QgsXyzTileRootItem::QgsXyzTileRootItem( QgsDataItem *parent, QString name, QString path )
: QgsDataCollectionItem( parent, name, path )
{
mCapabilities |= Fast;
mIconName = "mIconWms.svg";
populate();
}

QVector<QgsDataItem *> QgsXyzTileRootItem::createChildren()
{
QVector<QgsDataItem*> connections;
Q_FOREACH ( const QString& connName, QgsXyzConnectionUtils::connectionList() )
{
QgsXyzConnection connection( QgsXyzConnectionUtils::connection( connName ) );
QgsDataItem * conn = new QgsXyzLayerItem( this, connName, mPath + '/' + connName, connection.encodedUri() );
connections.append( conn );
}
return connections;
}

QList<QAction *> QgsXyzTileRootItem::actions()
{
QAction* actionNew = new QAction( tr( "New Connection..." ), this );
connect( actionNew, SIGNAL( triggered() ), this, SLOT( newConnection() ) );
return QList<QAction*>() << actionNew;
}

void QgsXyzTileRootItem::newConnection()
{
QString url = QInputDialog::getText( nullptr, tr( "New XYZ tile layer" ),
tr( "Please enter XYZ tile layer URL. {x}, {y}, {z} will be replaced by actual tile coordinates." ) );
if ( url.isEmpty() )
return;

QString name = QInputDialog::getText( nullptr, tr( "New XYZ tile layer" ),
tr( "Please enter name of the tile layer:" ) );
if ( name.isEmpty() )
return;

QgsXyzConnection conn;
conn.name = name;
conn.url = url;
QgsXyzConnectionUtils::addConnection( conn );

refresh();
}


// ---------------------------------------------------------------------------


QgsXyzLayerItem::QgsXyzLayerItem( QgsDataItem *parent, QString name, QString path, const QString &encodedUri )
: QgsLayerItem( parent, name, path, encodedUri, QgsLayerItem::Raster, "wms" )
{
setState( Populated );
}

QList<QAction *> QgsXyzLayerItem::actions()
{
QList<QAction*> lst = QgsLayerItem::actions();

QAction* actionDelete = new QAction( tr( "Delete" ), this );
connect( actionDelete, SIGNAL( triggered() ), this, SLOT( deleteConnection() ) );
lst << actionDelete;

return lst;
}

void QgsXyzLayerItem::deleteConnection()
{
QgsXyzConnectionUtils::deleteConnection( mName );

mParent->refresh();
}
60 changes: 60 additions & 0 deletions src/providers/wms/qgswmsdataitems.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define QGSWMSDATAITEMS_H

#include "qgsdataitem.h"
#include "qgsdataitemprovider.h"
#include "qgsdatasourceuri.h"
#include "qgswmsprovider.h"

Expand Down Expand Up @@ -105,4 +106,63 @@ class QgsWMSRootItem : public QgsDataCollectionItem
void newConnection();
};


//! Provider for WMS root data item
class QgsWmsDataItemProvider : public QgsDataItemProvider
{
public:
virtual QString name() override { return "WMS"; }

virtual int capabilities() override { return QgsDataProvider::Net; }

virtual QgsDataItem* createDataItem( const QString& path, QgsDataItem* parentItem ) override;
};


//! Root item for XYZ tile layers
class QgsXyzTileRootItem : public QgsDataCollectionItem
{
Q_OBJECT
public:
QgsXyzTileRootItem( QgsDataItem* parent, QString name, QString path );

QVector<QgsDataItem*> createChildren() override;

virtual QList<QAction*> actions() override;

private slots:
void newConnection();
};

//! Item implementation for XYZ tile layers
class QgsXyzLayerItem : public QgsLayerItem
{
Q_OBJECT
public:
QgsXyzLayerItem( QgsDataItem* parent, QString name, QString path, const QString& encodedUri );

virtual QList<QAction*> actions() override;

public slots:
void deleteConnection();
};


//! Provider for XYZ root data item
class QgsXyzTileDataItemProvider : public QgsDataItemProvider
{
public:
virtual QString name() override { return "XYZ Tiles"; }

virtual int capabilities() override { return QgsDataProvider::Net; }

virtual QgsDataItem* createDataItem( const QString& path, QgsDataItem* parentItem ) override
{
if ( path.isEmpty() )
return new QgsXyzTileRootItem( parentItem, "Tile Server (XYZ)", "xyz:" );
return nullptr;
}
};


#endif // QGSWMSDATAITEMS_H
Loading