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.

4
  • Are you wanting to update Singleband gray or Singleband pseudocolor?
    – Joseph
    Jun 16, 2016 at 9:55
  • @Joseph pseudocolor
    – B.Termeer
    Jun 16, 2016 at 12:44
  • Make sure you set the render type to pseudocolor before running the code.
    – Joseph
    Jun 16, 2016 at 12:51
  • can you explain me how i can do this?
    – B.Termeer
    Jun 16, 2016 at 12:54

2 Answers 2

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

8
  • 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, 2016 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, 2016 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, 2016 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, 2016 at 13:35
  • hmm, you can use QgsSingleBandPseudoColorRenderer but i have no shader argument, right?
    – B.Termeer
    Jun 16, 2016 at 13:41
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.

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.