0

I'm trying to programatically render bunch of vector lines that have an attribute 'route_id' which specifies which route they belong to, as well as an attribute 'ranking'. Some features overlap, and the 'ranking' attribute specifies the order in which they should be offset (ranking==1 -> offset by n * 1, ranking==2 -> offset by n * 2 etc.) so via rule-based symbology it should be possible to render them the way I need.

I'm however struggling to access the offset parameter via PyQGIS. As far as I can tell there are only methods for setting width and color.

layer = iface.activeLayer()
symbol = QgsSymbol.defaultSymbol(layer.geometryType())
renderer = QgsRuleBasedRenderer(symbol)
expression_1 = '"route_id" = \'109\' AND "ranking" = \'2\''
width = 0.86

def rule_based_style(layer, symbol, renderer, label, expression, color, width):
    root_rule = renderer.rootRule()
    rule = root_rule.children()[0].clone()
    rule.setLabel(label)
    rule.setFilterExpression(expression)
    rule.symbol().setColor(QColor(color))
    rule.symbol().setWidth(width)
    root_rule.appendChild(rule)
    layer.setRenderer(renderer)
    layer.triggerRepaint()
    iface.layerTreeView().refreshLayerSymbology(layer.id())
    
rule_based_style(layer, symbol, renderer, 'Rule_1', expression_1, 'red', width)

Is there any way I can specify the offset?

1
  • Maybe you can use this class QgsLineSymbolLayer wich has a setOffset method
    – JULESG
    Commented Nov 3, 2022 at 13:57

1 Answer 1

1

The symbol().setColor() and symbol.setWidth() are high level convenience methods. To access many more styling options you need to dig a bit deeper.

To elaborate on @JULESG's comment, you can access the individual symbol layers of the rule.symbol().

lyr = iface.activeLayer()

renderer = lyr.renderer()

root_rule = renderer.rootRule()

# here I don't clone the rule because I want to make changes in-place.
rule = root_rule.children()[0]

sym = rule.symbol()

# get the (first) symbol layer of the symbol
s = sym.symbolLayers()[0]

# set the offset. Here it is arbitrary, but you can set this dynamically in your code
s.setOffset(5)

# update the changes on the map canvas
lyr.triggerRepaint()

Note: The black line here is just for visualisation (a duplicate of the original layer). You will only see the offset line using the code above.

enter image description here

1
  • 1
    This works, thank you very much!
    – meisterlix
    Commented Nov 4, 2022 at 9:25

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.