4

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.

enter image description here

2
  • Just label and rule, or all table including count, duplicate count?
    – Noura
    Commented Feb 18, 2022 at 19:34
  • Thanks, I am after the rule and count.
    – Funzo
    Commented Feb 20, 2022 at 22:48

2 Answers 2

4

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
4

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

enter image description here

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:

enter image description here

The saved image files:

enter image description here

5
  • 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
    Commented Feb 20, 2022 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
    Commented Feb 20, 2022 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
    Commented Feb 21, 2022 at 0: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 after pip install flickrapi in the OsGeo4W shell.
    – Matt
    Commented Feb 21, 2022 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
    Commented Feb 23, 2022 at 1:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.