OWS Configuration - Colour-Ramp Styles

Colour-Ramp Styles

Colour-ramp Styles are styles where a single continuous index value is calculated from the raw data for each pixel, and that index value is mapped to a graduated colour ramp for display.

Colour-ramp styles support the elements common to all styles.

Colour-ramp styles support automatic legend generation. Specialised legend configuration is described below.

Note

On spelling

I am Australian and spell it “colour”. Most software packages use the US spelling “color”. Within the configuration file format we use the software conventional spelling, but within the text of this documentation, I use the UK/Australian spelling.

Calculating the Index Value

There are two methods to specify the calculation of the index value:

Expressions (simple calculations)

index_expression

The index_expression entry takes a string written in a simple expression language. Lexical units are floating point or integer constants, or band names (alias-aware). Simple operators (+, -, /, , *) and parentheses work in the usual manner. Note that you do NOT need to explicitly specify needed_bands when using an index_expression.

E.g.

# Simple nir/red NDVI
"index_expression": "(nir-red)/(nir+red)",

Functions (complex calculations)

For more complex calculations than are supported by the expression syntax, The index_function entry can define how the index is calculated at each pixel using an arbitrary Python function. The bands needed for the calculation must be declared in the needed_bands list entry.

index_function

The index_function allows the user to declare a callback function to calculate the index value using OWS’s function configuration format. The function is expected to take an xarray Dataset containing all the bands in the needed_bands list (plus any additional arguments handled by the function configuration format); and returns an xarray Dataset containing the index value.

A small library of general purpose band math functions are provided in datacube_ows.band_utils.

needed_bands list

The needed_bands entry must list the names (or aliases) of all the bands required by the index_function.

E.g.:

# Simple nir/red NDVI
"index_function": {
     "function": "datacube_ows.band_utils.norm_diff",
     "pass_product_cfg": True,
     "kwargs": {
         "band1": "nir",
         "band2": "red"
     }
},
"needed_bands": [ "red", "nir" ]

Colour Ramps

There are three ways to define the colour map:

  1. Use the default colour ramp (A fairly garish rainbow ramp running from dark purple through blue, green, yellow, orange to dark red.)

    To use the default colour ramp, just use the range style config entry.

  2. Use a pre-defined MatPlotLib colour ramp.

    To use a MatPlotLib colour ramp, use the mpl_ramp and range style config entries.

  3. Define your own colour ramp.

    To define a custom colour ramp, use the color_ramp style config entry.

Ramp Scale (Range)

For the Matplotlib colour ramps and the default colour ramp, you need to specify a value range over which the colour ramp is applied. The range element can be set to a tuple containing the index function values that should be mapped to the lowest and highest colour ramp values.

Values outside the configured range are clipped to the closest extreme of the colour ramp.

E.g.:

"range": [-1.0, 1.0]

mpl_ramp

You can use any named matplotlib colour ramp, see the matplotlib documentation for details. for a list of supported ramps.

Matplotlib colour ramps run from 0.0 to 1.0 to scale them to the output of your index function, define a range.

E.g.:

"mpl_ramp": "RdBu",
"range": [0.0, 1200.0]

Manual color_ramp

A colour ramp can be created manually using the color_ramp style configuration entry. color_ramp should be a list of colour point definitions. Each colour point definition describes a mapping from a value to a colour.

The list should be sorted in order of ascending value. If the index function value for a pixel exactly matches the first colour point definition value, then that definition’s colour is used. A pixel with a value less than the lowest value in the ramp will be the colour of the first colour point. A pixel with a value greater than than the highest value in the ramp will be the colour of the last colour point.

Pixels with index function value in between two colour point values will have be coloured a average of the rgb values of those two colour points, weighted by the difference between the pixel index function value and the values of the two colour points.

Colour Point Definition

Each Colour Point Definition must have a numeric value and a color in html hex format (e.g. #FFFFFF, #ffffff, #FFF and #fff all refer to pure white).

A Colour Point may also optionally have an alpha entry which should be a floating point entry between 0.0 (fully transparent) and 1.0 (fully opaque). If not provided, alpha defaults to fully opaque.

A Colour Point may also have an optional “legend” section which affects automatic legend generation, and is discussed below.

E.g.:

# <0: transparent
# 0: black
# 0-1: ramping from black to red
# 1-10: ramping from red to blue
# >10: blue
"color_ramp": [
   {
       "value": -0.00000000001,
       "color": "#000",
       "alpha": 0.0
   },
   {
       "value": 0.0,
       "color": "#000",
   },
   {
       "value": 1.0,
       "color": "#F00",
   },
   {
       "value": 10.0,
       "color": "#00F",
   }
],

Legend Configuration

Colour-ramp styles support automatic legend generation.

Automatic legend generation can be deactivated using the show_legend and url legend elements common to all styles. (show_legend is True by default for colour-ramp styles.)

Legend Title

The legend title defaults to the style name, but can be over-ridden:

E.g.:

"legend": {
    # Legend title will be display as "This is a nice legend"
    "title": "This is a nice legend"
}

You can optionally set units for the legend, which are placed in parentheses after the title. The default is to not display units:

"legend": {
    # Legend title will be display as "This is a nice legend(%)"
    "title": "This is a nice legend",
    "units": "%"
}

Legend Range

The legend range defaults to the range for the default colour ramp or MatPlotLib color ramps.

For manual colour ramps, the default range is between the values of first and last colour point definitions in the ramp, excluding any leading or trailing colour points that are full transparent (alpha=0.0).

To override the default range, use the begin and/or end entries in the legend section. They may be set using integers, numeric strings or floats. The vaguries of floating point arithmetic can cause unexpected behaviour with tick generation (discussed below), so it is strongly recommended to use numeric strings or integers.

E.g.:

# Integers, OK
"legend": {
    "begin": 0,
    "end": 99,
},

# Non-integers
# avoid floats as they may cause issues with tick generation.
# Use numeric strings instead, like this:
"legend": {
    "begin": "0.0",
    "end": "0.3",
},

Legend Ticks

“Ticks” are the labeled points along the ramp legend. The default behaviour is to have exactly two ticks, at the minimum and maximum values. This can be over-ridden by any of the following alternative methods:

Regularly spaced ticks, by size (ticks_every)

Ticks are placed at steps of the indicated size, starting from the beginning of the legend range. As with “begin” and “end”, numeric strings should be used in preference to floats.

E.g.:

"legend": {
    # Ticks at 0.0, 0.5 and 1.0
    "begin": "0.0",
    "end": "1.0",
    "ticks_every": "0.5",
}

"legend": {
    # Ticks at 0.0, 0.3, 0.6 and 0.9
    # Note that there will be no tick at the maximum position (1.0)
    "begin": "0.0",
    "end": "1.0",
    "ticks_every": "0.3",
}
Regularly spaced ticks, by count (tick_count)

The indicated number of ticks are spread evenly along the legend. The count includes the “end” tick but not the “begin” tick.

E.g.:

"legend": {
    # Tick at 0.0 only
    "begin": "0.0",
    "end": "1.0",
    "ticks_count": 0,
}

"legend": {
    # Ticks at 0.0 and 1.0
    # This is the default behaviour if no tick generation
    # option is specified
    "begin": "0.0",
    "end": "1.0",
    "ticks_count": 1,
}

"legend": {
    # Ticks at 0.0, 0.2, 0.4, 0.6, 0.8, 1.0
    "begin": "0.0",
    "end": "1.0",
    "ticks_count": 5,
}
Explicit ticks (ticks)

Tick locations can also be specified explicitly by setting ticks to a list of values. Again, please use numeric strings rather than floats.

E.g. the following are not possible with tick_count or ticks_every:

"legend": {
    # No ticks at all
    "begin": "0.0",
    "end": "1.0",
    "ticks": []
}

"legend": {
    # Evenly spaced ticks with no ticks on the extremes of the range.
    "begin": "0.0",
    "end": "1.0",
    "ticks": ["0.2", "0.4", "0.5", "0.6", "0.8"]
}

"legend": {
    # Unevenly spaced ticks
    "begin": "-5.0",
    "end": "5.0",
    "ticks": ["-5.0", "-1.0", "0.0", "1.0", "5.0"],
}

Tick Labels

Tick labels can be customised as follows:

decimal_places

The number of decimal places to display in tick labels. The default is one.

E.g.:

"legend": {
    # Tick labels: "0.00", "0.25", "0.50", "0.75", "1.00"
    "begin": "0.00",
    "end": "1.00",
    "ticks_every": "0.25",
    "decimal_places": 2
}

"legend": {
    # Tick labels: "0", "1", "2", "3", "4", "5"
    "begin": "0.0",
    "end": "5.0",
    "ticks_every": "1.0",
    "decimal_places": 0
}
Prefixes and suffixes

The “default” entry in the “tick_labels” table can set prefixes and suffixes to be added to all tick labels.

E.g.:

"legend": {
    "begin": "0.0",
    "end": "1.0",
    "ticks_every": "0.2",
    "decimal_places": 1,
    "tick_labels": {
        # Surround every tick label in square brackets
        "default": {
            "prefix": "[",
            "suffix": "]",
        }
    }
Over-riding labels for individual ticks

If a tick’s default label (with no prefix or suffix) appears as a key in the tick_labels dictionary then the prefix, suffix or label of that tick label can be overridden.

E.g.:

"legend": {
    "begin": "0.0",
    "end": "1.0",
    "ticks_every": "0.2",
    "decimal_places": 1,
    "tick_labels": {
        # Surround every tick label in square brackets
        "default": {
            "prefix": "[",
            "suffix": "]",
        },
        # There is no "0.0" entry, so the 0.0 tick will be labelled "[0.0]"
        # The 0.2 tick will be labelled "(0.2)"
        "0.2": {
            "prefix": "(",
            "suffix": ")",
        },
        # The 0.4 tick will be labelled "[foo]"
        # (Note the default prefix and suffix are still applied)
        "0.4": {
            "label": "foo",
        },
        # The 0.6 tick will be labelled "bar" with no prefix or suffix
        "0.6": {
            "prefix": "",
            "label": "bar",
            "suffix": "",
        },
        # The 0.8 tick will be labelled ":-)"
        "0.8": {
            "prefix": ":",
            "label": "-",
            "suffix": ")",
        },
        # There is no "1.0" entry, so the 1.0 tick will be labelled "[1.0]"
    }

Values passed to MatPlotLib

Colour ramp auto-legends are created using the MatPlotLib library. The following values are passed directly to the MatPlotLib library. Please refer to the MatPlotLib documentation for further information.

Image Size

The width and height values are passed to matplotlib to specify the size of the generated image.

The image size defaults to 4 inches wide by 1.25 inches tall. The default dpi for MatPlotLib is 100, so this corresponds to 400x125 pixels unless you have over-ridden the default dpi.

E.g.:

"legend": {
    "width": 4.5,
    "height": 2.1
}
strip_location

The location of the coloured ramp strip within the legend image can be customised with the strip_location element. This should be a tuple of four floats which is passed directly to the MatPlotLib Figure.add_axes function.

The four floats are expressed as fractions of the width or heigth (i.e. are numbers between 0.0 and 1.0). The values are interpreted as follows: [left, bottom, width, height].

The default value is [ 0.05, 0.5, 0.9, 0.15 ]

E.g.:

"legend": {
    "strip_location": [ 0.1, 0.4, 0.8, 0.2 ]
}
MatPlotLib rc params

Other MatPlotLib customisations (as they would appear in a .matplotlibrc file) can be specified with the optional rcParams element, defaulting to {}, meaning the MatPlotLib defaults for all options.

For a full list of possible options refer to the MatPlotLib documentation

E.g.:

"legend": {
    "rcParams": {
             "lines.linewidth": 2,
             "font.weight": "bold",
    },
}

Multi-Date Requests

Colour Ramp Styles support customised non-animated handlers for multi-date requests by providing for an aggregator function that converts the multi-date index data into a dateless index, and apply either the style’s colour ramp (i.e. the same as the single-date case), or a separate colour ramp.

pass_raw_data

The pass_raw_data entry controls what data is passed to the aggregator function:

pass_raw_data == False (the default):

The aggregator function is passed a time-dimensioned DataArray containing the output of the index function.

pass_raw_data == True:

The aggregator function is passed a time-dimensioned Dataset, containing all of the declared needed bands.

aggregator_function

The aggregator_function entry is required for colour ramp style multi-date handlers. It is a function defined using OWS’s function configuration format.

The first argument passed to the function depends on the value of the pass_raw_data element, as described above. The function should return an xarray DataArray with no time dimension, containing the data used as an input to the multi-date handler’s colour ramp.

Multi-Date Colour Ramps

Each multi-date handler has it’s own colour ramp. It may be defined by any of the colour ramp definition methods described above.

Multi-Date Legend

A legend can be automatically generated for a multi-date handler. The legend section of a colour ramp style multi-date handler behaves the same as the single-date legend section described above.

feature_info_label

The multi-date aggregator function value will be returned in multi-date GetFeatureInfo requests for this style, using the label declared by the feature_info_label entry.

E.g.

# A simple index delta (difference) multi-date handler
"multi_date": {
    # Only 2 dates makes sense for delta.
    "allowed_count_range": [2,2],
    # Calculating the difference
    "aggregator_function": {
        "function": "datacube_ows.band_utils.multi_date_delta",
    },
    # The delta colour ramp.
    "mpl_ramp": "RdBu",
    "range": [-1.0, 1.0],
    "legend": {
        # Ticks at -1.0, -0.5, 0.0, 0.5, 1.0
        "begin": "-1.0",
        "end": "1.0",
        "ticks_every": "0.5"
    },
    # The feature info label.
    "feature_info_label": "ndvi_delta",
}