12

I have rasters with discrete values (Landuse categories, boolean values...). I would like to be able to style them in a discrete way:

0 -> Red
1 -> Blue
2 -> Green
...

I wish something like the "Unique Categories" in ArcGIS: http://resources.arcgis.com/en/help/main/10.1/index.html#/representing_unique_categories_such_as_land_use/009t00000074000000/

However, all the options I see involve color ramps... Have I missed something?


I've opened a feature request, since there seems to be only workarounds available, not solutions: http://hub.qgis.org/issues/14845

8

You can create a customized style scheme, something like unique categories but you'll have to add all the categories manually ( Atleast I have to, but I'm using an older version of QGIS ).

Right click the raster -> properties -> style. In there choose singleband pseudocolor as the Render type and click on the little red plus to add your own values and corresponding colors. Something along the lines of: enter image description here

Note that you can also use classify to get the values automatically but if you have discrete values you might be better of just adding them manually, depends on how your data is distributed.

If your data follows a pattern, if it is 0,1,2....10, you could set the Mode to equal interval, define the range Min:0,Max:10 and Classes:11, click Classify and you'll get all the values automatically. Then you could alter them as you see fit. enter image description here I know it's not perfect, but this is the best I've found so far. There might be a plugin that deals with this issue.

10
  • 1
    I was hoping for the possibility to automatically retrieve all the possible values of my discrete raster but apparently this does not (yet?) exist. Thanks! – Stéphane Henriod May 19 '16 at 9:23
  • 3
    Thanks, that's indeed the workaround that I use. But I don't find it very intuitive, mostly when I teach Qgis to new users. Moreover, if I have a value "6" that I don't want to display on my raster, I have a problem: all pixels with "6" will bet a color interpolated between "5" and "7". I can of course say that "6" should be considered as NoData or I can can use the raster calculator to create a new raster without "6" but, again, those are all workarounds. Ideally, I would wish a button "Retrieve all single values". I'll check if it's planned for future versions... – Stéphane Henriod May 20 '16 at 4:55
  • 1
    I have created a ticket on a related issue: hub.qgis.org/issues/14449 – Kurt Menke Jan 13 '17 at 0:47
  • 1
    @joaoal Note that this feature has been included around 2 years ago. It goes smoothly now – Stéphane Henriod Jul 1 '20 at 11:19
  • 1
    3.14 I think it was introduced in 3.0. You should in any case update! Tremendous improvements between 2.x and 3.x! – Stéphane Henriod Jul 2 '20 at 17:17
5

Here is a quick and dirty Processing script that does exactly what you are asking for (apologies for the colour choices!). Put it in your Processing scripts directory (eg. C:\Users\.qgis2\processing\scripts) and it will show up in the Processing toolbox under Scripts > Raster.

Credit to Yury Ryabov for the Unique_values_count.py script on which this is based.

EDIT: I am just submitting a pull request to get this into the Processing scripts repository.

##Raster=group
##Generate unique values style=name
##Raster_to_extract_unique_values=raster
##round_values_to_ndigits=number 0

from osgeo import gdal
from random import randint
import math
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from qgis.utils import iface

# Rename verbose input vars
input = Raster_to_extract_unique_values
rdig = round_values_to_ndigits

# Initialize unique values list
sort_values = []
# create set for unique values list
cell_values = set()

# load raster
gdalData =  gdal.Open(str(input))

# get width and heights of the raster
xsize = gdalData.RasterXSize
ysize = gdalData.RasterYSize

# get number of bands
bands = gdalData.RasterCount

# process the raster
for i in xrange(1, bands + 1):
    progress.setText("processing band " + str(i) + " of " + str(bands))
    band_i = gdalData.GetRasterBand(i)
    raster = band_i.ReadAsArray() # This loads the entire raster into memory!
    # count unique values for the given band
    for col in range( xsize ):
        if col % 10 == 0: progress.setPercentage(int(100*col/xsize))
        for row in range( ysize ):
            cell_value = raster[row, col]
            # check if cell_value is NaN - don't add if it is
            if not math.isnan(cell_value):
                # round floats if needed
                if rdig:
                    try:
                        cell_value = round(cell_value, int(rdig))
                    except:
                        cell_value = round(cell_value)
                # Add to the unique values set
                cell_values.add(cell_value)

del(gdalData)

# decide whether to sort by the count-column or the value-column
sort_values = sorted(cell_values)

# Now load the layer and apply styling
layer = processing.getObjectFromUri(input)

qCRS = QgsColorRampShader()

# Build the colour ramp using random colours
colList = ['#ff0000','#ffff00','#0000ff','#00ffff','#00ff00','#ff00ff']

lst = []
for i,val in enumerate(sort_values):
    lst.append(QgsColorRampShader.ColorRampItem(val,QColor(colList[i % 6]),str(val)))

qCRS.setColorRampItemList(lst)
qCRS.setColorRampType(QgsColorRampShader.EXACT)

shader = QgsRasterShader()
shader.setRasterShaderFunction(qCRS)

renderer = QgsSingleBandPseudoColorRenderer(layer.dataProvider(), layer.type(), shader)
layer.setRenderer(renderer)
layer.triggerRepaint()
3
  • That works flawlessly, brilliant stuff! Cheeky request, but any chance of updating the label so that it shows the values in the TOC? For some reason it isn't showing them. – Ed Rollason Jan 12 '17 at 14:47
  • Done - just edited the script – Andy Harfoot Jan 12 '17 at 14:56
  • Thanks a lot! Btw I am putting together some feature description to have such a renderer included in 3.0: docs.google.com/document/d/… Anyone, feel free to check / comment / modify. It would be great to be able to easily style discrete rasters with using any workaround / scripting,... – Stéphane Henriod Jan 13 '17 at 3:26
4

You could try this:

1) Create a simple style in Layer properties, then save it to an export file using the save button. These are in the form:

value, R, G, B, Alpha, label

2) Use r.category in the grass (6 or 7) toolbox of the processing module. This should provide a list of raster values. Copy these values. You can use r.quantile for continuous data.

3) Open the export file you saved earlier in a text editor (e.g. notepad++ on windows, loads of choice on linux). Paste the values and reformat to suit.

@Stephane

3a) For discrete values, you can set the alpha to 0, or delete or comment out (using # at the start of the line) any data you don't want to display in the category file.

3b) For continuous values, create a row for the start value and another for the end value. Set the alpha for both to 0.

4) For example, say you have created three files:

categories.txt - output of r.category, copy and paste, add two blank lines at the top. Total length 1 column. Check the number of unique values, and make a style with the same number of entries in QGIS.

colours.txt - a QGIS Generated Color Map Export File with the colours you like. Total length 6 columns.

labels.txt - file with the labels you want, add two blank lines at the top. Total length 1 column.

Use a spreadsheet to merge the files and save as a csv. Open this in QGIS.

Alternatively, on the command line (bash or msys):

paste -d, categories.txt colours.txt labels.txt |cut -d, -f 1,3-6,8 > new_style.txt

See also:

https://pvanb.wordpress.com/2014/02/05/creating-a-qgis-color-map-from-text-file/

Advanced colour palette for raster data

1
  • Very nice method! But still, if there are some categories in my raster that I don't want to display, I'm stuck... – Stéphane Henriod May 22 '16 at 5:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.