mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
[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:
commit
717e7162f3
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 &);
|
||||
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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 )
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 )
|
||||
|
@ -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() );
|
||||
|
@ -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 )
|
||||
|
||||
|
@ -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() )
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -143,6 +143,7 @@ QgsMapCanvas::QgsMapCanvas( QWidget * parent )
|
||||
|
||||
mSettings.setFlag( QgsMapSettings::DrawEditingInfo );
|
||||
mSettings.setFlag( QgsMapSettings::UseRenderingOptimization );
|
||||
mSettings.setFlag( QgsMapSettings::RenderPartialOutput );
|
||||
|
||||
//segmentation parameters
|
||||
QSettings settings;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
57
src/providers/wms/qgstilecache.cpp
Normal file
57
src/providers/wms/qgstilecache.cpp
Normal 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;
|
||||
}
|
56
src/providers/wms/qgstilecache.h
Normal file
56
src/providers/wms/qgstilecache.h
Normal 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
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
59
src/providers/wms/qgsxyzconnection.cpp
Normal file
59
src/providers/wms/qgsxyzconnection.cpp
Normal 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 );
|
||||
}
|
47
src/providers/wms/qgsxyzconnection.h
Normal file
47
src/providers/wms/qgsxyzconnection.h
Normal 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
|
Loading…
x
Reference in New Issue
Block a user