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.

PolyGeo
  • 65,136
  • 29
  • 109
  • 338
maxwhere
  • 1,156
  • 6
  • 16
  • 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 07 '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 07 '19 at 11:16
  • As string: 187,51,129. But why does that matter? – maxwhere Nov 07 '19 at 11:18
  • Using some code could be an efficient method or would you rather do it via the GUI? – Joseph Nov 07 '19 at 11:20
  • An instructional code snipped would be nice of course - I suppose I expected an obvious GUI functionality. – maxwhere Nov 07 '19 at 11:24

1 Answers1

5

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

Joseph
  • 75,746
  • 7
  • 171
  • 282
  • 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 07 '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 07 '19 at 14:35