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 .. versionadded:: 2.14
%End %End
QgsRuleBasedRenderer::RuleList rulesForFeature( QgsFeature &feat, QgsRenderContext *context = 0 ); QgsRuleBasedRenderer::RuleList rulesForFeature( QgsFeature &feat, QgsRenderContext *context = 0, bool onlyActive = true );
%Docstring %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 %End
void stopRender( QgsRenderContext &context ); 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 :param iselse: If true, this rule is an ELSE rule
%End %End
bool isElse(); bool isElse() const;
%Docstring %Docstring
Check if this rule is an ELSE rule 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 ) ) if ( !isFilterOK( feat, context ) )
return false; return false;
if ( mSymbol ) if ( mSymbol )
return true; return true;
Q_FOREACH ( Rule *rule, mActiveChildren ) 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 true;
}
} }
return false; return false;
} }
@ -576,12 +589,31 @@ QSet<QString> QgsRuleBasedRenderer::Rule::legendKeysForFeature( QgsFeature &feat
Q_FOREACH ( Rule *rule, mActiveChildren ) 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; return lst;
} }
QgsRuleBasedRenderer::RuleList QgsRuleBasedRenderer::Rule::rulesForFeature( QgsFeature &feat, QgsRenderContext *context ) QgsRuleBasedRenderer::RuleList QgsRuleBasedRenderer::Rule::rulesForFeature( QgsFeature &feat, QgsRenderContext *context, bool onlyActive )
{ {
RuleList lst; RuleList lst;
if ( !isFilterOK( feat, context ) ) if ( !isFilterOK( feat, context ) )
@ -590,9 +622,13 @@ QgsRuleBasedRenderer::RuleList QgsRuleBasedRenderer::Rule::rulesForFeature( QgsF
if ( mSymbol ) if ( mSymbol )
lst.append( this ); 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; return lst;
} }

View File

@ -342,8 +342,15 @@ class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer
*/ */
QSet< QString > legendKeysForFeature( QgsFeature &feat, QgsRenderContext *context = nullptr ); 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 * 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 * \returns True if this rule is an else rule
*/ */
bool isElse() { return mElseRule; } bool isElse() const { return mElseRule; }
protected: protected:
void initFilter(); void initFilter();

View File

@ -40,7 +40,8 @@ from qgis.core import (QgsVectorLayer,
QgsRendererCategory, QgsRendererCategory,
QgsCategorizedSymbolRenderer, QgsCategorizedSymbolRenderer,
QgsGraduatedSymbolRenderer, QgsGraduatedSymbolRenderer,
QgsRendererRange QgsRendererRange,
QgsRenderContext
) )
from qgis.testing import start_app, unittest from qgis.testing import start_app, unittest
from utilities import unitTestDataPath from utilities import unitTestDataPath
@ -101,6 +102,52 @@ class TestQgsRulebasedRenderer(unittest.TestCase):
assert result 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): def testRefineWithCategories(self):
# Test refining rule with categories (refs #10815) # Test refining rule with categories (refs #10815)