[FEATURE] Preview of WMTS layers + add XYZ tile layers (PR #3473)

This introduces live preview when rendering WMTS layers - as soon as individual tiles are loaded, they are shown in map canvas... no need to wait with a blank map until all tiles are fully downloaded. Additionally, if there are already locally cached tiles of other zoom levels, they may be used in the preview while the tiles with best matching zoom level are being downloaded. This greatly improves the user experience when working with WMTS layers.

Additionally, I have added native support for XYZ tile layers into WMS provider (based on existing implementation of WMTS tiling). This allows loading of various new raster tile sources (e.g. OpenStreetMap tiles) that were before available only with QuickMapServices or OpenLayers plugins. To use XYZ tile layers, open the browser dock in QGIS and look for "Tile Servers (XYZ)" root entry. Right-clicking will open a menu to add connections. For example for OpenStreetMap the URL would be http://c.tile.openstreetmap.org/{z}/{x}/{y}.png

The work on WMTS live preview has been funded by Land Information New Zealand.

The work on XYZ tile layers has been funded by Lutra Consulting.
This commit is contained in:
Martin Dobias 2016-09-13 23:37:05 +08:00 committed by GitHub
commit 717e7162f3
30 changed files with 1545 additions and 715 deletions

View File

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

View File

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

View File

@ -9,7 +9,30 @@ class QgsRasterBlockFeedback : QgsFeedback
%TypeHeaderCode
#include <qgsrasterinterface.h>
%End
// TODO: extend with preview functionality??
public:
//! Construct a new raster block feedback object
QgsRasterBlockFeedback( QObject* parent = nullptr );
//! May be emitted by raster data provider to indicate that some partial data are available
//! and a new preview image may be produced
virtual void onNewData();
//! Whether the raster provider should return only data that are already available
//! without waiting for full result. By default this flag is not enabled.
//! @see setPreviewOnly()
bool isPreviewOnly() const;
//! set flag whether the block request is for preview purposes only
//! @see isPreviewOnly()
void setPreviewOnly( bool preview );
//! Whether our painter is drawing to a temporary image used just by this layer
//! @see setRenderPartialOutput()
bool renderPartialOutput() const;
//! Set whether our painter is drawing to a temporary image used just by this layer
//! @see renderPartialOutput()
void setRenderPartialOutput( bool enable );
};
@ -256,5 +279,10 @@ class QgsRasterInterface
int theStats = QgsRasterBandStats::All,
const QgsRectangle & theExtent = QgsRectangle(),
int theBinCount = 0 );
private:
QgsRasterInterface(const QgsRasterInterface &);
QgsRasterInterface &operator=(const QgsRasterInterface &);
};

View File

@ -1,6 +1,10 @@
/** Raster projector */
/** \ingroup core
* \brief QgsRasterProjector implements approximate projection support for
* it calculates grid of points in source CRS for target CRS + extent
* which are used to calculate affine transformation matrices.
* \class QgsRasterProjector
*/
class QgsRasterProjector : QgsRasterInterface
{
%TypeHeaderCode
@ -18,33 +22,6 @@ class QgsRasterProjector : QgsRasterInterface
Exact, //!< Exact, precise but slow
};
/** \brief QgsRasterProjector implements approximate projection support for
* it calculates grid of points in source CRS for target CRS + extent
* which are used to calculate affine transformation matrices.
*/
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
int theSrcDatumTransform,
int theDestDatumTransform,
const QgsRectangle& theDestExtent,
int theDestRows, int theDestCols,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
const QgsRectangle& theDestExtent,
int theDestRows, int theDestCols,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);
QgsRasterProjector();
/** \brief The destructor */
@ -66,9 +43,6 @@ class QgsRasterProjector : QgsRasterInterface
/** \brief Get destination CRS */
QgsCoordinateReferenceSystem destinationCrs() const;
/** \brief set maximum source resolution */
void setMaxSrcRes( double theMaxSrcXRes, double theMaxSrcYRes );
Precision precision() const;
void setPrecision( Precision precision );
// Translated precision mode, for use in ComboBox etc.

View File

@ -38,6 +38,7 @@
#include "qgsrenderer.h"
#include "qgsrendererregistry.h"
#include "qgsmaplayerregistry.h"
#include "qgsrasterdataprovider.h"
#include "qgsrasterlayer.h"
#include "qgsmaplayerconfigwidget.h"
#include "qgsmaplayerstylemanagerwidget.h"
@ -171,10 +172,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 )
@ -392,21 +397,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:

View File

@ -21,6 +21,8 @@
#include "qgslogger.h"
#include "qgsproviderregistry.h"
typedef QList<QgsDataItemProvider*> dataItemProviders_t();
/**
* \ingroup core
@ -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 )
{

View File

@ -234,7 +234,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;

View File

@ -333,6 +333,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;
}

View File

@ -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 3.0
// TODO: ignore scale-based visibility (overview)
};
Q_DECLARE_FLAGS( Flags, Flag )

View File

@ -129,6 +129,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() );

View File

@ -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 3.0
};
Q_DECLARE_FLAGS( Flags, Flag )

View File

@ -88,10 +88,20 @@ 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() )

View File

@ -36,7 +36,36 @@
*/
class CORE_EXPORT QgsRasterBlockFeedback : public QgsFeedback
{
// TODO: extend with preview functionality??
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;
};
@ -258,6 +287,9 @@ class CORE_EXPORT QgsRasterInterface
int theStats = QgsRasterBandStats::All,
const QgsRectangle & theExtent = QgsRectangle(),
int theBinCount = 0 );
private:
Q_DISABLE_COPY( QgsRasterInterface ) // there is clone() for copying
};
#endif

View File

@ -29,7 +29,7 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer* layer, QgsRender
, mRasterViewPort( nullptr )
, mPipe( nullptr )
, mContext( rendererContext )
, mFeedback( new QgsRasterBlockFeedback() )
, mFeedback( new Feedback( this ) )
{
mPainter = rendererContext.painter();
const QgsMapToPixel& theQgsMapToPixel = rendererContext.mapToPixel();
@ -226,3 +226,35 @@ 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, &feedback );
QgsDebugMsg( QString( "total raster preview time: %1 ms" ).arg( t.elapsed() ) );
mLastPreview = QTime::currentTime();
}

View File

@ -27,6 +27,11 @@ class QgsRasterPipe;
struct QgsRasterViewPort;
class QgsRenderContext;
class QgsRasterLayerRenderer;
#include "qgsrasterinterface.h"
/** \ingroup core
* Implementation of threaded rendering for raster layers.
*
@ -52,7 +57,28 @@ class QgsRasterLayerRenderer : public QgsMapLayerRenderer
QgsRasterPipe* mPipe;
QgsRenderContext& mContext;
QgsRasterBlockFeedback* mFeedback;
/** \ingroup core
* Specific internal feedback class to provide preview of raster layer rendering.
* @note added in 3.0
* @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

View File

@ -23,172 +23,23 @@
#include "qgscoordinatetransform.h"
#include "qgscsexception.h"
QgsRasterProjector::QgsRasterProjector(
const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
int theSrcDatumTransform,
int theDestDatumTransform,
const QgsRectangle& theDestExtent,
int theDestRows, int theDestCols,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent )
: QgsRasterInterface( nullptr )
, mSrcCRS( theSrcCRS )
, mDestCRS( theDestCRS )
, mSrcDatumTransform( theSrcDatumTransform )
, mDestDatumTransform( theDestDatumTransform )
, mDestExtent( theDestExtent )
, mExtent( theExtent )
, mDestRows( theDestRows ), mDestCols( theDestCols )
, pHelperTop( nullptr ), pHelperBottom( nullptr )
, mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes )
, mPrecision( Approximate )
, mApproximate( true )
{
QgsDebugMsgLevel( "Entered", 4 );
QgsDebugMsgLevel( "theDestExtent = " + theDestExtent.toString(), 4 );
calc();
}
QgsRasterProjector::QgsRasterProjector(
const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
const QgsRectangle& theDestExtent,
int theDestRows, int theDestCols,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent )
: QgsRasterInterface( nullptr )
, mSrcCRS( theSrcCRS )
, mDestCRS( theDestCRS )
, mSrcDatumTransform( -1 )
, mDestDatumTransform( -1 )
, mDestExtent( theDestExtent )
, mExtent( theExtent )
, mDestRows( theDestRows ), mDestCols( theDestCols )
, pHelperTop( nullptr ), pHelperBottom( nullptr )
, mMaxSrcXRes( theMaxSrcXRes ), mMaxSrcYRes( theMaxSrcYRes )
, mPrecision( Approximate )
, mApproximate( false )
{
QgsDebugMsgLevel( "Entered", 4 );
QgsDebugMsgLevel( "theDestExtent = " + theDestExtent.toString(), 4 );
calc();
}
QgsRasterProjector::QgsRasterProjector(
const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent )
: QgsRasterInterface( nullptr )
, mSrcCRS( theSrcCRS )
, mDestCRS( theDestCRS )
, mSrcDatumTransform( -1 )
, mDestDatumTransform( -1 )
, mExtent( theExtent )
, mDestRows( 0 )
, mDestCols( 0 )
, mDestXRes( 0.0 )
, mDestYRes( 0.0 )
, mSrcRows( 0 )
, mSrcCols( 0 )
, mSrcXRes( 0.0 )
, mSrcYRes( 0.0 )
, mDestRowsPerMatrixRow( 0.0 )
, mDestColsPerMatrixCol( 0.0 )
, pHelperTop( nullptr ), pHelperBottom( nullptr )
, mHelperTopRow( 0 )
, mCPCols( 0 )
, mCPRows( 0 )
, mSqrTolerance( 0.0 )
, mMaxSrcXRes( theMaxSrcXRes )
, mMaxSrcYRes( theMaxSrcYRes )
, mPrecision( Approximate )
, mApproximate( false )
{
QgsDebugMsgLevel( "Entered", 4 );
}
QgsRasterProjector::QgsRasterProjector()
: QgsRasterInterface( nullptr )
, mSrcDatumTransform( -1 )
, mDestDatumTransform( -1 )
, mDestRows( 0 )
, mDestCols( 0 )
, mDestXRes( 0.0 )
, mDestYRes( 0.0 )
, mSrcRows( 0 )
, mSrcCols( 0 )
, mSrcXRes( 0.0 )
, mSrcYRes( 0.0 )
, mDestRowsPerMatrixRow( 0.0 )
, mDestColsPerMatrixCol( 0.0 )
, pHelperTop( nullptr )
, pHelperBottom( nullptr )
, mHelperTopRow( 0 )
, mCPCols( 0 )
, mCPRows( 0 )
, mSqrTolerance( 0.0 )
, mMaxSrcXRes( 0 )
, mMaxSrcYRes( 0 )
, mPrecision( Approximate )
, mApproximate( false )
{
QgsDebugMsgLevel( "Entered", 4 );
}
QgsRasterProjector::QgsRasterProjector( const QgsRasterProjector &projector )
: QgsRasterInterface( nullptr )
, pHelperTop( nullptr )
, pHelperBottom( nullptr )
, mHelperTopRow( 0 )
, mCPCols( 0 )
, mCPRows( 0 )
, mSqrTolerance( 0 )
, mApproximate( false )
{
mSrcCRS = projector.mSrcCRS;
mDestCRS = projector.mDestCRS;
mSrcDatumTransform = projector.mSrcDatumTransform;
mDestDatumTransform = projector.mDestDatumTransform;
mMaxSrcXRes = projector.mMaxSrcXRes;
mMaxSrcYRes = projector.mMaxSrcYRes;
mExtent = projector.mExtent;
mDestRows = projector.mDestRows;
mDestCols = projector.mDestCols;
mDestXRes = projector.mDestXRes;
mDestYRes = projector.mDestYRes;
mSrcRows = projector.mSrcRows;
mSrcCols = projector.mSrcCols;
mSrcXRes = projector.mSrcXRes;
mSrcYRes = projector.mSrcYRes;
mDestRowsPerMatrixRow = projector.mDestRowsPerMatrixRow;
mDestColsPerMatrixCol = projector.mDestColsPerMatrixCol;
mPrecision = projector.mPrecision;
}
QgsRasterProjector & QgsRasterProjector::operator=( const QgsRasterProjector & projector )
{
if ( &projector != this )
{
mSrcCRS = projector.mSrcCRS;
mDestCRS = projector.mDestCRS;
mSrcDatumTransform = projector.mSrcDatumTransform;
mDestDatumTransform = projector.mDestDatumTransform;
mMaxSrcXRes = projector.mMaxSrcXRes;
mMaxSrcYRes = projector.mMaxSrcYRes;
mExtent = projector.mExtent;
mPrecision = projector.mPrecision;
}
return *this;
}
QgsRasterProjector* QgsRasterProjector::clone() const
{
QgsDebugMsgLevel( "Entered", 4 );
QgsRasterProjector * projector = new QgsRasterProjector( mSrcCRS, mDestCRS, mMaxSrcXRes, mMaxSrcYRes, mExtent );
QgsRasterProjector * projector = new QgsRasterProjector;
projector->mSrcCRS = mSrcCRS;
projector->mDestCRS = mDestCRS;
projector->mSrcDatumTransform = mSrcDatumTransform;
projector->mDestDatumTransform = mDestDatumTransform;
projector->mPrecision = mPrecision;
@ -197,8 +48,6 @@ QgsRasterProjector* QgsRasterProjector::clone() const
QgsRasterProjector::~QgsRasterProjector()
{
delete[] pHelperTop;
delete[] pHelperBottom;
}
int QgsRasterProjector::bandCount() const
@ -215,6 +64,10 @@ Qgis::DataType QgsRasterProjector::dataType( int bandNo ) const
return Qgis::UnknownDataType;
}
/// @cond PRIVATE
void QgsRasterProjector::setCrs( const QgsCoordinateReferenceSystem & theSrcCRS, const QgsCoordinateReferenceSystem & theDestCRS, int srcDatumTransform, int destDatumTransform )
{
mSrcCRS = theSrcCRS;
@ -223,22 +76,36 @@ void QgsRasterProjector::setCrs( const QgsCoordinateReferenceSystem & theSrcCRS,
mDestDatumTransform = destDatumTransform;
}
void QgsRasterProjector::calc()
ProjectorData::ProjectorData( const QgsRectangle& extent, int width, int height, QgsRasterInterface* input, const QgsCoordinateTransform& inverseCt, QgsRasterProjector::Precision precision )
: mApproximate( false )
, mInverseCt( new QgsCoordinateTransform( inverseCt ) )
, mDestExtent( extent )
, mDestRows( height )
, mDestCols( width )
, mDestXRes( 0.0 )
, mDestYRes( 0.0 )
, mSrcRows( 0 )
, mSrcCols( 0 )
, mSrcXRes( 0.0 )
, mSrcYRes( 0.0 )
, mDestRowsPerMatrixRow( 0.0 )
, mDestColsPerMatrixCol( 0.0 )
, pHelperTop( nullptr )
, pHelperBottom( nullptr )
, mHelperTopRow( 0 )
, mCPCols( 0 )
, mCPRows( 0 )
, mSqrTolerance( 0.0 )
, mMaxSrcXRes( 0 )
, mMaxSrcYRes( 0 )
{
QgsDebugMsgLevel( "Entered", 4 );
mCPMatrix.clear();
mCPLegalMatrix.clear();
delete[] pHelperTop;
pHelperTop = nullptr;
delete[] pHelperBottom;
pHelperBottom = nullptr;
// Get max source resolution and extent if possible
mMaxSrcXRes = 0;
mMaxSrcYRes = 0;
if ( mInput )
if ( input )
{
QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( mInput->sourceInput() );
QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider*>( input->sourceInput() );
if ( provider )
{
if ( provider->capabilities() & QgsRasterDataProvider::Size )
@ -264,9 +131,7 @@ void QgsRasterProjector::calc()
double myDestRes = mDestXRes < mDestYRes ? mDestXRes : mDestYRes;
mSqrTolerance = myDestRes * myDestRes;
QgsCoordinateTransform inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform );
if ( mPrecision == Approximate )
if ( precision == QgsRasterProjector::Approximate )
{
mApproximate = true;
}
@ -345,7 +210,15 @@ void QgsRasterProjector::calc()
mSrcXRes = mSrcExtent.width() / mSrcCols;
}
void QgsRasterProjector::calcSrcExtent()
ProjectorData::~ProjectorData()
{
delete[] pHelperTop;
delete[] pHelperBottom;
delete mInverseCt;
}
void ProjectorData::calcSrcExtent()
{
/* Run around the mCPMatrix and find source extent */
// Attention, source limits are not necessarily on destination edges, e.g.
@ -409,7 +282,7 @@ void QgsRasterProjector::calcSrcExtent()
QgsDebugMsgLevel( "mSrcExtent = " + mSrcExtent.toString(), 4 );
}
QString QgsRasterProjector::cpToString()
QString ProjectorData::cpToString()
{
QString myString;
for ( int i = 0; i < mCPRows; i++ )
@ -434,7 +307,7 @@ QString QgsRasterProjector::cpToString()
return myString;
}
void QgsRasterProjector::calcSrcRowsCols()
void ProjectorData::calcSrcRowsCols()
{
// Wee need to calculate minimum cell size in the source
// TODO: Think it over better, what is the right source resolution?
@ -473,11 +346,10 @@ void QgsRasterProjector::calcSrcRowsCols()
else
{
// take highest from corners, points in in the middle of corners and center (3 x 3 )
QgsCoordinateTransform inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform );
//double
QgsRectangle srcExtent;
int srcXSize, srcYSize;
if ( extentSize( inverseCt, mDestExtent, mDestCols, mDestRows, srcExtent, srcXSize, srcYSize ) )
if ( QgsRasterProjector::extentSize( *mInverseCt, mDestExtent, mDestCols, mDestRows, srcExtent, srcXSize, srcYSize ) )
{
double srcXRes = srcExtent.width() / srcXSize;
double srcYRes = srcExtent.height() / srcYSize;
@ -509,29 +381,22 @@ void QgsRasterProjector::calcSrcRowsCols()
}
inline void QgsRasterProjector::destPointOnCPMatrix( int theRow, int theCol, double *theX, double *theY )
inline void ProjectorData::destPointOnCPMatrix( int theRow, int theCol, double *theX, double *theY )
{
*theX = mDestExtent.xMinimum() + theCol * mDestExtent.width() / ( mCPCols - 1 );
*theY = mDestExtent.yMaximum() - theRow * mDestExtent.height() / ( mCPRows - 1 );
}
inline int QgsRasterProjector::matrixRow( int theDestRow )
inline int ProjectorData::matrixRow( int theDestRow )
{
return static_cast< int >( floor(( theDestRow + 0.5 ) / mDestRowsPerMatrixRow ) );
}
inline int QgsRasterProjector::matrixCol( int theDestCol )
inline int ProjectorData::matrixCol( int theDestCol )
{
return static_cast< int >( floor(( theDestCol + 0.5 ) / mDestColsPerMatrixCol ) );
}
QgsPoint QgsRasterProjector::srcPoint( int theDestRow, int theCol )
{
Q_UNUSED( theDestRow );
Q_UNUSED( theCol );
return QgsPoint();
}
void QgsRasterProjector::calcHelper( int theMatrixRow, QgsPoint *thePoints )
void ProjectorData::calcHelper( int theMatrixRow, QgsPoint *thePoints )
{
// TODO?: should we also precalc dest cell center coordinates for x and y?
for ( int myDestCol = 0; myDestCol < mDestCols; myDestCol++ )
@ -556,7 +421,8 @@ void QgsRasterProjector::calcHelper( int theMatrixRow, QgsPoint *thePoints )
thePoints[myDestCol].setY( t );
}
}
void QgsRasterProjector::nextHelper()
void ProjectorData::nextHelper()
{
// We just switch pHelperTop and pHelperBottom, memory is not lost
QgsPoint *tmp;
@ -567,7 +433,7 @@ void QgsRasterProjector::nextHelper()
mHelperTopRow++;
}
bool QgsRasterProjector::srcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol, const QgsCoordinateTransform& ct )
bool ProjectorData::srcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
{
if ( mApproximate )
{
@ -575,11 +441,11 @@ bool QgsRasterProjector::srcRowCol( int theDestRow, int theDestCol, int *theSrcR
}
else
{
return preciseSrcRowCol( theDestRow, theDestCol, theSrcRow, theSrcCol, ct );
return preciseSrcRowCol( theDestRow, theDestCol, theSrcRow, theSrcCol );
}
}
bool QgsRasterProjector::preciseSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol, const QgsCoordinateTransform& ct )
bool ProjectorData::preciseSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
{
#ifdef QGISDEBUG
QgsDebugMsgLevel( QString( "theDestRow = %1" ).arg( theDestRow ), 5 );
@ -595,9 +461,9 @@ bool QgsRasterProjector::preciseSrcRowCol( int theDestRow, int theDestCol, int *
QgsDebugMsgLevel( QString( "x = %1 y = %2" ).arg( x ).arg( y ), 5 );
#endif
if ( ct.isValid() )
if ( mInverseCt->isValid() )
{
ct.transformInPlace( x, y, z );
mInverseCt->transformInPlace( x, y, z );
}
#ifdef QGISDEBUG
@ -628,7 +494,7 @@ bool QgsRasterProjector::preciseSrcRowCol( int theDestRow, int theDestCol, int *
return true;
}
bool QgsRasterProjector::approximateSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
bool ProjectorData::approximateSrcRowCol( int theDestRow, int theDestCol, int *theSrcRow, int *theSrcCol )
{
int myMatrixRow = matrixRow( theDestRow );
int myMatrixCol = matrixCol( theDestCol );
@ -685,7 +551,7 @@ bool QgsRasterProjector::approximateSrcRowCol( int theDestRow, int theDestCol, i
return true;
}
void QgsRasterProjector::insertRows( const QgsCoordinateTransform& ct )
void ProjectorData::insertRows( const QgsCoordinateTransform& ct )
{
for ( int r = 0; r < mCPRows - 1; r++ )
{
@ -709,7 +575,7 @@ void QgsRasterProjector::insertRows( const QgsCoordinateTransform& ct )
}
}
void QgsRasterProjector::insertCols( const QgsCoordinateTransform& ct )
void ProjectorData::insertCols( const QgsCoordinateTransform& ct )
{
for ( int r = 0; r < mCPRows; r++ )
{
@ -727,7 +593,7 @@ void QgsRasterProjector::insertCols( const QgsCoordinateTransform& ct )
}
void QgsRasterProjector::calcCP( int theRow, int theCol, const QgsCoordinateTransform& ct )
void ProjectorData::calcCP( int theRow, int theCol, const QgsCoordinateTransform& ct )
{
double myDestX, myDestY;
destPointOnCPMatrix( theRow, theCol, &myDestX, &myDestY );
@ -752,7 +618,7 @@ void QgsRasterProjector::calcCP( int theRow, int theCol, const QgsCoordinateTran
}
}
bool QgsRasterProjector::calcRow( int theRow, const QgsCoordinateTransform& ct )
bool ProjectorData::calcRow( int theRow, const QgsCoordinateTransform& ct )
{
QgsDebugMsgLevel( QString( "theRow = %1" ).arg( theRow ), 3 );
for ( int i = 0; i < mCPCols; i++ )
@ -763,7 +629,7 @@ bool QgsRasterProjector::calcRow( int theRow, const QgsCoordinateTransform& ct )
return true;
}
bool QgsRasterProjector::calcCol( int theCol, const QgsCoordinateTransform& ct )
bool ProjectorData::calcCol( int theCol, const QgsCoordinateTransform& ct )
{
QgsDebugMsgLevel( QString( "theCol = %1" ).arg( theCol ), 3 );
for ( int i = 0; i < mCPRows; i++ )
@ -774,7 +640,7 @@ bool QgsRasterProjector::calcCol( int theCol, const QgsCoordinateTransform& ct )
return true;
}
bool QgsRasterProjector::checkCols( const QgsCoordinateTransform& ct )
bool ProjectorData::checkCols( const QgsCoordinateTransform& ct )
{
if ( !ct.isValid() )
{
@ -819,7 +685,7 @@ bool QgsRasterProjector::checkCols( const QgsCoordinateTransform& ct )
return true;
}
bool QgsRasterProjector::checkRows( const QgsCoordinateTransform& ct )
bool ProjectorData::checkRows( const QgsCoordinateTransform& ct )
{
if ( !ct.isValid() )
{
@ -864,6 +730,9 @@ bool QgsRasterProjector::checkRows( const QgsCoordinateTransform& ct )
return true;
}
/// @endcond
QString QgsRasterProjector::precisionLabel( Precision precision )
{
switch ( precision )
@ -892,22 +761,21 @@ QgsRasterBlock * QgsRasterProjector::block( int bandNo, QgsRectangle const & ex
return mInput->block( bandNo, extent, width, height, feedback );
}
mDestExtent = extent;
mDestRows = height;
mDestCols = width;
calc();
QgsCoordinateTransform inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform );
QgsDebugMsgLevel( QString( "srcExtent:\n%1" ).arg( srcExtent().toString() ), 4 );
QgsDebugMsgLevel( QString( "srcCols = %1 srcRows = %2" ).arg( srcCols() ).arg( srcRows() ), 4 );
ProjectorData pd( extent, width, height, mInput, inverseCt, mPrecision );
QgsDebugMsgLevel( QString( "srcExtent:\n%1" ).arg( pd.srcExtent().toString() ), 4 );
QgsDebugMsgLevel( QString( "srcCols = %1 srcRows = %2" ).arg( pd.srcCols() ).arg( pd.srcRows() ), 4 );
// If we zoom out too much, projector srcRows / srcCols maybe 0, which can cause problems in providers
if ( srcRows() <= 0 || srcCols() <= 0 )
if ( pd.srcRows() <= 0 || pd.srcCols() <= 0 )
{
QgsDebugMsgLevel( "Zero srcRows or srcCols", 4 );
return new QgsRasterBlock();
}
QgsRasterBlock *inputBlock = mInput->block( bandNo, srcExtent(), srcCols(), srcRows(), feedback );
QgsRasterBlock *inputBlock = mInput->block( bandNo, pd.srcExtent(), pd.srcCols(), pd.srcRows(), feedback );
if ( !inputBlock || inputBlock->isEmpty() )
{
QgsDebugMsg( "No raster data!" );
@ -948,12 +816,6 @@ QgsRasterBlock * QgsRasterProjector::block( int bandNo, QgsRectangle const & ex
// we cannot fill output block with no data because we use memcpy for data, not setValue().
bool doNoData = !QgsRasterBlock::typeIsNumeric( inputBlock->dataType() ) && inputBlock->hasNoData() && !inputBlock->hasNoDataValue();
QgsCoordinateTransform inverseCt;
if ( !mApproximate )
{
inverseCt = QgsCoordinateTransformCache::instance()->transform( mDestCRS.authid(), mSrcCRS.authid(), mDestDatumTransform, mSrcDatumTransform );
}
outputBlock->setIsNoData();
int srcRow, srcCol;
@ -961,10 +823,10 @@ QgsRasterBlock * QgsRasterProjector::block( int bandNo, QgsRectangle const & ex
{
for ( int j = 0; j < width; ++j )
{
bool inside = srcRowCol( i, j, &srcRow, &srcCol, inverseCt );
bool inside = pd.srcRowCol( i, j, &srcRow, &srcCol );
if ( !inside ) continue; // we have everything set to no data
qgssize srcIndex = static_cast< qgssize >( srcRow ) * mSrcCols + srcCol;
qgssize srcIndex = static_cast< qgssize >( srcRow ) * pd.srcCols() + srcCol;
QgsDebugMsgLevel( QString( "row = %1 col = %2 srcRow = %3 srcCol = %4" ).arg( i ).arg( j ).arg( srcRow ).arg( srcCol ), 5 );
// isNoData() may be slow so we check doNoData first

View File

@ -36,6 +36,9 @@ class QgsPoint;
class QgsCoordinateTransform;
/** \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
@ -50,44 +53,11 @@ 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.
*/
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
int theSrcDatumTransform,
int theDestDatumTransform,
const QgsRectangle& theDestExtent,
int theDestRows, int theDestCols,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
const QgsRectangle& theDestExtent,
int theDestRows, int theDestCols,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);
QgsRasterProjector( const QgsCoordinateReferenceSystem& theSrcCRS,
const QgsCoordinateReferenceSystem& theDestCRS,
double theMaxSrcXRes, double theMaxSrcYRes,
const QgsRectangle& theExtent
);
QgsRasterProjector();
/** \brief Copy constructor */
// To avoid synthesized which fails on copy of QgsCoordinateTransform
// (QObject child) in Python bindings
QgsRasterProjector( const QgsRasterProjector &projector );
/** \brief The destructor */
~QgsRasterProjector();
QgsRasterProjector & operator=( const QgsRasterProjector &projector );
QgsRasterProjector *clone() const override;
int bandCount() const override;
@ -104,13 +74,6 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
/** \brief Get destination CRS */
QgsCoordinateReferenceSystem destinationCrs() const { return mDestCRS; }
/** \brief set maximum source resolution */
void setMaxSrcRes( double theMaxSrcXRes, double theMaxSrcYRes )
{
mMaxSrcXRes = theMaxSrcXRes;
mMaxSrcYRes = theMaxSrcYRes;
}
Precision precision() const { return mPrecision; }
void setPrecision( Precision precision ) { mPrecision = precision; }
// Translated precision mode, for use in ComboBox etc.
@ -128,24 +91,49 @@ 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 );
@ -153,18 +141,12 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
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 );
@ -203,17 +185,12 @@ class CORE_EXPORT QgsRasterProjector : public QgsRasterInterface
/** Get mCPMatrix as string */
QString cpToString();
/** Source CRS */
QgsCoordinateReferenceSystem mSrcCRS;
/** Use approximation (requested precision is Approximate and it is possible to calculate
* an approximation matrix with a sufficient precision) */
bool mApproximate;
/** Destination CRS */
QgsCoordinateReferenceSystem mDestCRS;
/** Source datum transformation id (or -1 if none) */
int mSrcDatumTransform;
/** Destination datum transformation id (or -1 if none) */
int mDestDatumTransform;
/** Transformation from destination CRS to source CRS */
QgsCoordinateTransform* mInverseCt;
/** Destination extent */
QgsRectangle mDestExtent;
@ -284,13 +261,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

View File

@ -143,6 +143,7 @@ QgsMapCanvas::QgsMapCanvas( QWidget * parent )
mSettings.setFlag( QgsMapSettings::DrawEditingInfo );
mSettings.setFlag( QgsMapSettings::UseRenderingOptimization );
mSettings.setFlag( QgsMapSettings::RenderPartialOutput );
//segmentation parameters
QSettings settings;

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -40,6 +40,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();
@ -1258,6 +1289,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++;
}
@ -1340,17 +1372,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 );
@ -1797,6 +1831,8 @@ bool QgsWmsCapabilities::detectTileLayerBoundingBox( QgsWmtsTileLayer& l )
const QgsWmtsTileMatrix& tm = *tmIt;
double metersPerUnit = QgsUnitTypes::fromUnitToUnitFactor( crs.mapUnits(), QgsUnitTypes::DistanceMeters );
// 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 );
@ -2049,3 +2085,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();
}

View File

@ -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
{
@ -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
@ -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;
@ -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;
/**

View File

@ -16,12 +16,16 @@
#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 "qgsxyzconnection.h"
#include <QInputDialog>
// ---------------------------------------------------------------------------
QgsWMSConnectionItem::QgsWMSConnectionItem( QgsDataItem* parent, QString name, QString path, QString uri )
@ -423,12 +427,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() )
@ -450,3 +450,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();
}

View File

@ -16,6 +16,7 @@
#define QGSWMSDATAITEMS_H
#include "qgsdataitem.h"
#include "qgsdataitemprovider.h"
#include "qgsdatasourceuri.h"
#include "qgswmsprovider.h"
@ -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

File diff suppressed because it is too large Load Diff

View File

@ -348,6 +348,30 @@ class QgsWmsProvider : public QgsRasterDataProvider
*/
static QString prepareUri( QString uri );
//! Helper struct for tile requests
struct TileRequest
{
TileRequest( const QUrl& u, const QRectF& r, int i )
: url( u )
, rect( r )
, index( i )
{}
QUrl url;
QRectF rect;
int index;
};
typedef QList<TileRequest> TileRequests;
//! Tile identifier within a tile source
typedef struct TilePosition
{
TilePosition( int r, int c ): row( r ), col( c ) {}
bool operator==( const TilePosition& other ) const { return row == other.row && col == other.col; }
int row;
int col;
} TilePosition;
typedef QList<TilePosition> TilePositions;
signals:
/** \brief emit a signal to notify of a progress event */
@ -363,6 +387,9 @@ class QgsWmsProvider : public QgsRasterDataProvider
private:
//! In case of XYZ tile layer, setup capabilities from its URI
void setupXyzCapabilities( const QString& uri );
QImage *draw( QgsRectangle const & viewExtent, int pixelWidth, int pixelHeight, QgsRasterBlockFeedback* feedback );
/**
@ -424,6 +451,21 @@ class QgsWmsProvider : public QgsRasterDataProvider
private:
QUrl createRequestUrlWMS( const QgsRectangle& viewExtent, int pixelWidth, int pixelHeight );
void createTileRequestsWMSC( const QgsWmtsTileMatrix* tm, const QgsWmsProvider::TilePositions& tiles, QgsWmsProvider::TileRequests& requests );
void createTileRequestsWMTS( const QgsWmtsTileMatrix* tm, const QgsWmsProvider::TilePositions& tiles, QgsWmsProvider::TileRequests& requests );
void createTileRequestsXYZ( const QgsWmtsTileMatrix* tm, const QgsWmsProvider::TilePositions& tiles, QgsWmsProvider::TileRequests& requests );
//! Helper structure to store a cached tile image with its rectangle
typedef struct TileImage
{
TileImage( QRectF r, QImage i ): rect( r ), img( i ) {}
QRectF rect; //!< destination rectangle for a tile (in screen coordinates)
QImage img; //!< cached tile to be drawn
} TileImage;
//! Get tiles from a different resolution to cover the missing areas
void fetchOtherResTiles( QgsTileMode tileMode, const QgsRectangle& viewExtent, int imageWidth, QList<QRectF>& missing, double tres, int resOffset, QList<TileImage> &otherResTiles );
/** Return the full url to request legend graphic
* The visibleExtent isi only used if provider supports contextual
* legends according to the QgsWmsSettings
@ -488,13 +530,6 @@ class QgsWmsProvider : public QgsRasterDataProvider
*/
QString mImageCrs;
/**
* The previously retrieved image from the WMS server.
* This can be reused if draw() is called consecutively
* with the same parameters.
*/
QImage *mCachedImage;
/**
* The reply to the capabilities request
*/
@ -510,13 +545,6 @@ class QgsWmsProvider : public QgsRasterDataProvider
// TODO: better
QString mIdentifyResultXsd;
/**
* The previous parameters to draw().
*/
QgsRectangle mCachedViewExtent;
int mCachedViewWidth;
int mCachedViewHeight;
/**
* The error caption associated with the last WMS error.
*/
@ -582,6 +610,8 @@ class QgsWmsImageDownloadHandler : public QObject
QImage* mCachedImage;
QEventLoop* mEventLoop;
QgsRasterBlockFeedback* mFeedback;
};
@ -591,19 +621,7 @@ class QgsWmsTiledImageDownloadHandler : public QObject
Q_OBJECT
public:
struct TileRequest
{
TileRequest( const QUrl& u, const QRectF& r, int i )
: url( u )
, rect( r )
, index( i )
{}
QUrl url;
QRectF rect;
int index;
};
QgsWmsTiledImageDownloadHandler( const QString& providerUri, const QgsWmsAuthorization& auth, int reqNo, const QList<TileRequest>& requests, QImage* cachedImage, const QgsRectangle& cachedViewExtent, bool smoothPixmapTransform, QgsRasterBlockFeedback* feedback );
QgsWmsTiledImageDownloadHandler( const QString& providerUri, const QgsWmsAuthorization& auth, int reqNo, const QgsWmsProvider::TileRequests& requests, QImage* image, const QgsRectangle& viewExtent, bool smoothPixmapTransform, QgsRasterBlockFeedback* feedback );
~QgsWmsTiledImageDownloadHandler();
void downloadBlocking();
@ -628,8 +646,8 @@ class QgsWmsTiledImageDownloadHandler : public QObject
QgsWmsAuthorization mAuth;
QImage* mCachedImage;
QgsRectangle mCachedViewExtent;
QImage* mImage;
QgsRectangle mViewExtent;
QEventLoop* mEventLoop;
@ -638,6 +656,8 @@ class QgsWmsTiledImageDownloadHandler : public QObject
//! Running tile requests
QList<QNetworkReply*> mReplies;
QgsRasterBlockFeedback* mFeedback;
};

View File

@ -0,0 +1,59 @@
/***************************************************************************
qgsxyzconnection.h
---------------------
begin : August 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 "qgsxyzconnection.h"
#include "qgsdatasourceuri.h"
#include <QSettings>
QString QgsXyzConnection::encodedUri() const
{
QgsDataSourceUri uri;
uri.setParam( "type", "xyz" );
uri.setParam( "url", url );
return uri.encodedUri();
}
QStringList QgsXyzConnectionUtils::connectionList()
{
QSettings settings;
settings.beginGroup( "/Qgis/connections-xyz" );
return settings.childGroups();
}
QgsXyzConnection QgsXyzConnectionUtils::connection( const QString &name )
{
QSettings settings;
settings.beginGroup( "/Qgis/connections-xyz/" + name );
QgsXyzConnection conn;
conn.name = name;
conn.url = settings.value( "url" ).toString();
return conn;
}
void QgsXyzConnectionUtils::deleteConnection( const QString& name )
{
QSettings settings;
settings.remove( "/Qgis/connections-xyz/" + name );
}
void QgsXyzConnectionUtils::addConnection( const QgsXyzConnection &conn )
{
QSettings settings;
settings.beginGroup( "/Qgis/connections-xyz/" + conn.name );
settings.setValue( "url", conn.url );
}

View File

@ -0,0 +1,47 @@
/***************************************************************************
qgsxyzconnection.h
---------------------
begin : August 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 QGSXYZCONNECTION_H
#define QGSXYZCONNECTION_H
#include <QStringList>
struct QgsXyzConnection
{
QString name;
QString url;
QString encodedUri() const;
};
/** Utility class for handling list of connections to XYZ tile layers */
class QgsXyzConnectionUtils
{
public:
//! Returns list of existing connections
static QStringList connectionList();
//! Returns connection details
static QgsXyzConnection connection( const QString& name );
//! Removes a connection from the list
static void deleteConnection( const QString& name );
//! Adds a new connection to the list
static void addConnection( const QgsXyzConnection& conn );
};
#endif // QGSXYZCONNECTION_H