Skip to content

Commit

Permalink
When a rule based renderer can be treated as a categorical renderer,
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed May 21, 2024
1 parent 76e7b6e commit e4aee23
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
81 changes: 81 additions & 0 deletions felt/core/fsl_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,87 @@ def rule_based_renderer_to_fsl(

return res

# multiple rules. can we treat this as a categorized renderer?
filter_attribute = None
converted_symbols = []
category_values = []
other_symbol = None
legend_text = {}

for rule in renderer.rootRule().children():
if rule.children():
# rule has nested children, can't convert
return None

if not rule.symbol():
# no symbol rule, can't convert
return None

if rule.dependsOnScale():
# rule has scale based visibility, can't convert
return None

filter_expression = rule.filterExpression()
if not filter_expression:
# multiple symbol per feature, can't convert
return None

if not rule.isElse():
res, field, value = QgsExpression.isFieldEqualityExpression(
filter_expression
)
if not res:
# not a simple field=value expression, can't convert
return None

if filter_attribute and filter_attribute != field:
# rules depend on different attributes, can't convert
return None

filter_attribute = field

converted_symbol = FslConverter.symbol_to_fsl(rule.symbol(),
context,
layer_opacity)
if not converted_symbol:
# can't convert symbol
return None

if rule.isElse():
if other_symbol:
# multiple ELSE rules, can't conver
return None
other_symbol = converted_symbol
legend_text['Other'] = rule.label()
else:
converted_symbols.append(converted_symbol)
legend_text[str(value)] = rule.label()
category_values.append(str(value))

all_symbols = converted_symbols
if other_symbol:
all_symbols.append(other_symbol)

if not all_symbols:
return None

style = FslConverter.create_varying_style_from_list(
all_symbols
)

return {
"config": {
"categoricalAttribute": filter_attribute,
"categories": category_values,
"showOther": bool(other_symbol)
},
"legend": {
"displayName": legend_text
},
"style": style,
"type": "categorical"
}

@staticmethod
def null_renderer_to_fsl(renderer: QgsNullSymbolRenderer,
context: ConversionContext,
Expand Down
52 changes: 52 additions & 0 deletions felt/test/test_fsl_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -1608,6 +1608,58 @@ def test_rule_based_renderer(self):
'type': 'simple'}
)

def test_categorical_rule_based_renderer(self):
"""
Test converting rule based renderers which can be treated as
categorical renderers
"""
conversion_context = ConversionContext()

root_rule = QgsRuleBasedRenderer.Rule(None)
renderer = QgsRuleBasedRenderer(root_rule)

line = QgsSimpleLineSymbolLayer(color=QColor(255, 0, 0))
line_symbol = QgsLineSymbol()
line_symbol.changeSymbolLayer(0, line.clone())
child_rule = QgsRuleBasedRenderer.Rule(line_symbol.clone())
child_rule.setLabel('rule 1')
child_rule.setFilterExpression('"my_field"=\'a\'')
root_rule.appendChild(child_rule)

line = QgsSimpleLineSymbolLayer(color=QColor(255, 255, 0))
line_symbol = QgsLineSymbol()
line_symbol.changeSymbolLayer(0, line.clone())
child_rule = QgsRuleBasedRenderer.Rule(line_symbol.clone())
child_rule.setLabel('rule 2')
child_rule.setFilterExpression('"my_field"=\'b\'')
root_rule.appendChild(child_rule)

line = QgsSimpleLineSymbolLayer(color=QColor(255, 255, 255))
line_symbol = QgsLineSymbol()
line_symbol.changeSymbolLayer(0, line.clone())
child_rule = QgsRuleBasedRenderer.Rule(line_symbol.clone())
child_rule.setLabel('rule 3')
child_rule.setIsElse(True)
root_rule.appendChild(child_rule)

self.assertEqual(
FslConverter.vector_renderer_to_fsl(renderer, conversion_context),
{'config': {'categoricalAttribute': 'my_field',
'categories': ['a', 'b'],
'showOther': True},
'legend': {'displayName': {'Other': 'rule 3', 'a': 'rule 1',
'b': 'rule 2'}},
'style': [{'color': ['rgb(255, 0, 0)',
'rgb(255, 255, 0)',
'rgb(255, 255, 255)'],
'isClickable': False,
'isHoverable': False,
'lineCap': 'square',
'lineJoin': 'bevel',
'size': 1}],
'type': 'categorical'}
)

def test_null_symbol_renderer(self):
"""
Test converting null symbol renderers
Expand Down

0 comments on commit e4aee23

Please sign in to comment.