Skip to content

Commit

Permalink
[vectortiles] Fix labels disappear randomly between map redraws
Browse files Browse the repository at this point in the history
Fixes #37450
  • Loading branch information
nyalldawson committed Sep 1, 2020
1 parent 5a2bc75 commit 9f83b41
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 20 deletions.
4 changes: 2 additions & 2 deletions python/core/auto_generated/labeling/qgspallabeling.sip.in
Expand Up @@ -782,9 +782,9 @@ class QgsPalLabeling
%End
public:

static bool staticWillUseLayer( QgsVectorLayer *layer );
static bool staticWillUseLayer( const QgsMapLayer *layer );
%Docstring
called to find out whether the layer is used for labeling
Called to find out whether a specified ``layer`` is used for labeling.

.. versionadded:: 2.4
%End
Expand Down
32 changes: 30 additions & 2 deletions src/core/labeling/qgspallabeling.cpp
Expand Up @@ -67,6 +67,8 @@
#include "qgsgeometrycollection.h"
#include "callouts/qgscallout.h"
#include "callouts/qgscalloutsregistry.h"
#include "qgsvectortilelayer.h"
#include "qgsvectortilebasiclabeling.h"
#include <QMessageBox>

using namespace pal;
Expand Down Expand Up @@ -3533,9 +3535,35 @@ void QgsPalLayerSettings::parseDropShadow( QgsRenderContext &context )
// -------------


bool QgsPalLabeling::staticWillUseLayer( QgsVectorLayer *layer )
bool QgsPalLabeling::staticWillUseLayer( const QgsMapLayer *layer )
{
return layer->labelsEnabled() || layer->diagramsEnabled();
switch ( layer->type() )
{
case QgsMapLayerType::VectorLayer:
{
const QgsVectorLayer *vl = qobject_cast< const QgsVectorLayer * >( layer );
return vl->labelsEnabled() || vl->diagramsEnabled();
}

case QgsMapLayerType::VectorTileLayer:
{
const QgsVectorTileLayer *vl = qobject_cast< const QgsVectorTileLayer * >( layer );
if ( !vl->labeling() )
return false;

if ( const QgsVectorTileBasicLabeling *labeling = dynamic_cast< const QgsVectorTileBasicLabeling *>( vl->labeling() ) )
return !labeling->styles().empty();

return false;
}

case QgsMapLayerType::RasterLayer:
case QgsMapLayerType::PluginLayer:
case QgsMapLayerType::MeshLayer:
case QgsMapLayerType::AnnotationLayer:
return false;
}
return false;
}


Expand Down
4 changes: 2 additions & 2 deletions src/core/labeling/qgspallabeling.h
Expand Up @@ -1263,10 +1263,10 @@ class CORE_EXPORT QgsPalLabeling
public:

/**
* called to find out whether the layer is used for labeling
* Called to find out whether a specified \a layer is used for labeling.
* \since QGIS 2.4
*/
static bool staticWillUseLayer( QgsVectorLayer *layer );
static bool staticWillUseLayer( const QgsMapLayer *layer );

//! \note not available in Python bindings
static void drawLabelCandidateRect( pal::LabelPosition *lp, QPainter *painter, const QgsMapToPixel *xform, QList<QgsLabelCandidate> *candidates = nullptr ) SIP_SKIP;
Expand Down
45 changes: 34 additions & 11 deletions src/core/qgsmaprendererjob.cpp
Expand Up @@ -31,6 +31,7 @@
#include "qgspallabeling.h"
#include "qgsvectorlayerrenderer.h"
#include "qgsvectorlayer.h"
#include "qgsvectortilelayer.h"
#include "qgsexception.h"
#include "qgslabelingengine.h"
#include "qgsmaplayerlistutils.h"
Expand Down Expand Up @@ -95,16 +96,39 @@ bool QgsMapRendererJob::prepareLabelCache() const
// calculate which layers will be labeled
QSet< QgsMapLayer * > labeledLayers;
const QList<QgsMapLayer *> layers = mSettings.layers();
for ( const QgsMapLayer *ml : layers )
for ( QgsMapLayer *ml : layers )
{
QgsVectorLayer *vl = const_cast< QgsVectorLayer * >( qobject_cast<const QgsVectorLayer *>( ml ) );
if ( vl && QgsPalLabeling::staticWillUseLayer( vl ) )
labeledLayers << vl;
if ( vl && vl->labelsEnabled() && vl->labeling()->requiresAdvancedEffects() )
if ( QgsPalLabeling::staticWillUseLayer( ml ) )
labeledLayers << ml;

switch ( ml->type() )
{
canCache = false;
break;
case QgsMapLayerType::VectorLayer:
{
QgsVectorLayer *vl = qobject_cast< QgsVectorLayer *>( ml );
if ( vl->labelsEnabled() && vl->labeling()->requiresAdvancedEffects() )
{
canCache = false;
}
break;
}

case QgsMapLayerType::VectorTileLayer:
{
// TODO -- add detection of advanced labeling effects for vector tile layers
break;
}

case QgsMapLayerType::RasterLayer:
case QgsMapLayerType::AnnotationLayer:
case QgsMapLayerType::PluginLayer:
case QgsMapLayerType::MeshLayer:
break;
}

if ( !canCache )
break;

}

if ( mCache && mCache->hasCacheImage( LABEL_CACHE_ID ) )
Expand Down Expand Up @@ -331,11 +355,10 @@ LayerRenderJobs QgsMapRendererJob::prepareJobs( QPainter *painter, QgsLabelingEn

// Force render of layers that are being edited
// or if there's a labeling engine that needs the layer to register features
if ( mCache && vl )
if ( mCache )
{
bool requiresLabeling = false;
requiresLabeling = ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer( vl ) ) && requiresLabelRedraw;
if ( vl->isEditable() || requiresLabeling )
const bool requiresLabeling = ( labelingEngine2 && QgsPalLabeling::staticWillUseLayer( ml ) ) && requiresLabelRedraw;
if ( ( vl && vl->isEditable() ) || requiresLabeling )
{
mCache->clearCacheImage( ml->id() );
}
Expand Down
24 changes: 21 additions & 3 deletions tests/src/python/test_qgspallabeling_base.py
Expand Up @@ -43,7 +43,11 @@
QgsVectorLayer,
QgsVectorLayerSimpleLabeling,
QgsMultiRenderChecker,
QgsUnitTypes
QgsUnitTypes,
QgsVectorTileLayer,
QgsVectorTileBasicLabelingStyle,
QgsWkbTypes,
QgsVectorTileBasicLabeling
)

from qgis.testing import start_app, unittest
Expand Down Expand Up @@ -277,8 +281,8 @@ def controlImagePath(self, grpprefix=''):
def saveControlImage(self, tmpimg=''):
# don't save control images for RenderVsOtherOutput (Vs) tests, since
# those control images belong to a different test result
if ('PAL_CONTROL_IMAGE' not in os.environ or
'Vs' in self._TestGroup):
if ('PAL_CONTROL_IMAGE' not in os.environ
or 'Vs' in self._TestGroup):
return
imgpath = self.controlImagePath()
testdir = os.path.dirname(imgpath)
Expand Down Expand Up @@ -399,6 +403,20 @@ def test_layer_pal_activated(self):
msg = '\nLayer labeling not activated, as reported by labelingEngine'
self.assertTrue(QgsPalLabeling.staticWillUseLayer(self.layer), msg)

# also test for vector tile layer
tile_layer = QgsVectorTileLayer('x', 'y')
self.assertFalse(QgsPalLabeling.staticWillUseLayer(tile_layer))

st = QgsVectorTileBasicLabelingStyle()
st.setStyleName("st1")
st.setLayerName("place")
st.setFilterExpression("rank = 1 AND class = 'country'")
st.setGeometryType(QgsWkbTypes.PointGeometry)
labeling = QgsVectorTileBasicLabeling()
labeling.setStyles([st])
tile_layer.setLabeling(labeling)
self.assertTrue(QgsPalLabeling.staticWillUseLayer(tile_layer))

def test_write_read_settings(self):
# Verify written PAL settings are same when read from layer
# load and write default test settings
Expand Down

0 comments on commit 9f83b41

Please sign in to comment.