Colour bar for discrete rasters with Matplotlib

  • blog

Plotting raster data can be done in a few lines of code with Matplotlib. Adding a colour bar (color bar) associated to your data can be as simple as plt.colorbar() for example:

This is a nice quick way of displaying a scale for your data. I’ve often wanted to plot discrete (and not continuous) colour bars associated to my data and have wondered how to do this. If I’ve classified an image and plotted it up, the continuous colour bar is not so useful. I went looking for an answer to this on stackoverflow and found some really neat solutions. My favourite of these is this one.

import matplotlib.pyplot as plt
import numpy as np
def discrete_matshow(data):
  #get discrete colormap
  cmap = plt.get_cmap('RdBu', np.max(data)-np.min(data)+1)
  # set limits .5 outside true range
  mat = plt.matshow(data,cmap=cmap,vmin = np.min(data)-.5, vmax = np.max(data)+.5)
  #tell the colorbar to tick at integers
  cax = plt.colorbar(mat, ticks=np.arange(np.min(data),np.max(data)+1))

a=np.random.randint(1, 9, size=(10, 10))
discrete_matshow(a)

Which gives me this:

I like that the ticks are centred and that the colourbar is stepped for discrete data. I am going to use this base code and apply it to a previous notebook to improve the display of the colourbar. So, this blog post is all about loading a Satellite image running a k-means clustering algorithm and plotting that data with a discrete colourbar. The orginal code I wrote is here. If you are interested in more of the details on k-means unsupervised classification of satellite data, that blog post is here.

Applying a discrete colourbar to classified data

I am going to skip over the code to load satellite data and run the classification; it’s all in the associated notebook and described in the previous blog mentioned before. This time round I am using a Sentinel 2 image covering the Isle of Wight.

I have run k-means with five clusters over this data and plotted it up.

There are some obvious problems with the colourbar here. Instead of plotting via the imshow() method, let’s use the code from the stack overflow page:

discrete_matshow(X_cluster)

This gives:

I think this is an improvement. Now to make some adjustments to the discrete_matshow function. In this case I’ll call it discrete_matshow2()

def discrete_matshow2(data):
   #get discrete colormap
   cmap = plt.get_cmap('hsv', np.max(data)-np.min(data)+1)
   # set limits .5 outside true range
   plt.figure(figsize=(10,10)) ## increase fig size
   mat = plt.matshow(data,cmap=cmap,vmin = np.min(data)-.5, vmax = np.max(data)+.5, fignum=1) ## added fignum
   #tell the colorbar to tick at integers
   plt.colorbar(mat, ticks=np.arange(np.min(data),np.max(data)+1), shrink=0.5) ## added shrink

I’ve highlighted the code that I have changed. Firstly, I set the colourmap to ‘hsv’, secondly I created the figure and increased the size of display, thirdly I told plt.matshow that the fignum=1 (or it will error) and finally, on the colourbar I have added shrink=0.5 to reduce the size of the bar in relation to the image. Run discrete_matshow2(data) and you get this:

This is much closer to what I wanted to achieve. I wondered also if I could plot both the image and the classification together and set one transparent. I adapted the above code and made a new function called discrete_matshow3 and added both images and (plt.imshow) and used the parameters zorder and alpha to change the layer ordering, and the transparancy, respectively. These changes are shown in bold below:

def discrete_matshow3(data):
   #get discrete colormap
   cmap = plt.get_cmap('hsv', np.max(data)-np.min(data)+1)
   # set limits .5 outside true range
   plt.figure(figsize=(20,20)) ## increase fig size
   mat = plt.imshow(data,cmap=cmap,vmin = np.min(data)-.5, vmax = np.max(data)+.5, zorder=2, alpha=0.2) ## added fignum
   #tell the colorbar to tick at integers
   plt.colorbar(mat, ticks=np.arange(np.min(data),np.max(data)+1), shrink=0.5)## added shrink
   plt.imshow(img[:,:,:], zorder=1, alpha = 0.5)

This gives me this resulting image. I’m not convinced it adds much to be honest, perhaps it needs some further tweaking?

The main thing is that I was able to plot my data with a discrete colour bar and I think that will make discrete rasters easier to read in future work.

All the code for this post is on my satellite imagery github repo here and the direct link for this corresponding notebook is here.

I am a freelancer able to help you with your projects. I offer consultancy, training and writing. I’d be delighted to hear from you.

Feel free to connect or follow me; I am always keen to talk about Earth Observation.

I am @map_andrew on twitter