1

I am developing a plugin in PyQGIS 3. Within this plugin, I already succeeded in categorizing using PYQGIS 3 but I am still looking to find a way to create ranges within these newly created categories using PYQGIS 3. This is my code so far:

layer = QgsVectorLayer(output_folder + layer + '.shp', "layer", "ogr")
QgsProject.instance().addMapLayer(Regional_BC)

field_name = 'CNTR_CODE'
field_index = layer.fields().indexFromName(field_name)
unique_values = layer.uniqueValues(field_index)

# create category list
category_list = []
for value in unique_values:
    symbol = QgsSymbol.defaultSymbol(Regional_BC.geometryType())
    category = QgsRendererCategory(value, symbol, str(value))
    category_list.append(category)

# create renderer by specifying category list
renderer = QgsCategorizedSymbolRenderer(field_name, category_list)
style = QgsStyle().defaultStyle()
ramp = style.colorRamp('RdYlGn')
renderer.updateColorRamp(ramp)

layer.setRenderer(renderer)
layer.triggerRepaint()

I want to categorize based on the column CNTR_CODE in PyQGIS 3. This step has been done in the first section of the code. However, when I want to give these produced categories sublayers color based on the fieldName 'INCOMING_T'. This is a link to the working manual flow (OPTION 1 ANSWER): Using graduate and categorize in QGIS?

I am trying to recreate the steps in the link --> option 1 answer within the PYQGIS 3 environment enter image description here

1 Answer 1

0

I found the solution to this question after I took a good look at the existing documentation. Steps that need to be conducted:

  1. Create CategorizedRenderer
  2. Update existing CategorizedRenderer and change it into RuleBasedRenderer
  3. Build a for loop that goes through all the symbologies (in my case symbologies of countries) using the ruleRend.refineRuleRanges() function.

Here is an example of what my code looks like:

#create regional_BC_year layer with symbology
layer3 = QgsVectorLayer(output_folder + 'Regional_BC_Joined_' + str(year) + '.shp', "Regional_BC_produced_" + str(year), "ogr")

# read correct field name in regional_bc layer
field_name = 'CNTR_CODE'
field_index = layer3.fields().indexFromName(field_name)
unique_values = layer3.uniqueValues(field_index)

# create category list
category_list = []
for value in unique_values:
    symbol = QgsSymbol.defaultSymbol(layer3.geometryType())
    category = QgsRendererCategory(value, symbol, str(value))
    category_list.append(category)

# create renderer by specifying category list
renderer = QgsCategorizedSymbolRenderer(field_name, category_list)
style = QgsStyle().defaultStyle()
ramp = style.colorRamp('RdYlGn')
renderer.updateColorRamp(ramp)

# set new renderer of layer
layer3.crs = {'init' :'epsg:4326'}
layer3.setRenderer(renderer)
layer3.triggerRepaint()

# change to from categorizedrenderer into rulebased renderer
if renderer.type() == 'categorizedSymbol':
    ruleRend = QgsRuleBasedRenderer.convertFromRenderer(renderer)

# add layer to map and set new rulebased renderer
QgsProject.instance().addMapLayer(layer3)
layer3.setRenderer(ruleRend)
layer3.triggerRepaint()
# refresh symbology of layer
iface.layerTreeView().refreshLayerSymbology(layer3.id())

# load values of BC_per_reg column and add to vals df
vals = []
fld = 'BC_per_reg'
for f in layer3.getFeatures():
    vals.append(f[fld])

# colors of series that will be visualized
colors = ['#f7fcf5', '#d5efcf', '#9ed898', '#54b567', '#1d8641']

# calculate upper and lower values of vals df
vals = list(filter(None, vals))
vals = [float(s) for s in vals]
lower = 0.001
upper = 15

# add bounds and colors to each range and add to each "symbol"
step = 2
range_list = []
for c in colors:
    cat = [lower, lower+step, c]
    sym = QgsSymbol.defaultSymbol(layer3.geometryType())
    sym.setColor(QColor(cat[2]))
    rng = QgsRendererRange(cat[0], cat[1], sym, '{0:.1f}-{1:.1f}'.format(cat[0], cat[1]))
    range_list.append(rng)
    lower = (lower+step)

# add all symbols, and add to range_list
symbol = QgsSymbol.defaultSymbol(layer3.geometryType())
symbol.setColor(QColor('#00441b'))
rng1 = QgsRendererRange(10, 100, symbol, '{0:.1f}-{1:.1f}'.format(10, 100))
range_list.append(rng1)

# refineRuleRanges so that for eacht class --> e.g. France we will get 5 ranges of values inside
# Scale symbol based on column
if ruleRend.type() == 'RuleRenderer':
    for i in range(len(ruleRend.rootRule().children())):
        ruleRend.refineRuleRanges(layer3.renderer().rootRule().children()[i], QgsGraduatedSymbolRenderer(fld, range_list)) #inputs for QgsGraduatedSymbolRenderer(fld, range_list)

v1 = layer3

# set renderer and reload layers on map
QgsProject.instance().addMapLayer(v1)
v1.setRenderer(ruleRend)
v1.triggerRepaint()
iface.layerTreeView().refreshLayerSymbology(v1.id())

A visual representation of what the results look like:

enter image description here

I can now check and uncheck all countries while the ranges are visible for other countries! See unchecked regions Portugal:

enter image description here

3
  • 1
    Would you mind including an image or two showing how the results of the code in your answer look when applied to your dataset, both in the table of contents and on the map canvas. I am genuinely curious as I spent quite a bit of time playing around with this with a view to posting an answer, but I must admit I have some misgivings about this combination of symbology from a cartographic point of view.
    – Ben W
    Commented Jun 30, 2021 at 12:41
  • I edited my answer with some visual representations. If you have some more questions please let me know! Commented Jun 30, 2021 at 13:00
  • Great, thanks for taking the time to post the images, cheers.
    – Ben W
    Commented Jul 1, 2021 at 11:34

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.