2

For a script, I want to update the exsiting min and max of a colour ramp with a new one. In the image you can see what I want to update. enter image description here

To do so I tried to follow this thread. I tested the following code to update the raster:

min = 1
max = 5

l.renderer().setClassificationMin(min)
l.renderer().setClassificationMax(max)

I get the following error:

AttributeError: 'QgsSingleBandGrayRenderer' object has no attribute 'setClassificationMin'

I can't find any information about this code on the internet. The answer of the thread gives a solution but you HAVE to update the colour as well.

I'm wondering if somebody know how to update the min and max without updating the colour.

B.Termeer
  • 227
  • 1
  • 8

2 Answers2

1
  1. Double-click your raster to access its properties then go to the Style tab as you have shown in your question. Make sure the render type is set to Singleband pseudocolor:

    Raster render type


  1. Click Apply and OK.

  1. Make sure the raster layer is still selected and run the following code to change the min and max values:

    rLayer = iface.activeLayer()
    provider = rLayer.dataProvider()
    stats = provider.bandStatistics(1, QgsRasterBandStats.All, rLayer.extent())
    
    min = 1
    max = 5
    
    rLayer.renderer().setClassificationMin(min)
    rLayer.renderer().setClassificationMax(max)
    

    The min and max values should be the only settings which are updated.

    New min and max values

Joseph
  • 75,746
  • 7
  • 171
  • 282
  • 1
    Thank you very much! can you briefly explain how you also update the layer? because the min and max is filled but it isn;t classified yet. I m sorry that i ased so much – B.Termeer Jun 16 '16 at 13:14
  • @B.Termeer - Most welcome! No need to apologise but not sure I can help you much there. In your question, you included a link to a thread. The answer in that thread allows you to apply classifications. – Joseph Jun 16 '16 at 13:31
  • i was aleready looking in that thread. I tried it with : rLayer.triggerRepaint() but that was not the trick – B.Termeer Jun 16 '16 at 13:34
  • @B.Termeer - triggerRepaint() only updates the appearence of the layer shown on the map, it doesn't apply to its settings. – Joseph Jun 16 '16 at 13:35
  • hmm, you can use QgsSingleBandPseudoColorRenderer but i have no shader argument, right? – B.Termeer Jun 16 '16 at 13:41
  • @B.Termeer - You would have to create it and define the colours you want. You could ask another question regarding how the Classify button actually classifies colours for the values. I can't find the code for this but should be generic and would allow you to define min and max values without having to constantly write code for individual colours. – Joseph Jun 16 '16 at 13:48
  • @B.Termeer - Your welcome, I am interested in seeing how this could be done with python :) – Joseph Jun 16 '16 at 14:10
  • Me too, it is also very usefull in my daily work if i can keep the existing colour ramp but change the min and max value. I have to do this a lot and it will probaly saves me 5 sec every time. – B.Termeer Jun 16 '16 at 14:12
0

For me, all the reclassification codes didn't work. I also got this type of error and many others. So I decided to bypass this by using another approach: I generated by own .qml style file with a python script and then applied it to the active layer.

Here is how it works:

First create your .qml style that you want to apply to a layer, save it as a template in .txt format. The idea is to create a copy of this file and "manually" update the color ramp values between Min and Max from layer statistics.

QML structure

Then create a python script and call it: AUTOMATIC_QML.py It will copy the .txt file template to a new .qml file and fill it with the values defined in between Min and Max.

Inside this script paste the following code, which you will need to modify:

import numpy as np
import os

def copy_text_to_qml(input_file, output_file): try:

    # Erases the text from previous file (resets it)
    file_to_delete = open(output_file,'w')
    file_to_delete.close()

    with open(input_file, 'r') as text_file:
        lines = text_file.readlines()

    with open(output_file, 'w') as qml_file:
            qml_file.write(''.join(lines))                   
except FileNotFoundError:
    print('Input file not found.')

def replace_line(file_path, line_number, new_text):

""" 
This function specifies the Min and Max values 
of the color ramp

"""
try:
    with open(file_path, 'r') as file:
        lines = file.readlines()

    # Check if the line number is within range
    if line_number < 1 or line_number > len(lines):
        print('Invalid line number')
        return

    lines[line_number - 1] = new_text + '\n'

    with open(file_path, 'w') as file:
        file.writelines(lines)

    #print(f'Line {line_number} replaced successfully!')
except FileNotFoundError:
    print('File not found.')

def replace_character_range(file_path, line_number, new_text):

"""    
# This function is used to load the predefined color style and update  
# the lines with 
# the new values calculated in between Min and Max values  

# Notice that the user defined preset color style must have exactly 255 
# color classes 
"""
try:

    with open(file_path, 'r') as file:
        lines = file.readlines()

    # Check if the line number is within range
    if line_number < 1 or line_number > len(lines):
        print('Invalid line number')
        return

    line = lines[line_number - 1]

    end_index = len(line) - 5
    # Check if the character range is valid
    if 66 < 0 or end_index >= len(line):
        print('Invalid character range')
        return

    replaced_line = line[:66] + new_text + line[end_index + 1 :]
    lines[line_number - 1] = replaced_line

    with open(file_path, 'w') as file:
        file.writelines(lines)

    #print(f'Character range [{66}:{end_index}] in line {line_number} replaced successfully!')
except FileNotFoundError:
    print('File not found.')

def Set_New_QML_File(MIN, MAX, PATH):

""" 
Example of how to call this function
#MIN, MAX = 1, 2    
#Set_New_QML_File(MIN, MAX)
"""       
# Do not change this (unless you have your own color table preset in specified location)
input_path = PATH
output_path = input_path[:-4] + '.qml' # creates a temporary .qml file 
copy_text_to_qml(input_path, output_path)  # copy preset to .qml file

# Replaces 2 specific lines containing Min and Max values of the Color 
# Table that we want to update

new_text1 = '    <rasterrenderer alphaBand="-1" opacity="1" classificationMax=' + \
    '"{}"'.format(str(MIN)) + ' classificationMin=' + '"{}"'.format(str(MAX)) + \
    ' type="singlebandpseudocolor" nodataColor="" band="1">'

new_text2 = '        <colorrampshader colorRampType="INTERPOLATED" minimumValue=' + \
    '"{}"'.format(str(MIN)) + ' classificationMode="1" maximumValue=' + \
    '"{}"'.format(str(MAX)) + ' labelPrecision="4" clip="0">'

replace_line(output_path, 126, new_text1)
replace_line(output_path, 137, new_text2)

Lines_to_replace = np.arange(149, 404)  
# You may need to adjust those values 

# Create 255 classes (values of color table) between min and max
ColorClasses = np.linspace(MIN, MAX, 255)

for line_id, line in enumerate(Lines_to_replace):  
    color_class = str(ColorClasses[line_id])      
    #print(color_class)
    replace_character_range(output_path, line, color_class)

Then you create another python script inside the QGIS Python Console Editor and paste the following code:

# Import the necessary libraries
import os
import numpy as np

Make sure that the AUTOMATIC_QML.py file

is in the same folder as this script

from AUTOMATIC_QML import Set_New_QML_File

Get the list of all layers displayed in QGIS

layers = QgsProject.instance().mapLayers()

""" SELECT THE PATH TO AVG_colorstyle_PRESET.txt file """ PATH = 'C:/PATH_TO_YOUR_SAVED_QML_FILE/AVG_colorstyle_PRESET.txt'

print(layer.name())

Compute Min and Max values of the active layer

provider = layer.dataProvider() stats = provider.bandStatistics(1, QgsRasterBandStats.All) MIN = stats.minimumValue MAX = stats.maximumValue Set_New_QML_File(MIN, MAX, PATH)

Once the color table .qml file is updated it is applied to the layer

#Path to the updated QML style file qml_file_path = PATH[:-3] + "qml"

Apply the QML style to the active layer

layer.loadNamedStyle(qml_file_path) layer.triggerRepaint()

Refresh the layer in the QGIS interface

iface.layerTreeView().refreshLayerSymbology(layer.id())

print('QML style applied to active layer successfully!')

Thisscript will call the AUTOMATIC_QML.py script and apply its new generated QML to your active layer.

This is definitely not the best way of doing it, but it has the advantage of not using any qgis.core utilities. It uses only basic python functions does not consume a lot of time.