Skip to content
Permalink
Browse files

[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.
  • Loading branch information
mhugent authored and nyalldawson committed Jul 14, 2017
1 parent bf94df8 commit 2d531e5814c1b43a23c36d09ace9a5f5729e5bb4
Showing with 116 additions and 1 deletion.
  1. +67 −0 src/gui/qgsmapcanvas.cpp
  2. +8 −0 src/gui/qgsmapcanvas.h
  3. +34 −1 src/gui/qgsmapcanvasmap.cpp
  4. +7 −0 src/gui/qgsmapcanvasmap.h
@@ -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,16 +44,45 @@ 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 ) );
// This happens on zoom events when ::paint is called before
// 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

0 comments on commit 2d531e5

Please sign in to comment.
You can’t perform that action at this time.