[GIS] Apply a color ramp to vector layer using PyQGIS3

color ramppyqgis-3qgis

I'm trying to apply a color ramp to a QgsGraduatedSymbolRenderer in PyQGIS 3 (QGIS 3.8) using the following code:

Getting the QgsGradientColorRamp:

myStyle = QgsStyle().defaultStyle()
defaultColorRampNames = myStyle.colorRampNames()
ramp = myStyle.colorRamp(defaultColorRampNames[-6]) #Spectral color ramp

Applying it to the QgsGraduatedSymbolRenderer:

myRenderer = QgsGraduatedSymbolRenderer('', myRangeList) #A list of break values
myRenderer.setSourceColorRamp(ramp)
myVectorLayer.setRenderer(myRenderer)
myVectorLayer.triggerRepaint()

Unfortunately the layer still shows in its original colors without the color map being applied. Upon looking in properties, however, oddly enough the color map is applied partially. The color map selected is indeed the one chosen ('Spectral'), but this isn't applied to the values' symbols. If I then just click on the color ramp and reapply it, it works, but I'd like to achieve this all through PyQGIS. I looked at similar questions here and the QGIS3 Python API and couldn't find a solution.

enter image description here

Best Answer

Updated answer:

Below is a recipe to apply a graduated renderer based on values in an attribute field, specifying the number of classes and color ramp.

# Set layer name and desired paremeters
layer_name = 'Your_layer_name'
ramp_name = 'Spectral'
value_field = 'Your_field_name'
num_classes = 5
classification_method = QgsClassificationEqualInterval()

#You can use any of these classification method classes:
#QgsClassificationQuantile()
#QgsClassificationEqualInterval()
#QgsClassificationJenks()
#QgsClassificationPrettyBreaks()
#QgsClassificationLogarithmic()
#QgsClassificationStandardDeviation()

layer = QgsProject().instance().mapLayersByName(layer_name)[0]

# change format settings as necessary
format = QgsRendererRangeLabelFormat()
format.setFormat("%1 - %2")
format.setPrecision(2)
format.setTrimTrailingZeroes(True)

default_style = QgsStyle().defaultStyle()
color_ramp = default_style.colorRamp(ramp_name)

renderer = QgsGraduatedSymbolRenderer()
renderer.setClassAttribute(value_field)
renderer.setClassificationMethod(classification_method)
renderer.setLabelFormat(format)
renderer.updateClasses(layer, num_classes)
renderer.updateColorRamp(color_ramp)

layer.setRenderer(renderer)
layer.triggerRepaint()

I will leave the original answer below:

I have been through the same experience before, and (I may be wrong) but as far as I am aware, it is not possible to programmatically set a default color ramp and have it automatically applied. As you discovered, doing setSourceColorRamp() just sets that color ramp in the symbology dialog- it doesn't apply it to the renderer range symbols.

You have to create each range, instantiate QgsSymbol() objects, set colors to them and pass those in to a QgsRendererRange() constructor, together with the range and legend values.

It's a bit hacky, but I knocked out the script below which will create a graduated symbology with 10 classes (like your example above) in a basic spectral color ramp. Just change the layer name to match yours:

layer = QgsProject().instance().mapLayersByName('Your_Layer_Name')[0]
vals = []
fld = 'AREA'
for f in layer.getFeatures():
    vals.append(f[fld])
# If you don't like these colors, change them out for ones you do, using hexcodes,
# RGB codes etc. as long as the items in this list are valid strings you
# can pass to a QColor constructor 
colors = ['#0011FF', '#0061FF', '#00D4FF', '#00FF66', '#00FF00', '#E5FF32', '#FCFC0C', '#FF9F00', '#FF3F00', '#FF0000']
lower = sorted(vals)[0]
upper = sorted(vals)[-1]
step = (upper-lower)/len(colors)
range_list = []
for c in colors:
    cat = [lower, lower+step, c]
    sym = QgsSymbol.defaultSymbol(layer.geometryType())
    sym.setColor(QColor(cat[2]))
    rng = QgsRendererRange(cat[0], cat[1], sym, '{0:.1f}-{1:.1f}'.format(cat[0], cat[1]))
    range_list.append(rng)
    lower = (lower+step)+0.1
renderer = QgsGraduatedSymbolRenderer(fld, range_list)
layer.setRenderer(renderer)
layer.triggerRepaint()

Below is the result of this script run on a sample dataset- IBRA Bioregions of Australia, obtaining the graduation values from the "SHAPE_Area" field.

enter image description here

Based on the comment by @Cushen below, you could obtain a range of color values from an existing styled layer with a graduated ramp of the required number of classes and read those colors into a list object with a simple list comprehension:

styled_layer = iface.activeLayer() # existing layer styled with desired color ramp
colors = [s.color().name() for s in styled_layer.renderer().symbols(QgsRenderContext())]