giCentre Utilities

Colour tables

Colour table files allow the rules that map data values to colours to be stored and shared between applications. The format used here is that used by LandSerf and TreeMappa. It consists of a collection of colour rules each of which specifies a data value to colour mapping. Colour rules can be discrete indicating that only the specified data value should be mapped to a colour, or continuous indicating that any a range of data values can be mapped to colours. A large number of preset colour tables can be created including all LandSerf and ColorBrewer schemes.

colour table bar

Using colour tables in Processing

You will need to import the relevant colour classes into your sketch with the line (note the English spelling of 'colour'):

import org.gicentre.utils.colour.*; 

There are a number of methods available for creating colour tables, adding colour rules and opening and saving to files. For a full documentation of the methods see the ColourTable API documentation.

The example sketch below shows how the more common methods might be used:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import org.gicentre.utils.colour.*;    // For colour tables.

// Sketch to demonstrate the use of LandSerf style colour tables. A ColourTable 
// object consists of a set of ColourRules. Each rule maps a numeric value to a
// colour. The colour to be associated with any value can be found by calling the
// findColour() method of a colour table. If the rules are continuous, the 
// returned colour is interpolated between the two colour rules closest to the 
// given value. If rules are discrete, only exact matches are mapped to a colour.
// Version 1.1, 12th July, 2009.
// Author Jo Wood.

// ------------------ Object variables --------------------

ColourTable cTable1, cTable2, cTable3;      // Colour tables to use.

// ------------------- Initialisation ---------------------

/** Creates the colour tables to display and save as files.
 */
void setup()
{
  size(500,250);
  noLoop();
  smooth();

  // Create a continuous Brewer colour table (YlOrBr6).
  cTable1 = new ColourTable();
  cTable1.addContinuousColourRule(0.5/6, 255,255,212);
  cTable1.addContinuousColourRule(1.5/6, 254,227,145);
  cTable1.addContinuousColourRule(2.5/6, 254,196, 79);
  cTable1.addContinuousColourRule(3.5/6, 254,153, 41);
  cTable1.addContinuousColourRule(4.5/6, 217, 95, 14);
  cTable1.addContinuousColourRule(5.5/6, 153, 52,  4);
  
  // Create a preset colour table and save it as a file
  cTable2 = ColourTable.getPresetColourTable(ColourTable.IMHOF_L3,0,1);
  ColourTable.writeFile(cTable2,createOutput("imhofLand3.ctb"));
  
  // Read in a colour table from a ctb file.  
  cTable3 = ColourTable.readFile(createInput("imhofLand3.ctb")); 
}


// ------------------ Processing draw --------------------

/** Draws the colour tables as horizontal colour bars.
 */
void draw()
{
  background(255);
  
  // Draw the continuous Brewer colour table.
  float inc = 0.001;
  for (float i=0; i<1; i+=inc)
  {
    fill(cTable1.findColour(i));
    stroke(cTable1.findColour(i));
    rect(width*i,10,width*inc,50);
  }

  // Draw the discrete version of the Brewer colour table.
  stroke(0);
  inc = 1/6.0;
  for (float i=0; i<1; i+=inc)
  {
    fill(cTable1.findColour(i + 0.5*inc));
    rect(width*i,70,width*inc,50);
  }
  
  // Draw the preset colour table.
  inc = 0.001;
  for (float i=0; i<1; i+=inc)
  {
    fill(cTable2.findColour(i));
    stroke(cTable2.findColour(i));
    rect(width*i,130,width*inc,50);
  }
  
  // Draw the colour table loaded from a file.
  inc = 0.001;
  for (float i=0; i<1; i+=inc)
  {
    fill(cTable3.findColour(i));
    stroke(cTable3.findColour(i));
    rect(width*i,190,width*inc,50);
  }
}

When run, it produces output as follows:

Colour bars from Processing

Preset colour tables

A large number of preset colour schemes can be created easily by calling the ColourTable.getPresetColourTable() method. This includes all LandSerf and ColorBrewer schemes. ColorBrewer schemes are particularly useful for showing statistical data that are sequentially continuous, diverging or categorical. For a full list of ColorBrewer schemes, see the ColourTable API. The available schemes are shown below:

ColourBrewer schemes

CIELab colour space

CIELab colour space is an approximately perceptually uniform space in that any two pairs of colours a fixed distance apart in CIELab space are equally distinguishable regardless of their location. The class CIELab in the colour package can convert to and from this space using the methods getColour() and getLab(). In the example below, that conversion is done in line 40. Because not all CIELab values result in a visible colour (so-called 'out-of-gamut' colours), the final parameter of this method allows the option of extrapolating colours that fall outside of gamaut to their nearest visible colour. If false, out-of-gamut colours are returned as null objects, if true, the method guarantees that a visible colour is always returned, regarless of L,a,b values.

Note that in processing, to convert a Java Color object into a valid Processing colour value, call its getRGB() method (see line 43 below for an example).

CIELab colour slice with L=63

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import org.gicentre.utils.colour.*; 

// Sketch to demonstrate the use of CIELab colour conversion. 
// Use up and down arrows to change the CIELAB 'L' value.
// Version 1.0, 7th September, 2009.
// Author Jo Wood.

// ------------------ Object variables --------------------

CIELab converter;       // Does the colour conversion work.
float L;                // Current L value for CIELab slice

// ------------------- Initialisation ---------------------

void setup()
{
  size(400,400);
  converter = new CIELab();
  L = 50;
}

// ------------------ Processing draw --------------------

void draw()
{
  background(255);
  noStroke();
  noLoop();
      
  // Transform from colour space to screen space
  scale(width/200.0,-height/200.0);
  translate(100,-100);

  // Draw a slice through CIELab space.
  float inc = 1.0;
  for (float a=-100; a <=100; a+=inc)
  {
    for (float b=-100; b<=100; b+=inc)
    {
      Color colour = converter.getColour(L,a,b,false);
      if (colour != null)
      {
        fill(colour.getRGB());
        rect(a,b,inc,inc);
      }
    }
  }
}

// ------------ Processing keyboard handling --------------

void keyPressed()
{  
  if (key==CODED)
  {
    if ((keyCode==UP) && (L<100))
    { 
      L++;
      println("L="+L);
      loop();
    }
    else if ((keyCode==DOWN) && (L>0))
    {
      L--;
      println("L="+L);
      loop();
    }    
  }
}