mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-04 00:06:46 -05:00
[FEATURE] First experiment with preloading
After main map canvas render is complete, render a separate image for each adjacent map "tile" (in the background). These are shown when panning the map to display a preview of what will be visible when the panning operation ends.
This commit is contained in:
parent
bf94df83f1
commit
2d531e5814
@ -503,6 +503,7 @@ void QgsMapCanvas::refreshMap()
|
||||
QgsDebugMsgLevel( "CANVAS refresh!", 3 );
|
||||
|
||||
stopRendering(); // if any...
|
||||
stopPreviewJobs();
|
||||
|
||||
//build the expression context
|
||||
QgsExpressionContext expressionContext;
|
||||
@ -624,6 +625,7 @@ void QgsMapCanvas::rendererJobFinished()
|
||||
p.end();
|
||||
|
||||
mMap->setContent( img, imageRect( img, mSettings ) );
|
||||
startPreviewJobs();
|
||||
}
|
||||
|
||||
// now we are in a slot called from mJob - do not delete it immediately
|
||||
@ -634,6 +636,20 @@ void QgsMapCanvas::rendererJobFinished()
|
||||
emit mapCanvasRefreshed();
|
||||
}
|
||||
|
||||
void QgsMapCanvas::previewJobFinished()
|
||||
{
|
||||
QgsMapRendererQImageJob *job = qobject_cast<QgsMapRendererQImageJob *>( sender() );
|
||||
if ( !job )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( mMap )
|
||||
{
|
||||
mMap->addPreviewImage( job->renderedImage(), job->mapSettings().extent() );
|
||||
}
|
||||
}
|
||||
|
||||
QgsRectangle QgsMapCanvas::imageRect( const QImage &img, const QgsMapSettings &mapSettings )
|
||||
{
|
||||
// This is a hack to pass QgsMapCanvasItem::setRect what it
|
||||
@ -2109,3 +2125,54 @@ const QgsLabelingEngineSettings &QgsMapCanvas::labelingEngineSettings() const
|
||||
{
|
||||
return mSettings.labelingEngineSettings();
|
||||
}
|
||||
|
||||
void QgsMapCanvas::startPreviewJobs()
|
||||
{
|
||||
stopPreviewJobs(); //just in case still running
|
||||
|
||||
QgsRectangle mapRect = mSettings.visibleExtent();
|
||||
|
||||
for ( int j = 0; j < 3; ++j )
|
||||
{
|
||||
for ( int i = 0; i < 3; ++i )
|
||||
{
|
||||
if ( i == 1 && j == 1 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
//copy settings, only update extent
|
||||
QgsMapSettings jobSettings = mSettings;
|
||||
|
||||
double dx = ( i - 1 ) * mapRect.width();
|
||||
double dy = ( 1 - j ) * mapRect.height();
|
||||
QgsRectangle jobExtent = mapRect;
|
||||
jobExtent.setXMaximum( jobExtent.xMaximum() + dx );
|
||||
jobExtent.setXMinimum( jobExtent.xMinimum() + dx );
|
||||
jobExtent.setYMaximum( jobExtent.yMaximum() + dy );
|
||||
jobExtent.setYMinimum( jobExtent.yMinimum() + dy );
|
||||
|
||||
jobSettings.setExtent( jobExtent );
|
||||
|
||||
QgsMapRendererQImageJob *job = new QgsMapRendererParallelJob( jobSettings );
|
||||
mPreviewJobs.append( job );
|
||||
connect( job, SIGNAL( finished() ), this, SLOT( previewJobFinished() ) );
|
||||
job->start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QgsMapCanvas::stopPreviewJobs()
|
||||
{
|
||||
QList< QgsMapRendererQImageJob * >::iterator it = mPreviewJobs.begin();
|
||||
for ( ; it != mPreviewJobs.end(); ++it )
|
||||
{
|
||||
if ( *it )
|
||||
{
|
||||
( *it )->cancel();
|
||||
}
|
||||
delete ( *it );
|
||||
}
|
||||
mPreviewJobs.clear();
|
||||
}
|
||||
|
||||
@ -586,6 +586,9 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
|
||||
//! called when a renderer job has finished successfully or when it was canceled
|
||||
void rendererJobFinished();
|
||||
|
||||
//! called when a preview job has been finished
|
||||
void previewJobFinished();
|
||||
|
||||
void mapUpdateTimeout();
|
||||
|
||||
void refreshMap();
|
||||
@ -820,6 +823,8 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
|
||||
|
||||
QgsSnappingUtils *mSnappingUtils = nullptr;
|
||||
|
||||
QList< QgsMapRendererQImageJob * > mPreviewJobs;
|
||||
|
||||
//! lock the scale, so zooming can be performed using magnication
|
||||
bool mScaleLocked;
|
||||
|
||||
@ -868,6 +873,9 @@ class GUI_EXPORT QgsMapCanvas : public QGraphicsView
|
||||
|
||||
void setLayersPrivate( const QList<QgsMapLayer *> &layers );
|
||||
|
||||
void startPreviewJobs();
|
||||
void stopPreviewJobs();
|
||||
|
||||
friend class TestQgsMapCanvas;
|
||||
|
||||
}; // class QgsMapCanvas
|
||||
|
||||
@ -16,7 +16,9 @@
|
||||
#include "qgslogger.h"
|
||||
#include "qgsmapcanvas.h"
|
||||
#include "qgsmapcanvasmap.h"
|
||||
#include "qgsmaprendererjob.h"
|
||||
#include "qgsmapsettings.h"
|
||||
#include "qgsmaplayer.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
@ -30,6 +32,8 @@ QgsMapCanvasMap::QgsMapCanvasMap( QgsMapCanvas *canvas )
|
||||
|
||||
void QgsMapCanvasMap::setContent( const QImage &image, const QgsRectangle &rect )
|
||||
{
|
||||
mPreviewImages.clear();
|
||||
|
||||
mImage = image;
|
||||
|
||||
// For true retro fans: this is approximately how the graphics looked like in 1990
|
||||
@ -40,9 +44,23 @@ void QgsMapCanvasMap::setContent( const QImage &image, const QgsRectangle &rect
|
||||
setRect( rect );
|
||||
}
|
||||
|
||||
void QgsMapCanvasMap::addPreviewImage( const QImage &image, const QgsRectangle &rect )
|
||||
{
|
||||
mPreviewImages.append( qMakePair( image, rect ) );
|
||||
update();
|
||||
}
|
||||
|
||||
QRectF QgsMapCanvasMap::boundingRect() const
|
||||
{
|
||||
double width = mItemSize.width();
|
||||
double height = mItemSize.height();
|
||||
|
||||
return QRectF( -width, -height, 3 * width, 3 * height );
|
||||
}
|
||||
|
||||
void QgsMapCanvasMap::paint( QPainter *painter )
|
||||
{
|
||||
int w = qRound( boundingRect().width() ) - 2, h = qRound( boundingRect().height() ) - 2; // setRect() makes the size +2 :-(
|
||||
int w = qRound( mItemSize.width() ) - 2, h = qRound( mItemSize.height() ) - 2; // setRect() makes the size +2 :-(
|
||||
if ( mImage.size() != QSize( w, h ) )
|
||||
{
|
||||
QgsDebugMsg( QString( "map paint DIFFERENT SIZE: img %1,%2 item %3,%4" ).arg( mImage.width() ).arg( mImage.height() ).arg( w ).arg( h ) );
|
||||
@ -50,6 +68,21 @@ void QgsMapCanvasMap::paint( QPainter *painter )
|
||||
// the renderer has completed
|
||||
}
|
||||
|
||||
/*Offset between 0/0 and mRect.xMinimum/mRect.yMinimum.
|
||||
We need to consider the offset, because mRect is not updated yet and there might be an offset*/
|
||||
QgsPoint pt = toMapCoordinates( QPoint( 0, 0 ) );
|
||||
double offsetX = pt.x() - mRect.xMinimum();
|
||||
double offsetY = pt.y() - mRect.yMaximum();
|
||||
|
||||
//draw preview images first
|
||||
QMap< QgsRectangle, QImage >::const_iterator previewIt = mPreviewImages.constBegin();
|
||||
for ( ; previewIt != mPreviewImages.constEnd(); ++previewIt )
|
||||
{
|
||||
QPointF ul = toCanvasCoordinates( QgsPoint( previewIt.key().xMinimum() + offsetX, previewIt.key().yMaximum() + offsetY ) );
|
||||
QPointF lr = toCanvasCoordinates( QgsPoint( previewIt.key().xMaximum() + offsetX, previewIt.key().yMinimum() + offsetY ) );
|
||||
painter->drawImage( QRectF( ul.x(), ul.y(), lr.x() - ul.x(), lr.y() - ul.y() ), previewIt.value(), QRect( 0, 0, previewIt.value().width(), previewIt.value().height() ) );
|
||||
}
|
||||
|
||||
painter->drawImage( QRect( 0, 0, w, h ), mImage );
|
||||
|
||||
// For debugging:
|
||||
|
||||
@ -45,9 +45,16 @@ class QgsMapCanvasMap : public QgsMapCanvasItem
|
||||
|
||||
virtual void paint( QPainter *painter ) override;
|
||||
|
||||
void addPreviewImage( const QImage &image, const QgsRectangle &rect );
|
||||
|
||||
QRectF boundingRect() const;
|
||||
|
||||
private:
|
||||
|
||||
QImage mImage;
|
||||
|
||||
//! Preview images for panning. Usually cover area around the rendered image
|
||||
QList< QPair< QImage, QgsRectangle > > mPreviewImages;
|
||||
};
|
||||
|
||||
/// @endcond
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user