5

I am tightly bound to a client's CI and fortunately, the data I am working with comes with RGB values in the attribute table. Perfect for data-driven symbology. But how can I (easily) manage to make my legend match with those?

attribute based symbology but not in the legend

Now I know about rule-based symbology but as stated in this answer to Changing marker shape based on a rule or column value in QGIS? that requires a lot of tedious clicking.

5
  • To underline the importance of the question's necessity: Another scenario relying heavily on a legend's matching symbology are digitized thematic maps, where raster sampling is beeing used to get the original colors. – maxwhere Nov 7 '19 at 10:45
  • How are the RGB values stored in the attribute table? E.g. like (187,51,129) or [187,51,129] etc. – Joseph Nov 7 '19 at 11:16
  • As string: 187,51,129. But why does that matter? – maxwhere Nov 7 '19 at 11:18
  • Using some code could be an efficient method or would you rather do it via the GUI? – Joseph Nov 7 '19 at 11:20
  • An instructional code snipped would be nice of course - I suppose I expected an obvious GUI functionality. – maxwhere Nov 7 '19 at 11:24
4

Perhaps there is a simple GUI method which has been overlooked but the following snippet should colour your legend symbols as those stored in the attribute field containing the RGB values. You can copy/paste the following function into the Python Console:

from ast import literal_eval as make_tuple

def matchLegendColour(layer, categoryField, colorField):
    # Create dictionary to store
    # 'attribute value' : ('symbol colour', 'legend name')
    land_class = {}
    for feat in layer.getFeatures():
        land_class[feat[categoryField]] = (feat[colorField], str(feat[categoryField]))
    # Create list to store symbology properties
    categories = []
    # Iterate through the dictionary
    for classes, (color, label) in land_class.items():
        # Automatically set symbols based on layer's geometry
        symbol = QgsSymbol.defaultSymbol(layer.geometryType())
        # Convert colour attribute into tuple to insert into QColor
        colours = make_tuple(color)
        # Extract the colours
        (red, green, blue) = colours[0], colours[1], colours[2]
        # Set colour
        symbol.setColor(QColor(red, green, blue))
        # Set the renderer properties
        category = QgsRendererCategory(classes, symbol, label)
        categories.append(category)
    # Field name
    expression = categoryField
    # Set the categorized renderer
    renderer = QgsCategorizedSymbolRenderer(expression, categories)
    layer.setRenderer(renderer)
    # Refresh layer
    layer.triggerRepaint()

Then to run said function, we can define the 3 parameters to set the chosen layer, the field name for the values, and the field name containing the RGB attribute (assuming they are stored as 187,51,129 etc):

matchLegendColour(iface.activeLayer(), 'Value', 'Colour')

Result

2
  • 1
    Thank you so much for sharing this gem! It would've taken me at least two days to write something like this! – maxwhere Nov 7 '19 at 14:29
  • 1
    @maxwhere - Most welcome, glad it helped! Fortunately I store various snippets of code and just have to tweak parts of it to get the desired effect. It certainly helps me to spend less time than I should :) – Joseph Nov 7 '19 at 14:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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