I'm shading a raster layer using a QgsColorRampShader, using discrete values. Basically, I want to make the value '255' a black color because it is a null value. Here is the step-by-step through my code.
First, here is a link to the raster file I'm using: https://github.com/epurpur/PyQGIS-Scripts/blob/master/AestheticMin.tif
In this raster layer, the value range of the pixels is 3 to 900. First I calculate the value range of the raster layer, because I want to check to know what the values are:
layer = iface.activeLayer()
print("Active Layer: ", layer.name())
provider = layer.dataProvider()
extent = layer.extent()
#Using RasterBandStats to find range of values in raster layer
stats = provider.bandStatistics(1, QgsRasterBandStats.All)
min_val = stats.minimumValue #minimum pixel value in layer
max_val = stats.maximumValue #maximum pixel value in layer
value_range = range(int(min_val), int(max_val+1)) #Range of values in raster layer. Without +1 doesn't capture highest value
The important part is the value_range variable, which captures the range of pixel values for the raster layer.
Because each raster will have a different value_range (probably), this is dynamic. Here is how I calculate the first half and second half values for my layer now that I know the value_range:
import numpy
first_half_max = round(numpy.percentile(value_range, 50), 2)
first_half_min = round(min_val, 2)
second_half_max = round(numpy.percentile(value_range, 100), 2)
second_half_min = round((first_half_max + .01), 2)
Now, I shade my raster layer:
raster_shader = QgsColorRampShader()
raster_shader.setColorRampType(QgsColorRampShader.Discrete)
colors_list = [
QgsColorRampShader.ColorRampItem(first_half_max, QColor(204, 219, 255), f"{first_half_min} - {first_half_max}"), \
QgsColorRampShader.ColorRampItem(second_half_max, QColor(153, 184, 255), f"{second_half_min} - {second_half_max}") ]
raster_shader.setColorRampItemList(colors_list)
shader = QgsRasterShader()
shader.setRasterShaderFunction(raster_shader)
renderer = QgsSingleBandPseudoColorRenderer(layer.dataProvider(), 1, shader)
layer.setRenderer(renderer)
layer.triggerRepaint()
This produces the following result. This is OK, except there are pixels with a value of '255' (no value) that are lost in this map:
The desired output would look like this. Notice, the black values around the map and then some pixels inside the map with missing values:
@J.Monticolo's answer below works, but only if the values of the raster layer are less than 255. If there are values greater than 255, the 255 value is lost. Here is how J.Monticolo creates the color's list (all is the same except the color's list, so I am only including colors_list):
colors_list = [
QgsColorRampShader.ColorRampItem(first_half_max, QColor(255, 255, 255), f"{first_half_min} - {first_half_max}"), \
QgsColorRampShader.ColorRampItem(second_half_max, QColor(153, 184, 255), f"{second_half_min} - {second_half_max}"), \
QgsColorRampShader.ColorRampItem(255, QColor(0, 0, 0), 'No Value') ]
He specifically isolates the value '255' in the last line, where he defines it as a black value. But when using this code with my raster layer, I get the following result:
The legend is correct. But notice, because the value 255 falls within the first_half values, it is shaded white instead of black.
Can I isolate the '255' value while using a range of values to shade the others?