I have hundreds of rules specifying ranges of polygon areas. I would like to create a table of the ranges and their values outside of QGIS and since these results are exactly what I am after I would like to export them directly from the layer properties here. I know there are tools like statistics by category which are useful but it would be far easier and quicker if I could simply use these count results directly.
-
Just label and rule, or all table including count, duplicate count? – Noura Feb 18 '22 at 19:34
-
Thanks, I am after the rule and count. – Funzo Feb 20 '22 at 22:48
2 Answers
You can access the filter expression of each rule with this script. Copy-paste it to the script editor of the Python console and click Run
Make sure the layer is selected in the Layers Panel, and that it is not in edit mode, else the filter will not be applied.
# get layer
lyr = iface.activeLayer()
get the rule-based rendered from the layer
r = lyr.renderer()
get the root rule
root = r.rootRule()
iterate through rules
for rule in root.children():
# get the rule filter expression
exp = rule.filterExpression()
# set the rule filter on the layer
lyr.setSubsetString(exp)
# count filtered features
count = lyr.featureCount()
# reset the filter
lyr.setSubsetString('')
print(f'rule: {exp} | count: {count}')
## do something with rules (save to text file)
Output:
rule: "code" = 'A' | count: 12425
rule: "code" = 'B' | count: 12580
rule: "code" = 'C' | count: 12298
rule: "code" = 'D' | count: 12618
rule: "code" = 'E' | count: 12534
rule: "code" = 'F' | count: 12441
- 16,843
- 3
- 21
- 52
To elaborate on my other answer, and with the help of this excellent answer by Ben W, I came up with a script to save the rules, counts, and symbology to an Excel spreadsheet.
It relies on one non-native library, XlsxWriter. The easiest way to install it is to open the OsGeo4W shell (via the Start Menu on Windows) and use:
pip install XlsxWriter
Then run the script in the Script Editor in the QGIS Python console.
import os
import xlsxwriter
from io import BytesIO
path to save images
IMG_PATH = r'D:\OneDrive\geodata\symbols'
path to save the spreadsheet
EXPORT_PATH = r'D:\OneDrive\geodata\rules.xlsx'
workbook = xlsxwriter.Workbook(EXPORT_PATH)
worksheet = workbook.add_worksheet()
def convert_icon(icon, size=None, fmt='PNG'):
'''
This function modified from:
https://stackoverflow.com/questions/40789856/python-convert-qt-qicon-into-jpeg
Credit: stackoverflow member ekhumoro
'''
if size is None:
size = 18
pixmap = QPixmap.fromImage(icon)
pixmap = pixmap.scaledToWidth(size)
array = QtCore.QByteArray()
buffer = QtCore.QBuffer(array)
buffer.open(QtCore.QIODevice.WriteOnly)
pixmap.save(buffer, fmt)
buffer.close()
return array.data()
write the spreadsheet headers
worksheet.write('A1', 'symbol')
worksheet.write('B1', 'label')
worksheet.write('C1', 'rule')
worksheet.write('D1', 'count')
get layer
lyr = iface.activeLayer()
get the rule-based rendered from the layer
r = lyr.renderer()
get the root rule
root = r.rootRule()
set the spreadsheet row to enter data
sheet_row = 2
iterate through rules
for rule in root.children():
# get various attributes from rule
sym = rule.symbol()
lab = rule.label()
exp = rule.filterExpression()
# set the rule filter on the layer
lyr.setSubsetString(exp)
# count filtered features
count = lyr.featureCount()
# reset the filter
lyr.setSubsetString('')
# get rule symbol as image
img = sym.asImage(QSize(40,40))
# optionally save symbols as png
img.save(
os.path.join(
IMG_PATH,
(exp.replace('<=', 'lte')
.replace('>=', 'gte')
.replace('<>', 'not')
.replace('<', 'lt')
.replace('>', 'gt')
.replace('=', 'eq')
.replace('.', '_')
.replace('"','')
.replace("'", '') + '.png')
)
)
# convert QImage to QByteArray
img_data = convert_icon(img)
# convert to BytesIO object
io_data = BytesIO(img_data)
# assign cells for data
icon_cell = f'A{sheet_row}'
label_cell = f'B{sheet_row}'
rule_cell = f'C{sheet_row}'
count_cell = f'D{sheet_row}'
# insert symbol and corresponding data into cells
worksheet.insert_image(icon_cell, 'image.png', {'image_data': io_data, 'x_offset': 15, 'y_offset': 3})
worksheet.write(label_cell, lab)
worksheet.write(rule_cell, exp)
worksheet.write(count_cell, count)
# set the width of specific columns to accommodate data
worksheet.set_column('B:B', 20)
worksheet.set_column('C:C', 40)
# go to next row of spreadsheet
sheet_row+=1
commit the changes to the workbook
workbook.close()
The spreadsheet:
The saved image files:
- 16,843
- 3
- 21
- 52
-
Thanks Matt, this looks fantastic. Unfortunately I'm having trouble running the script. I seem to have successfully installed xlsxwriter (ran pip.main(['install','XlsxWriter']) in the python console and output said successfully installed), then when I run the script it fails at the beginning with ModuleNotFoundError: No module named 'xlsxwriter' (I can't paste the output here as it goes over character limit). – Funzo Feb 20 '22 at 23:13
-
Ben details a few ways to install the module in his answer. I found the easiest way was through the OsGeo4W shell (see update to my answer). Is it a requirement that it must be installed via the script? – Matt Feb 20 '22 at 23:29
-
I tried first through OsGeo4W shell as Ben writes but doing that I got the error msg it is not recognized as an internal or external command, etc.
I looked for other ways and found that the command python -m pip install XlsxWriter worked.
I don't know if it has to be installed via a script, it would be good if there was perhaps another way (I haven't so far found one).
– Funzo Feb 21 '22 at 00:02 -
Unfortunately, I cannot reproduce your issue, as using OsGeo4W shell works fine for me. I just tried with another library (
flickrapi) and I can import it in the QGIS Python console directly afterpip install flickrapiin the OsGeo4W shell. – Matt Feb 21 '22 at 11:41 -
Thanks Matt, got it up and running! What if there was another level of rules and counts within the rules ie. children of children, could that be shown as well? – Funzo Feb 23 '22 at 01:44



