Leviathan
Leviathan

Reputation: 11

Applying style to raster layer with pyqgis - style panel and toc issues

I'm trying to apply a custom colour ramp to a single band QgsRasterLayer using PyQGIS. The colour ramp comes from matplotlib. For the map-canvas this works fine. However, in the TOC the values reach from 0 to 255 (rather than the actual min/max values), though the colour ramp seems correct (for the tiny range). In the style panel it shows the last colour ramp I used (even after restarting QGIS). Is it possible to update these correctly?

In the following example I apply viridis.

screenshot of TOC and style panel in QGIS after code execution

Example Code:

from matplotlib import colormaps
import numpy as np

lyr = iface.activeLayer()
if isinstance(lyr, QgsRasterLayer):
    if lyr.bandCount() == 1:
        # define color map by name
        map = colormaps['viridis']

        # get min/max values
        stats = lyr.dataProvider().bandStatistics(1, QgsRasterBandStats.All)
        val_min = stats.minimumValue
        val_max = stats.maximumValue

        # limit number of color steps to 20
        if map.N > 20:
            colors = map(np.linspace(1, map.N, 20, dtype=np.int16))
        else:
            colors = map(range(1,map.N))
        colorsN = len(colors)

        # raster values for the color steps
        valList = np.linspace(val_min, val_max, colorsN)

        # making a colorRamp-list with
        colorsList = []
        for idx, col in enumerate(colors):
            qcol = QColor(int(col[0]*255), int(col[1]*255), int(col[2]*255))
            val = valList[idx]
            #print(val, qcol)
            colorsList.append( QgsColorRampShader.ColorRampItem(val, qcol) )

        # apply code (cf. https://docs.qgis.org/3.34/en/docs/pyqgis_developer_cookbook/raster.html#single-band-rasters)
        fcn = QgsColorRampShader()
        fcn.setColorRampType(QgsColorRampShader.Interpolated)
        fcn.setColorRampItemList(colorsList)
        shader = QgsRasterShader()
        shader.setRasterShaderFunction(fcn)
        renderer = QgsSingleBandPseudoColorRenderer(lyr.dataProvider(), 1, shader)                
        lyr.setRenderer(renderer)
        lyr.renderer().createLegendNodes(QgsLayerTreeLayer(lyr.id()))

lyr.triggerRepaint()
lyr.emitStyleChanged()

Upvotes: 1

Views: 140

Answers (1)

Leviathan
Leviathan

Reputation: 11

With the help of this answer I could solve it eventually:

  • with renderer.setClassificationMin and Max most problems go away; just in the style panel the color ramp isn't shown in the dropdown
  • using QgsColorRamp instead QgsColorRampShader solves the remaing issue

So the working code is:

from matplotlib import colormaps
import numpy as np

lyr = iface.activeLayer()
if isinstance(lyr, QgsRasterLayer):
    if lyr.bandCount() == 1:
        # define color map by name
        map = colormaps['viridis']

        # get min/max values
        stats = lyr.dataProvider().bandStatistics(1, QgsRasterBandStats.All)
        val_min = stats.minimumValue
        val_max = stats.maximumValue

        # limit number of color steps to 20
        if map.N > 20:
            colors = map(np.linspace(1, map.N, 20, dtype=np.int16))
        else:
            colors = map(range(1,map.N))
        colorsN = len(colors)

        # making a colorRamp-list with
        colorsList = []
        for idx, col in enumerate(colors):
            qcol = QColor(int(col[0]*255), int(col[1]*255), int(col[2]*255))
            if idx == 0:
                startColor = qcol
            elif idx == colorsN - 1:
                endColor = qcol
            else:
                val = idx * 1/colorsN
                colorsList.append( QgsGradientStop((idx + 1) * 1 / (colorsN - 1), qcol) )
        
        colorRamp = QgsGradientColorRamp(startColor, endColor, False, colorsList)

        renderer = QgsSingleBandPseudoColorRenderer(lyr.dataProvider(), 1)
        renderer.setClassificationMin(val_min)
        renderer.setClassificationMax(val_max)
        renderer.createShader(colorRamp)
        
        lyr.setRenderer(renderer)

lyr.triggerRepaint()

Upvotes: 0

Related Questions