6

I'm trying to control the number of decimal places represented for a layer in my project. I can do this in the symbology tab like this:

enter image description here

I want to do this in PyQGIS. Here is an example of how I would symbolize my layer:

uri = '/Users/ep9k/Desktop/SandraMonson/TestZips.shp'
join_layer = iface.addVectorLayer(uri, 'Patients by Zip Code', 'ogr')
target_field = 'Patient_Da'

def apply_graduated_symbology():    

    myRangeList = []

    symbol = QgsSymbol.defaultSymbol(join_layer.geometryType())     
    symbol.setColor(QColor("#f5c9c9"))                              
    myRange = QgsRendererRange(0, 3, symbol, '0-3')                   
    myRangeList.append(myRange)                                     

    myRenderer = QgsGraduatedSymbolRenderer(target_field, myRangeList)  
    myRenderer.setMode(QgsGraduatedSymbolRenderer.Custom)               

    join_layer.setRenderer(myRenderer)                                  

    print(f"Graduated color scheme applied")

apply_graduated_symbology()

The class QgsRendererRangeLabelFormat seems to be what I need to control the precision of these labels, as it has a method setPrecision.

What I do not understand is, that it seems QgsRendererRange and QgsRendererRangeLabelFormat have no relationship to each other according to the documentation, the PyQGIS console, etc. In fact, QgsRenderRangeLabelFormat seems to have no parent or child classes and I can't figure out how this class relates to anything else.

It seems like I should be able to do something like this:

label = QgsRendererRangeLabelFormat('0-3', 2)   #2nd argument sets the precision 
myRange = QgsRendererRange(0, 3, symbol, label) 

But this doesn't make sense because I create a string ('0-3') and then setPrecision, which should affect the precision of a float.

Here is the help from the QGIS console regarding arguments. Why is the first argument a Qstring?

enter image description here

How do I use the class QgsRendererRangeLabelFormat?

1 Answer 1

5

Since you are manually creating each custom range, you can simply pass your legend label as the 4th argument to the QgsRendererRange class constructor in whatever form you like.

For example:

myRange1 = QgsRendererRange(0.00, 3.00, mySymbol1, '0.00-3.00')
...
myRange2 = QgsRendererRange(3.10, 6.00, mySymbol2, '3.10-6.00')

etc.

In practice the appropriate precision, both for setting and interpreting each range, would probably depend on factors like the actual precision of the numeric attribute field you are using, the size of each value range etc.

Since that argument is just a string which will be used to label each range in the table of contents panel/ legend you can also include units to provide context e.g. '0-3 patients' or use very simple labels to broadly interpret each value range such as 'Low', 'Medium', 'High' etc.

By the way, you may find you also need to call: join_layer.triggerRepaint() after setting the renderer to apply the new layer symbology in the map canvas.

If you want to use QgsRendererRangeLabelFormat, you need to call updateRangeLabels() after setting the label format e.g.

lyr_name = 'ibra7_regions'
vlayer = QgsProject.instance().mapLayersByName(lyr_name)[0]
target_field = 'SQ_KM'

def apply_graduated_symbology(lyr, fld):
    range_list = []
    color_1 = QColor('#FFD700')
    color_2 = QColor('#FF4500')
    color_3 = QColor('#DC143C')
    symbol_1 = QgsSymbol.defaultSymbol(lyr.geometryType())
    symbol_1.setColor(color_1)
    range_1 = QgsRendererRange(1.00, 150000.00, symbol_1, '1-150000')
    range_list.append(range_1)
    symbol_2 = QgsSymbol.defaultSymbol(lyr.geometryType())
    symbol_2.setColor(color_2)
    range_2 = QgsRendererRange(150000.00, 300000.00, symbol_2, '150000-300000')
    range_list.append(range_2)
    symbol_3 = QgsSymbol.defaultSymbol(lyr.geometryType())
    symbol_3.setColor(color_3)
    range_3 = QgsRendererRange(300000.00, 450000.00, symbol_3, '300000-450000')
    range_list.append(range_3)
    renderer = QgsGraduatedSymbolRenderer(fld, range_list)
    renderer.setMode(QgsGraduatedSymbolRenderer.Custom)
    
    # Set label format and update range labels
    lbl_format = QgsRendererRangeLabelFormat()
    lbl_format.setFormat("%1 - %2")
    lbl_format.setPrecision(2)
    lbl_format.setTrimTrailingZeroes(False)
    renderer.setLabelFormat(lbl_format)
    renderer.updateRangeLabels()
    
    lyr.setRenderer(renderer)
    lyr.triggerRepaint()
    
apply_graduated_symbology(vlayer, target_field)

To update the label precision of an existing graduated renderer:

layer = iface.activeLayer()
#print(layer.renderer().labelFormat().precision())
lbl_format = QgsRendererRangeLabelFormat()
lbl_format.setFormat("%1 - %2")
lbl_format.setPrecision(2)
lbl_format.setTrimTrailingZeroes(False)
renderer = layer.renderer().clone()
renderer.setLabelFormat(lbl_format)
renderer.updateRangeLabels()
layer.setRenderer(renderer)
layer.triggerRepaint()
iface.layerTreeView().refreshLayerSymbology(layer.id())

Setting label precision programmatically is more commonly used when calculating classes automatically.

Note that from QGIS 3.10 onwards, QgsRendererRangeLabelFormat is deprecated. Now we should set the label format and precision in the QgsClassificationMethod subclass e.g. QgsClassificationEqualInterval(). The snippet below creates a graduated renderer using the equal interval method, with 7 classes and a label precision of 1:

layer = iface.activeLayer()

default_style = QgsStyle().defaultStyle()

# Use any existing color ramp e.g.
# 'Greens', 'Purples', 'Cividis' etc
# or create a custom color ramp
# Here we are using the purple/blue/green ramp...
color_ramp = default_style.colorRamp('PuBuGn')

renderer = QgsGraduatedSymbolRenderer()
renderer.setClassAttribute('SQ_KM')

# using equal interval method...
classification_method = QgsClassificationEqualInterval()
classification_method.setLabelPrecision(1)
classification_method.setLabelTrimTrailingZeroes(True)
renderer.setClassificationMethod(classification_method)

# creating 7 classes...
renderer.updateClasses(layer, 7)
renderer.updateColorRamp(color_ramp)

layer.setRenderer(renderer)
layer.triggerRepaint()

# optionally expand the layer legend...
lyr_tree_root = iface.layerTreeView().layerTreeModel().rootGroup()
lyr_node = lyr_tree_root.findLayer(layer.id())
if not lyr_node.isExpanded():
    lyr_node.setExpanded(True)

And to update the label precision on an existing graduated renderer:

layer = iface.activeLayer()
renderer = layer.renderer().clone()
class_method = renderer.classificationMethod()
class_method.setLabelFormat("%1 - %2")
class_method.setLabelPrecision(3)
class_method.setLabelTrimTrailingZeroes(False)
renderer.updateRangeLabels()
layer.setRenderer(renderer)
layer.triggerRepaint()
iface.layerTreeView().refreshLayerSymbology(layer.id())
3
  • thanks Ben. I arrived at the same conclusion you did, where the 4th argument in QgsRendererRange is the label which I can customize how I like. And this solution works well enough but it is not quite what I'm looking for. My question is really a meta-question of "how do I use this weird class that has no documentation and seemingly no relationship to other classes, but supposedly does what I want" Commented May 20, 2019 at 1:53
  • 1
    Thanks again Ben. After much effort I also could not get it to do anything except affect the graduated symbology dialog box. I wonder, what is the purpose of this class? Maybe it exists to support the dialog box and isn't really meant to be used programmatically? Commented May 20, 2019 at 14:11
  • 1
    Hi @Erich Purpur, better late than never- I have added some updates and revisions to this answer including the use of QgsRendererRangeLabelFormat, which is now deprecated in any case.
    – Ben W
    Commented Aug 25 at 7:24

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.