# Import modules
import numpy as np
from matplotlib import colors, colormaps
2 Colormaps
Colormaps are a common theme across all the notebooks. After all, colormaps are essential tools in data visualization by enabling the interpretation of numerical data through color.
A colormap is sequence of colors, where each color corresponds to a particular data value or range of values. By assigning specific colors to different data ranges, colormaps can highlight variations, patterns, and anomalies in spatial data. For example, in visualizing soil moisture, a colormap might transition from dark red (dry) to white (average moisture) to dark blue (wet) to illustrate soil moisture levels.
The structure of a colormap is often defined in terms of RGB (Red, Green, Blue), RGBA (Red, Green, Blue, Alpha), or HEX (hexadecimal) color codes. The alpha channel represents the transparency or opacity of the color.
To learn more about colormaps, I suggest checking the Matplotlib library
In this tutorial we will learn how to:
- Access predefined colormap from the Matplotlib library
- Convert colors from the RGB to HEX system
- Convert custom color palettes from the HEX to RGB system
Visualization colormaps in Google Earth Engine need to be provided using the HEX system. To leverage existing colormaps in libraries like Matplotlib, we need to convert them or to create colormaps from scratch. Fortunately, Matplotlib provides the tools for these operations.
Pre-defined colormaps
Let’s explore how to get a colormap, how to visualize them, how to reverse a colormap, and how to get a reduced version with fewer colors.
# Divergent colormap good for representing soil moisture conditions
'RdBu') # Red-Blue colormaps.get_cmap(
# Increasing colormap good for representing sand or soil organic carbon
'YlOrBr') # Yellow-Orange-Brown colormaps.get_cmap(
# Monochrome colormaps
'Greens') # Can also try Reds, Blues colormaps.get_cmap(
# Discrete colormap good for representing categorical variables,
# like soil textural classes.
'Set1') colormaps.get_cmap(
# Reverse a colormap with "_r" (run code again removing "_r")
'Spectral_r') colormaps.get_cmap(
# Get number of colors in colormap
print(colormaps.get_cmap('Spectral').N)
256
# Resample to select fewer colors
'Spectral').resampled(10) colormaps.get_cmap(
# Access single color
# First save the colormap into a variable
= colormaps.get_cmap('Spectral').resampled(10)
cmap
# Explore cmap data type (note that this is not a list)
print(type(cmap))
# Access the first color
print(cmap(0))
# Access the fifth color
print(cmap(4))
# Access the last color
print(cmap(cmap.N))
<class 'matplotlib.colors.LinearSegmentedColormap'>
(0.6196078431372549, 0.00392156862745098, 0.25882352941176473, 1.0)
(0.9978213507625272, 0.9324618736383442, 0.6357298474945533, 1.0)
(0.3686274509803922, 0.30980392156862746, 0.6352941176470588, 1.0)
Note that the outputs of the colormap are on a scale of 0 to 1 rather than on a scale 0 to 255. Representing RGB colors using the 0 to 1 scale enables a more precise specification of color values, simpler calculations for gradients and blending of colors, compatibility with libraries, and a more intuitive represention of the scale of a given color (easier to understand that 0 to 1 than 0 to 255).
RGB to HEX
As mentioned earlier, we need to pass colormaps in the HEX system to GEE. Let’s practice this.
# Get colormap from Matplotlib
= colormaps.get_cmap('magma')
rgb_cmap
# Define number of colors
= 7 # Use rgb_cmap.N for all the colors
n
# Split the cmap "n" colors. Here we create the index values
= np.linspace(0, rgb_cmap.N-1, n).astype(int)
rgb_index
# Create a list of HEX colors
= [colors.rgb2hex(rgb_cmap(k)) for k in rgb_index]
hex_cmap
print(hex_cmap)
['#000004', '#2c115f', '#721f81', '#b5367a', '#f1605d', '#feae77', '#fcfdbf']
Create a helper function
This will be handy to re-use our code in other tutorials or projects.
# Define function to retrieve colormaps
def get_hex_cmap(name,n=10):
"""
Function to get list of HEX colors from a Matplotlib colormap.
"""
= colormaps.get_cmap(name)
rgb_cmap if n > rgb_cmap.N-1:
raise ValueError(f"You select {n} colors, but {name} colormap only has {rgb_cmap.N} colors.")
else:
= np.linspace(0, rgb_cmap.N-1, n).astype(int)
rgb_index = [colors.rgb2hex(rgb_cmap(k)) for k in rgb_index]
hex_cmap return hex_cmap
# Test function
'Set1', n=7) get_hex_cmap(
['#e41a1c', '#377eb8', '#4daf4a', '#ff7f00', '#ffff33', '#a65628', '#999999']
HEX to RGB colors
This conversion sometimes becomes necessary for leveraging Python libraries, such as Rasterio, to generate local maps. For example, when consulting GEE documentation to construct a vegetation index map, you will probably encounter colormaps specified in HEX format. This assumes the use of GEE’s plotting tools. However, to employ the same colormap with alternative Python libraries that operate in the RGB color system, a conversion from HEX to RGB can be useful.
# Paletter of colors for the Enhanced Vegetation Index
= ['#CE7E45', '#DF923D', '#F1B555', '#FCD163', '#99B718', '#74A901',
hex_palette '#66A000', '#529400', '#3E8601', '#207401', '#056201', '#004C00', '#023B01',
'#012E01', '#011D01', '#011301']
# Use the built-in ListedColormap function to do the conversion
= colors.ListedColormap(hex_palette)
rgb_cmap
# Set under and bad values
'#FEFEFE')
rgb_cmap.set_bad('#0000FF') rgb_cmap.set_under(
# Display our new colormap
rgb_cmap
RGB and HEX in more depth
If you made this far, let’s delve into a greater level of detail about the RGB and HEX color systems.
The RGB color space specifies colors using three components: red, green, and blue, each varying from 0 to 255, or occasionally from 0 to 1 as demonstrated by Matplotlib’s output.
In contrast, the HEX system employs six-digit hexadecimal numbers preceded by a hash (#), with each digit pair denoting the red, green, and blue components. For example, white is expressed as (255, 255, 255)
in RGB and #ffffff
in HEX. Consequently, the RGB system offers 256 intensity levels per component, totaling over 16 million possible colors (256^3 = 16.7 million).
Let’s look at some examples. Green is depicted as (0, 255, 0)
or (0, 1, 0)
in RGB and as #00ff00
in HEX. Here, 00
signifies the minimum intensity, while ff
denotes the maximum intensity. So ff
is equivalent to 255
or 1
. Utilizing ten numeric (0 to 9) and six alphabetic (a through f) characters results in 16 alphanumeric digits. Given that \sqrt{256} = 16, two characters are required to represent 256 combinations, with 00
for 0
, 01
for 1
, and so forth until fe
for 254
, and ff
for 255
.
# Recall our description of the HEX system above.
# Show that the first color (index zero) "#FEFEFE" is (254, 254, 254)
print('Scale 0-1:', np.array(rgb_cmap(0)))
print('Scale 0-255:', (np.array(rgb_cmap(0))*255).astype(int))
# The alpha channel does not have any transparency, so it has a value of 1 or 255
Scale 0-1: [0.80784314 0.49411765 0.27058824 1. ]
Scale 0-255: [206 126 69 255]