Skip to content

Commit

Permalink
When the canvas temporal range is changed, we need to invalidate any
Browse files Browse the repository at this point in the history
cached images we have of temporal enabled layers

Otherwise these will not be re-rendered using the update temporal range
when the canvas is redrawn (and we want to avoid a full map redraw during
animations, which is expensive, so we want to be able to reuse as many
cached images as possible!)
  • Loading branch information
nyalldawson committed Mar 6, 2020
1 parent c307f0e commit 9bbfe81
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 0 deletions.
7 changes: 7 additions & 0 deletions python/core/auto_generated/qgsmaprenderercache.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@ Returns a list of map layers on which an image in the cache depends.
Removes an image from the cache with matching ``cacheKey``.

.. seealso:: :py:func:`clear`
%End

void invalidateCacheForLayer( QgsMapLayer *layer );
%Docstring
Invalidates cached images which relate to the specified map ``layer``.

.. versionadded:: 3.14
%End

};
Expand Down
7 changes: 7 additions & 0 deletions src/core/qgsmaprenderercache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,15 @@ QList< QgsMapLayer * > QgsMapRendererCache::dependentLayers( const QString &cach
return QList< QgsMapLayer * >();
}


void QgsMapRendererCache::layerRequestedRepaint()
{
QgsMapLayer *layer = qobject_cast<QgsMapLayer *>( sender() );
invalidateCacheForLayer( layer );
}

void QgsMapRendererCache::invalidateCacheForLayer( QgsMapLayer *layer )
{
if ( !layer )
return;

Expand Down Expand Up @@ -175,3 +181,4 @@ void QgsMapRendererCache::clearCacheImage( const QString &cacheKey )
mCachedImages.remove( cacheKey );
dropUnusedConnections();
}

7 changes: 7 additions & 0 deletions src/core/qgsmaprenderercache.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ class CORE_EXPORT QgsMapRendererCache : public QObject
*/
void clearCacheImage( const QString &cacheKey );

/**
* Invalidates cached images which relate to the specified map \a layer.
*
* \since QGIS 3.14
*/
void invalidateCacheForLayer( QgsMapLayer *layer );

private slots:
//! Remove layer (that emitted the signal) from the cache
void layerRequestedRepaint();
Expand Down
14 changes: 14 additions & 0 deletions src/gui/qgsmapcanvas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ email : sherman at mrcc.com
#include "qgscustomdrophandler.h"
#include "qgsreferencedgeometry.h"
#include "qgsprojectviewsettings.h"
#include "qgsmaplayertemporalproperties.h"

/**
* \ingroup gui
Expand Down Expand Up @@ -734,6 +735,19 @@ void QgsMapCanvas::setTemporalRange( const QgsDateTimeRange &dateTimeRange )
return;

mSettings.setTemporalRange( dateTimeRange );

if ( mCache )
{
// we need to discard any previously cached images which have temporal properties enabled, so that these will be updated when
// the canvas is redrawn
const QList<QgsMapLayer *> layers;
for ( QgsMapLayer *layer : layers )
{
if ( layer->temporalProperties() && layer->temporalProperties()->isActive() )
mCache->invalidateCacheForLayer( layer );
}
}

emit temporalRangeChanged();
}

Expand Down
21 changes: 21 additions & 0 deletions tests/src/python/test_qgsmaprenderercache.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,27 @@ def testRequestRepaintSimple(self):
layer.triggerRepaint(True)
self.assertFalse(cache.hasCacheImage('xxx'))

def testInvalidateCacheForLayer(self):
""" test invalidating the cache for a layer """
layer = QgsVectorLayer("Point?field=fldtxt:string",
"layer", "memory")
QgsProject.instance().addMapLayers([layer])
self.assertTrue(layer.isValid())

# add image to cache
cache = QgsMapRendererCache()
im = QImage(200, 200, QImage.Format_RGB32)
cache.setCacheImage('xxx', im, [layer])
self.assertFalse(cache.cacheImage('xxx').isNull())
self.assertTrue(cache.hasCacheImage('xxx'))

# invalidate cache for layer
cache.invalidateCacheForLayer(layer)
# cache image should be cleared
self.assertTrue(cache.cacheImage('xxx').isNull())
self.assertFalse(cache.hasCacheImage('xxx'))
QgsProject.instance().removeMapLayer(layer.id())

def testRequestRepaintMultiple(self):
""" test requesting repaint with multiple dependent layers """
layer1 = QgsVectorLayer("Point?field=fldtxt:string",
Expand Down

0 comments on commit 9bbfe81

Please sign in to comment.