Python matplotlib example read and use NCL colormaps#

Software requirements:

  • Python 3

  • numpy

  • matplotlib

Run the read and use NCL colormaps:

python read_and_use_NCL_colormaps.py

Script read_and_use_NCL_colormaps.py:

#!/usr/bin/env python
# coding: utf-8

# Use NCL colormaps
#
# NCL provides a large collection of colormaps that former NCL users would
# like to continue using with Python.
#
# The following function `get_NCL_colormap` reads an NCL colormap either from
# local disk or from an existing NCL installation or directly from the colormaps
# URL of NCL. The RGB values are then converted to a Matplotlib Colormap
# object that can be used to create colored plots.
#
# The function `display_colormap_indices` generates a raster plot using the
# given colormap and add the index value to each color box.
#
# 2024 DKRZ, licensed under CC BY-NC-SA 4.0

import os, re
import numpy as np
import matplotlib.colors as mcolors
import matplotlib.pyplot as plt

#---------------------------------------
# Function get_NCL_colormap
#---------------------------------------
def get_NCL_colormap(cmap_name, extend='None'):
    '''Read an NCL RGB colormap file and convert it to a Matplotlib colormap
    object.

    Parameter:
        cmap_name     NCL RGB colormap name, e.g. 'ncl_default'
        extend        use NCL behavior of color handling for the colorbar 'under'
                      and 'over' colors. 'None' or 'ncl', default 'None'

    Description:
        Read the NCL colormap and convert it to a Matplotlib Colormap object.
        It checks if the colormap file is already available or use the
        appropriate URL.

        If NCL is installed the colormap will be searched in its colormaps
        folder $NCARG_ROOT/lib/ncarg/colormaps.

        Returns a Matplotlib Colormap object.
    '''
    from matplotlib.colors import ListedColormap
    import requests
    import errno

    #-- NCL colormaps URL
    NCARG_URL = 'https://www.ncl.ucar.edu/Document/Graphics/ColorTables/Files/'

    #-- read the NCL colormap RGB file
    colormap_file = cmap_name+'.rgb'
    cfile = os.path.split(colormap_file)[1]

    if os.path.isfile(colormap_file) == False:
        #-- if NCL is already installed
        if 'NCARG_ROOT' in os.environ:
            cpath = os.environ['NCARG_ROOT']+'/lib/ncarg/colormaps/'
            if os.path.isfile(cpath + cfile):
                colormap_file = cpath + cfile
                with open(colormap_file) as f:
                    lines = [re.sub('\s+',' ',l)  for l in f.read().splitlines() if not (l.startswith('#') or l.startswith('n'))]
        #-- use URL to read colormap
        elif not 'NCARG_ROOT' in os.environ:
            url_file = NCARG_URL+'/'+cmap_name+'.rgb'
            res = requests.head(url_file)
            if not res.status_code == 200:
                print(f'{cmap_name} does not exist!')
                raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), cmap_name)
            content = requests.get(url_file, stream=True).content
            lines = [re.sub('\s+',' ',l)  for l in content.decode('UTF-8').splitlines() if not (l.startswith('#') or l.startswith('n'))]
        else:
            raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), colormap_file)
    #-- use local colormap file
    else:
        with open(colormap_file) as f:
            lines = [re.sub('\s+',' ',l)  for l in f.read().splitlines() if not (l.startswith('#') or l.startswith('n'))]

    #-- skip all possible header lines
    tmp  = [l.split('#', 1)[0] for l in lines]

    tmp = [re.sub(r'\s+',' ', s) for s in tmp]
    tmp = [ x for x in tmp if ';' not in x ]
    tmp = [ x for x in tmp if x != '']

    #-- get the RGB values
    i = 0
    for l in tmp:
        new_array = np.array(l.split()).astype(float)
        if i == 0:
            color_list = new_array
        else:
            color_list = np.vstack((color_list, new_array))
        i += 1

    #-- make sure that the RGB values are within range 0 to 1
    if (color_list > 1.).any(): color_list = color_list / 255

    #-- add alpha-channel RGB -> RGBA
    alpha        = np.ones((color_list.shape[0],4))
    alpha[:,:-1] = color_list
    color_list   = alpha

    #-- convert to Colormap object
    if extend == 'ncl':
        cmap = ListedColormap(color_list[1:-1,:])
    else:
        cmap = ListedColormap(color_list)

    #-- define the under, over, and bad colors
    under = color_list[0,:]
    over  = color_list[-1,:]
    bad   = [0.5, 0.5, 0.5, 1.]
    cmap.set_extremes(under=color_list[0], bad=bad, over=color_list[-1])

    return cmap
#---------------------------------------
# Function display_colormap_indices()
#---------------------------------------
def display_colormap_indices(cmap, colormap_name, txcol='k'):
    '''Display an NCL colormap with max 256 colors.

    Parameter
        cmap            Matplotlib Colormap object
        colormap_name   name of the NCL colormap
        txcol           Font color

    Description
        Create a raster plot of 16x16 color boxes using the given colormap.
    '''
    nx, ny = 16, 16
    data = np.arange(0,256).reshape((nx,ny))

    fig, ax = plt.subplots(figsize=(8,8))
    plot = ax.pcolormesh(np.arange(-0.5,nx+0.5), np.arange(-0.5,ny+0.5), data,
                         cmap=cmap, vmin=0, vmax=cmap.N)
    ax.set_xticks([])
    ax.set_yticks([])

    n = np.arange(0,cmap.N+2)
    k = 0
    for i in range(0,data.shape[1]):
        for j in range(0,data.shape[0]):
            if k >= max(n):
                index = ''
            else:
                index = str(n[k])
            ax.text(j,i,index, ha='center', va='center', fontsize=8, color=txcol)
            k += 1

    tx = ax.set_title(f'NCL colormap', loc='left', weight='bold')
    tx = ax.set_title(f'{colormap_name} ({cmap.N} colors)', loc='center',
                      weight='bold')
    tx = ax.set_title(f'max.colors 256', loc='right', fontsize=8)

    tx = ax.text(0.905, 0.12,
                 'Original NCL colormaps https://www.ncl.ucar.edu/Document/Graphics/ColorTables/Files/',
                 rotation=-90, fontsize=8,
                 transform=plt.gcf().transFigure)

    plt.savefig(colormap_name+'_display.png', bbox_inches='tight', dpi=100)

#---------------------------------------
# Function main()
#---------------------------------------
def main():
    # Get NCL colormap
    #
    # The colormap file can be stored on your lokal machine in any directory.
    # If NCL is already installed the colormaps are stored in the directory
    # $NCARG_ROOT/lib/ncarg/colormaps.
    #
    # In this example we want to make sure that we use the original colormap from
    # NCL URL. Therefore, to be on the safe side, we unset the environment variable
    # NCARG_ROOT.
    os.environ.pop('NCARG_ROOT',None)

    # Convert NCL colormap
    #
    # Choose the colormap and convert it to the Matplotlib Colormap object.
    # Here are some examples
    colormap1 = 'NEO_div_vegetation_a'
    cmap1 = get_NCL_colormap(colormap1)

    colormap2 = 'BlueWhiteOrangeRed'
    cmap2 = get_NCL_colormap(colormap2)

    colormap3 = 'cmocean_deep'
    cmap3 = get_NCL_colormap(colormap3)

    colormap4 = 'GMT_relief'
    cmap4 = get_NCL_colormap(colormap4)

    # Display colormap with indices
    #
    # Let's see how the colormap looks in detail now, therefore we create a
    # 2-d data array of range 0 to ncolors and plot the data with Matplotlib's
    # `pcolormesh`.
    display_colormap_indices(cmap1, colormap1)

    display_colormap_indices(cmap2, colormap2)

    display_colormap_indices(cmap3, colormap3, txcol='white')

    display_colormap_indices(cmap4, colormap4, txcol='gray')

#---------------------------------------

if __name__ == '__main__':
    main()

Plot result:

image0