4

I'm trying to apply a color ramp on a categorized render in QGIS 3.4 (via the Python console).

I started by adapting the solution for a catagorized symbol layer - to QGIS 3.4 but this applies a random color scheme. Since then i searched for how to apply a color ramp, which i have started to adapt from an earlier solution for applying a color ramp.

So far I've got his:

# Get the active layer (must be a vector layer)
layer = QgsProject.instance().mapLayersByName("t_zpbo")[0]

# provide file name index and field's unique values
field = layer.fields().lookupField('zp_capamax')
unique_values = layer.uniqueValues(field)

## get default style
myStyle = QgsStyle().defaultStyle()

## get a list of default color ramps [u'Blues', u'BrBG', u'BuGn'....]
defaultColorRampNames = myStyle.colorRampNames()

## setting ramp to Reds, sixth index of defaultColorRampNames
ramp = myStyle.colorRamp(defaultColorRampNames[9])

# set up an empty categorized renderer and assign the color ramp
renderer = QgsCategorizedSymbolRenderer('zp_capamax', [])
renderer.setSourceColorRamp(ramp)
layer.setRenderer(renderer)

Unfortunately this returns the follwoing error:

TypeError: QgsCategorizedSymbolRenderer(attrName: str = '', categories: Iterable[QgsRendererCategory] = []): argument 1 has unexpected type 'int'

Which leaves me a little confused... as far as I can tell it doesn't seem to like the fact the values in my field are integers...

EDIT: I modified the original code i posted above to take into account the comments of Ben W (see below) - the code above doesnt return any error codes - but it doesn't seem to creates classes. When I look at the layer properties after I run the script I can see the styling is catagorized, and the right field is selected, and a color ramp is selected - but no classes appear.

6
  • 1
    The variable: field which you are passing to the renderer constructor is an integer (since it returns the field index) but the constructor expects a string argument. Try passing your field name 'zp_capamax' instead.
    – Ben W
    Commented Feb 1, 2019 at 14:09
  • @BenW Cheers, again! I'm not getting the error message anymore but nothing happens - when I look in the properties->style windows no classes appear... (the right field is selected and the color ramp)...
    – lhowarth
    Commented Feb 1, 2019 at 14:54
  • It looks like you have two different variable names for your layer (layer in 2nd line and vl in last line). Make them the same. Surprising you didn't get a name error?
    – Ben W
    Commented Feb 1, 2019 at 23:16
  • aah well spotted ! Not sure why I didn't get a name error.. ? Also I realised that the orange red color ramp is index number [9].. i edited the code above to take into account your comments - though for the minute I've still got the same result as my previous comment i.e. the classes don't seem to be defined (when I look at the layer properties the styling is catagorized, the field appears as 'zp_capamax', a color ramp appears but no classes..
    – lhowarth
    Commented Feb 3, 2019 at 19:55
  • 1
    BTW, the main problem now with your code above is that you are not actually creating any categories... and you would still need to call triggerRepaint on your layer at the end. But bear in mind that doing setSourceColorRamp doesn't work like you hope it would! Yes, it changes the colour ramp in the symbology dialog, but it doesn't apply that ramp to the categories you create programmatically. For custom colours, you really have to create a colour and symbol and pass it to each category as you create it.
    – Ben W
    Commented Feb 5, 2019 at 2:45

1 Answer 1

9

Improved answer

The trick is to use renderer.updateColorRamp(ramp) instead of renderer.setSourceColorRamp(ramp).

Here is a working code snippet to create categories from unique values, pass them to a categorized renderer, and apply a color ramp:

layer = QgsProject.instance().mapLayersByName('t_zpbo')[0]
field = layer.fields().lookupField('zp_capamax') # field which contains unique values
unique_values = layer.uniqueValues(field)

categories = []
for value in unique_values:
    symbol = QgsSymbol.defaultSymbol(layer.geometryType())
    category = QgsRendererCategory(value, symbol, str(value))
    categories.append(category)
    
myStyle = QgsStyle().defaultStyle()
ramp = myStyle.colorRamp('Reds') # pass any color ramp name e.g. 'Spectral'
    
renderer = QgsCategorizedSymbolRenderer('zp_capamax', categories)
renderer.updateColorRamp(ramp)
layer.setRenderer(renderer)
layer.triggerRepaint()

I will leave the original answer here in case it may still be useful.

Original answer

Firstly, The variable field which you are passing to the renderer constructor is an integer (since it returns the field index) but the constructor is expecting a string argument. You need to pass your field name 'zp_capamax' instead.

Secondly, you have two different variable names for your layer (layer in line 2 and vl in the last line). These must be the same.

Thirdly, I played around the setSourceColorRamp approach to apply a red colour ramp. What I found was that, while that does set the colour ramp to red in the layer symbology dialog, it is not actually applied to the categories.

The best solution I have found is by adapting the answer by Detlev in the first link you posted. I have updated it for Qgis 3, and changed the RGB generation.

I tested this (with my own layer and field names) on two polygon layers, one with 5 features and one with around 250 features. It worked quite satisfactorily on both layers, however you could play around with adjusting both the 'step' values as well as the starting GB value.

Try the code below and let me know how it works for you.

layer = QgsProject().instance().mapLayersByName('t_zpbo')[0]
fld = layer.fields().lookupField('zp_capamax')
values = sorted(list(layer.uniqueValues(fld)))
total = len(values)
rampPoint = total/2
Rstep = 300/total
GBstep = 400/total
categories = []
R = 255
GB = 245
for current, value in enumerate(values):
    if current < rampPoint:
        GB = GB - GBstep
    else:
        R = R - Rstep
    symbol = QgsSymbol.defaultSymbol(layer.geometryType())    
    layer_style = {}
    layer_style['color'] = '{}, {}, {}'.format(int(R), int(GB), int(GB))
    layer_style['outline'] = '#000000'
    #print(layer_style)
    symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)
    if symbol_layer is not None:
        symbol.changeSymbolLayer(0, symbol_layer)
    category = QgsRendererCategory(value, symbol, str(value))
    categories.append(category)
renderer = QgsCategorizedSymbolRenderer('zp_capamax', categories)
if renderer is not None:
    layer.setRenderer(renderer)
layer.triggerRepaint()

Here is the result of the above code on the Natural Earth world countries data set:

enter image description here

And a couple of screen shots of the classification (A & Z) showing the colour ramp range:

enter image description here

enter image description here

3
  • Cheers, I started off with somthing similar (though I didn't try to modify the RGB values like you) - the problem is that the colours are assigned randomly - even if they are all reds - I tried to modifying the second script (posted in my question) because I really want the intensity of the colour to be linked to the attribute value in my field 'zp_capamax'
    – lhowarth
    Commented Feb 3, 2019 at 19:31
  • 1
    @lhowarth, I spent some more time on this after work today. Please see my edited answer above. Hopefully this will now get pretty close to want you want.
    – Ben W
    Commented Feb 4, 2019 at 11:13
  • Wow that's great! I'm not quite sure I follow excatly how it works (i'm still learning python/pyQGIS) but that's already a big help thanks :)
    – lhowarth
    Commented Feb 5, 2019 at 14:23

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.