Merge pull request #6679 from pblottiere/bugfix_will_render_feature

[bugfix] Fixes identify action on deactivated rules for QgsRuleBasedRenderer
This commit is contained in:
Blottiere Paul 2018-03-30 09:08:11 +01:00 committed by GitHub
commit 4be8baa683
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 12 deletions

View File

@ -324,9 +324,14 @@ Returns which legend keys match the feature
.. versionadded:: 2.14
%End
QgsRuleBasedRenderer::RuleList rulesForFeature( QgsFeature &feat, QgsRenderContext *context = 0 );
QgsRuleBasedRenderer::RuleList rulesForFeature( QgsFeature &feat, QgsRenderContext *context = 0, bool onlyActive = true );
%Docstring
tell which rules will be used to render the feature
Returns the list of rules used to render the feature in a specific
context.
:param feat: The feature for which rules have to be find
:param context: The rendering context
:param onlyActive: True to search for active rules only, false otherwise
%End
void stopRender( QgsRenderContext &context );
@ -411,7 +416,7 @@ Sets if this rule is an ELSE rule
:param iselse: If true, this rule is an ELSE rule
%End
bool isElse();
bool isElse() const;
%Docstring
Check if this rule is an ELSE rule

View File

@ -541,13 +541,26 @@ bool QgsRuleBasedRenderer::Rule::willRenderFeature( QgsFeature &feat, QgsRenderC
{
if ( !isFilterOK( feat, context ) )
return false;
if ( mSymbol )
return true;
Q_FOREACH ( Rule *rule, mActiveChildren )
{
if ( rule->willRenderFeature( feat, context ) )
if ( rule->isElse() )
{
RuleList lst = rulesForFeature( feat, context, false );
lst.removeOne( rule );
if ( lst.empty() )
{
return true;
}
}
else if ( !rule->isElse( ) && rule->willRenderFeature( feat, context ) )
{
return true;
}
}
return false;
}
@ -576,12 +589,31 @@ QSet<QString> QgsRuleBasedRenderer::Rule::legendKeysForFeature( QgsFeature &feat
Q_FOREACH ( Rule *rule, mActiveChildren )
{
lst.unite( rule->legendKeysForFeature( feat, context ) );
bool validKey = false;
if ( rule->isElse() )
{
RuleList lst = rulesForFeature( feat, context, false );
lst.removeOne( rule );
if ( lst.empty() )
{
validKey = true;
}
}
else if ( !rule->isElse( ) && rule->willRenderFeature( feat, context ) )
{
validKey = true;
}
if ( validKey )
{
lst.unite( rule->legendKeysForFeature( feat, context ) );
}
}
return lst;
}
QgsRuleBasedRenderer::RuleList QgsRuleBasedRenderer::Rule::rulesForFeature( QgsFeature &feat, QgsRenderContext *context )
QgsRuleBasedRenderer::RuleList QgsRuleBasedRenderer::Rule::rulesForFeature( QgsFeature &feat, QgsRenderContext *context, bool onlyActive )
{
RuleList lst;
if ( !isFilterOK( feat, context ) )
@ -590,9 +622,13 @@ QgsRuleBasedRenderer::RuleList QgsRuleBasedRenderer::Rule::rulesForFeature( QgsF
if ( mSymbol )
lst.append( this );
Q_FOREACH ( Rule *rule, mActiveChildren )
RuleList listChildren = children();
if ( onlyActive )
listChildren = mActiveChildren;
Q_FOREACH ( Rule *rule, listChildren )
{
lst += rule->rulesForFeature( feat, context );
lst += rule->rulesForFeature( feat, context, onlyActive );
}
return lst;
}

View File

@ -342,8 +342,15 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer
*/
QSet< QString > legendKeysForFeature( QgsFeature &feat, QgsRenderContext *context = nullptr );
//! tell which rules will be used to render the feature
QgsRuleBasedRenderer::RuleList rulesForFeature( QgsFeature &feat, QgsRenderContext *context = nullptr );
/**
* Returns the list of rules used to render the feature in a specific
* context.
*
* \param feat The feature for which rules have to be find
* \param context The rendering context
* \param onlyActive True to search for active rules only, false otherwise
*/
QgsRuleBasedRenderer::RuleList rulesForFeature( QgsFeature &feat, QgsRenderContext *context = nullptr, bool onlyActive = true );
/**
* Stop a rendering process. Used to clean up the internal state of this rule
@ -419,7 +426,7 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer
*
* \returns True if this rule is an else rule
*/
bool isElse() { return mElseRule; }
bool isElse() const { return mElseRule; }
protected:
void initFilter();

View File

@ -40,7 +40,8 @@ from qgis.core import (QgsVectorLayer,
QgsRendererCategory,
QgsCategorizedSymbolRenderer,
QgsGraduatedSymbolRenderer,
QgsRendererRange
QgsRendererRange,
QgsRenderContext
)
from qgis.testing import start_app, unittest
from utilities import unitTestDataPath
@ -101,6 +102,52 @@ class TestQgsRulebasedRenderer(unittest.TestCase):
assert result
def testWillRenderFeature(self):
vl = self.mapsettings.layers()[0]
ft = vl.getFeature(0) # 'id' = 1
renderer = vl.renderer()
ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
ctx.expressionContext().setFeature(ft)
renderer.rootRule().children()[0].setActive(False)
renderer.rootRule().children()[1].setActive(True)
renderer.rootRule().children()[2].setActive(True)
renderer.startRender(ctx, vl.fields()) # build mActiveChlidren
rendered = renderer.willRenderFeature(ft, ctx)
renderer.stopRender(ctx)
renderer.rootRule().children()[0].setActive(True)
assert rendered == False
renderer.startRender(ctx, vl.fields()) # build mActiveChlidren
rendered = renderer.willRenderFeature(ft, ctx)
renderer.stopRender(ctx)
assert rendered == True
def testFeatureCount(self):
vl = self.mapsettings.layers()[0]
ft = vl.getFeature(2) # 'id' = 3 => ELSE
renderer = vl.renderer()
ctx = QgsRenderContext.fromMapSettings(self.mapsettings)
ctx.expressionContext().setFeature(ft)
counter = vl.countSymbolFeatures()
counter.waitForFinished()
renderer.startRender(ctx, vl.fields())
elseRule = None
for rule in renderer.rootRule().children():
if rule.filterExpression() == 'ELSE':
elseRule = rule
assert elseRule != None
cnt = counter.featureCount(elseRule.ruleKey())
assert cnt == 1
def testRefineWithCategories(self):
# Test refining rule with categories (refs #10815)