I've created a layer style consisting of a rule-based renderer with multiple rules, each of them categorized into several categories. I load this style (qml) automatically to new layers that are added to my project.
However, not all layers contain data of all categories. My aim is now, to delete all categories within the rules, that don't contain any features.
But, how is it possible to manipulate the categories within rules in PyQGIS?
Here's my code so far:
source= os.path.join(path + "|layername=" + layer)
example= QgsVectorLayer(source, layer, "ogr")
QgsProject.instance().addMapLayer(example, True)
my_style = os.path.join(os.path.join(path, 'my_style.qml'))
example.loadNamedStyle(my_style)
renderer = example.renderer()
root_rule = renderer.rootRule()
I want to delete Category_2 in Rule_2
For example, I want to delete Category_2 in Rule_2 because there are no features within this category.
1 Answer 1
Something like this should do the trick- this will delete any unused rules taking into account parent rule and any sub-rule categories as a combined filter. Note that this code would need to be modified if there were more rule levels in the structure.
# Required imports if outside the Python console
from qgis.core import QgsProject, QgsFeatureRequest, QgsExpression
lyr = QgsProject.instance().mapLayersByName('layername')[0]
r_clone = lyr.renderer().clone()
rr = r_clone.rootRule()
for r in rr.descendants():
filter_exp = r.filterExpression()
ft_req = QgsFeatureRequest(QgsExpression(filter_exp))
parent_rule_exp = r.parent().filterExpression()
if parent_rule_exp:
ft_req.combineFilterExpression(parent_rule_exp)
filter_fts = lyr.getFeatures(ft_req)
ft_count = len(list(filter_fts))
if not ft_count:
r.parent().removeChild(r)
lyr.setRenderer(r_clone)
iface.layerTreeView().refreshLayerSymbology(lyr.id())
This took just a couple of seconds on the test layer below with 39000 features.
Before:
After:
Explore related questions
See similar questions with these tags.