Sometimes I create hillshades using the QGIS hillshade plugin and then overlay the original DEM over it. I set the DEM to have a false colour pallette and set it to be semi-transparent to produce something like this:
That is all well and good but a bit impractical. It would be much nice to have the colour pallette permanetly assigned to the hillshade. Also I want to be able to clip and mask the resulting raster to the boundary specified in a shapefile. Fortunately, GDAL provides all the tools you need to make this happen. There are already some nice articles (like this one) that describe parts of this workflow but I am writing this because I wanted to note the additional steps that I took to make this work well for me.
Before you begin
Before you begin you should have:
- a raster containing digital elevation data (DEM) – in my case its called ‘alt.tif’
- a vector layer (typically a shapefile) containing the area of interest for your final product – in my case its called ‘tanzania.shp’
Create the hillshade image
The first thing we need to do is generate a hillshade. There is a nice python plugin for doing this in QGIS, you can do it in GRASS using the QGIS-GRASS plugin. But in this article I’m going for an all-GDAL approach so we will be using the handy gdaldem command line application. I won’t be explaining the parameters I have used here as they are well explained in the gdaldem manual pages.
So to create our hillshade we do something like this:
gdaldem hillshade alt.tif shade.tif -z 5 -s 111120 -az 90 Which will produce something like this:
Create the terrain map
Here we want to create a pleasing colour palette for the terrain. There is a lot of content on colour theory available out there on the internet – googling for ‘colorbrewer‘ would be a good place to start if you want to learn more. Another favourite site of mine is colourlovers.com and for this tutorial I decided to use a pallette from there to see how it would turn out.
Once you have selected a suitable colour pallette (around 5 or 6 colour classes should do it), the next thing you need to do is get some information about the range of values contained in your DEM. Once again you can easily point and click your way to this in QGIS, but here is the way to get it in gdal from the command line:
gdalinfo -mm alt.tif
The resulting output includes the computed min/max for Band 1 – which is what we are after:
Band 1 Block=1334x3 Type=UInt16, ColorInterp=Gray Computed Min/Max=1.000,5768.000 NoData Value=65535
Ok so our minimum height is 1m and maximum is 5768m – Tanzania is the home of Kilimanjaro after all! So lets split that range up into 5 classes to match the ‘Landcarpet Europe‘ colourlover pallette I selected. I set nearly white as an additional colour for the highest altitude range.
65535 255 255 255 5800 254 254 254 3000 121 117 10 1500 151 106 47 800 127 166 122 500 213 213 149 1 218 179 122
The value in the first column is the base of the scale range. The subsequent values are RGB triplets for that range. I saved this as a text file called ‘ramp.txt’ in the same directory as my ‘alt.tiff’ dataset. You will notice that I made the value ranges closer together at lower altitudes and wider appart at higher altitudes. You may want to experiment a little to get pleasing results – especially if you have a relatively small number of high lying terrain pixels and the rest confined to lower lying areas.
Also note that I assigned the highest terrain ‘nearly white’ so that I could reserve white (RGB: 255 255 255) for the nodata value (65535) in this dataset. We will use the fact that white is only used for nodata to our advantage when we do a clip further on in these instructions.
Ok now we can use gdaldem to generate the terrain map:
gdaldem color-relief alt.tif ramp.txt relief.tif
This is what my relief map looked like:
Don’t worry about the fact that it does not resemble the colour pallette you have chosen – it will do in the next step!
Merging the two rasters
The next step is to combine the two products. I used Frank Warmerdam’s handy hsv_merge.py script for this purpose.
./hsv_merge.py relief.tif shade.tif colour_shade.tif
Which produced this:
You may have noticed that it is only at this point that the colours of our product resemble the original pallette we used.
One little gotcha with the hsv_merge.py script is that it throws away our nodata values, so what was sea before (and nodata in my original alt.tif dataset) is now white (RGB: 255 255 255).
Clipping and masking
You may have everything you need from the above steps. Alternatively you can also clip and mask the dataset using a shapefile.
gdalwarp -co compress=deflate -dstnodata 255 -cutline Tanzania.shp \
colour_shade.tif colour_shade_clipped.tif
My final image is now a compressed tif with nodata outside of the country of Tanzania and looks like this:
A final note
One of the things I love about the command line is the repeatable and reusable workflows it allows for. I can distill everything in this blog post into a sequence of commands and replay them any time. If you are still stuck doing things in a GUI only way, give BASH a try – you can really do powerful geospatial data processing with it!





I’ll recommend using virtual rasters in gdal (gdalbuildvrt) for processing in a number of steps – it speeds the process up and leaves only small files behind – except the resulting file of course.
Hi Karl
Thanks for your comments. In which steps would you have used vrt’s here? I use vrt’s a lot myself, but mainly when I want to represent several discrete files as a single raster.
Regards
Tim
My computer is not too strong, so my coffee becomes cold waiting.. And our IT dep is always crying on lack of disk and backup volume.
My humble experience is, that the calculations are faster using vrt, e.g. 1. input DEM, 2. reprojecting (warp) to a vrt if needed, 3. hillshade as vrt, 4. relief as vrt, 5. colour_shade as vrt, 6. colour_shade_clipped as tif.
However, if you need to use the intermediate files (relief.tif, shade.tif etc.) for other purposes for the end user, I’ll make them as tiffs.
Thanks for the post on gdal. If would be great with more examples around. Franks gdal.org is not filled up with easy-to-understand examples – if you are a beginner.
[...] Very educational: Tim Sutton’s A workflow for creating beautiful relief shaded dems using GDAL and QGIS. [...]
[...] Tim Sutton from Linfiniti has a great post on creating beautiful shaded relief dems using GDAL. [...]
[...] 3D рельеф в QGIS (используя Grass) и теневая отмывка в GDAL. [...]
[...] Sutton’s excellent post from last year gives a recipe for creating a professional looking color relief background map from [...]
Very useful article!
Are there any basic instructions for running that hsv_merge.py script. I save it to my OsGeo4W/bin folder with the other gdal scripts and utilities but it can’t find the osgeo module.
Hi
You need to be sure to run it from within an osgeo4w dos shell so that if can find your python modules. I don’t have a windows machine handy to give you the exact procedure but doing the aforementioned should work.
Regards
Tim
[...] of the most popular posts on my blog ever is my article about creating GDAL hillshades: ‘A workflow for creating beautiful relief shaded DEMS using GDAL‘. In the techniques outlined in the aforementioned article, colours are assigned across a [...]