5

Is there a way to save a custom maplotlib colourmap (matplotlib.cm) as a file (e.g Color Palette Table file (.cpt), like used in MATLAB) to be shared and then use later in other programs? (e.g. Panopoly, MATLAB...)

Example

Below a new LinearSegmentedColormap is made by modifying an existing colormap (by truncation, as shown in another question linked here).

import matplotlib
import matplotlib.pyplot as plt
import numpy as np

# Get an existing colorbar
cb = 'CMRmap'
cmap = plt.get_cmap( cb )

# Variables to modify (truncate) the colormap with
minval = 0.15 
maxval = 0.95
npoints = 100 

# Now modify (truncate) the colorbar
cmap = matplotlib.colors.LinearSegmentedColormap.from_list( 
    'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval,
    b=maxval), cmap(np.linspace(minval, maxval, npoints)))

# Now the data can be extracted as a dictionary
cdict = cmap._segmentdata

# e.g. variables ('blue', 'alpha', 'green', 'red')
print( cdict.keys() )

# Now, is it possible to save to this as a .cpt?

More detail

I am aware of ways of loading external colormaps in matplotlib (e.g. shown here and here).

From NASA GISS's Panoply documentation:

Color Palette Table (CPT) indicates a color palette format used by the Generic Mapping Tools program. The format defines a number of solid color and/or gradient bands between the colorbar extrema rather than a finite number of distinct colors.

tsherwen
  • 1,076
  • 16
  • 21
  • Do you have a resource showing the format specification of a `.cpt` file? – ImportanceOfBeingErnest Jan 18 '18 at 13:45
  • @ImportanceOfBeingErnest There is an example of a ``.cpt`` [linked here](http://soliton.vm.bytemark.co.uk/pub/cpt-city/arendal/temperature.cpt) – tsherwen Jan 18 '18 at 13:48
  • 1
    is it correct that the cpt file contains the data itself in the first column? So if you have data between 0 and 1 and a cpt file for it, it cannot be used for data ranging from 1 to 2? – ImportanceOfBeingErnest Jan 18 '18 at 13:52
  • @ImportanceOfBeingErnest [The same colormap is given at this link in multiple file formats](http://soliton.vm.bytemark.co.uk/pub/cpt-city/arendal/tn/temperature.png.index.html), as well as in ``.cpt`` – tsherwen Jan 18 '18 at 14:03
  • @ImportanceOfBeingErnest I think I will open an issue on the [maplotlib github](https://github.com/matplotlib/matplotlib) to start a discussion about whether this functionality could/should be included. What do you think? – tsherwen Jan 19 '18 at 14:56
  • Given that (1) reading this question was the first time I heard of this cpt format, (2) the format is not well suited for general purpose applications because it contains the data limits themselves, (3) I would not clutter the matplotlib code with such export capabilited and (4) writing such a format is a relatively easy task, as seen below, my *personal* opinion on this would be that it is not worth being included in the code itself. But please do judge for yourself, I'm not in the position to give any advise here. – ImportanceOfBeingErnest Jan 19 '18 at 15:02
  • Thank you for your thoughts @ImportanceOfBeingErnest. I would just reiterate one thing, the question was (in essence) about exporting colormaps to a portable format (e.g. ``RGB``, ``ACT``... etc... and ``CPT``). I only choose the [``Generic Mapping Tools``](http://gmt.soest.hawaii.edu)'s format ``.cpt`` from a open-source and portable perspective. I will think more about whether to raise an ``issue``. – tsherwen Jan 19 '18 at 20:03
  • If the question was to export a colormap to a general purpose ascii file, you can simply do `np.savetxt("filename.txt", cmap(np.linspace(0,1,255)) )`. – ImportanceOfBeingErnest Jan 20 '18 at 09:48

1 Answers1

2

The following is a function that takes a colormap, some limits (vmin and vmax) and the number of colors as input and creates a cpt file from it.

import matplotlib.pyplot as plt
import numpy as np

def export_cmap_to_cpt(cmap, vmin=0,vmax=1, N=255, filename="test.cpt",**kwargs):
    # create string for upper, lower colors
    b = np.array(kwargs.get("B", cmap(0.)))
    f = np.array(kwargs.get("F", cmap(1.)))
    na = np.array(kwargs.get("N", (0,0,0))).astype(float)
    ext = (np.c_[b[:3],f[:3],na[:3]].T*255).astype(int)
    extstr = "B {:3d} {:3d} {:3d}\nF {:3d} {:3d} {:3d}\nN {:3d} {:3d} {:3d}"
    ex = extstr.format(*list(ext.flatten()))
    #create colormap
    cols = (cmap(np.linspace(0.,1.,N))[:,:3]*255).astype(int)
    vals = np.linspace(vmin,vmax,N)
    arr = np.c_[vals[:-1],cols[:-1],vals[1:],cols[1:]]
    # save to file
    fmt = "%e %3d %3d %3d %e %3d %3d %3d"
    np.savetxt(filename, arr, fmt=fmt, 
               header="# COLOR_MODEL = RGB",
               footer = ex, comments="")

# test case: create cpt file from RdYlBu colormap
cmap = plt.get_cmap("RdYlBu",255)
# you may create your colormap differently, as in the question

export_cmap_to_cpt(cmap, vmin=0,vmax=1,N=20)

The resulting file looks like

# COLOR_MODEL = RGB
0.000000e+00 165   0  38 5.263158e-02 190  24  38
5.263158e-02 190  24  38 1.052632e-01 215  49  39
1.052632e-01 215  49  39 1.578947e-01 231  83  55
1.578947e-01 231  83  55 2.105263e-01 244 114  69
2.105263e-01 244 114  69 2.631579e-01 249 150  86
2.631579e-01 249 150  86 3.157895e-01 253 181 104
3.157895e-01 253 181 104 3.684211e-01 253 207 128
3.684211e-01 253 207 128 4.210526e-01 254 230 153
4.210526e-01 254 230 153 4.736842e-01 254 246 178
4.736842e-01 254 246 178 5.263158e-01 246 251 206
5.263158e-01 246 251 206 5.789474e-01 230 245 235
5.789474e-01 230 245 235 6.315789e-01 206 234 242
6.315789e-01 206 234 242 6.842105e-01 178 220 235
6.842105e-01 178 220 235 7.368421e-01 151 201 224
7.368421e-01 151 201 224 7.894737e-01 120 176 211
7.894737e-01 120 176 211 8.421053e-01  96 149 196
8.421053e-01  96 149 196 8.947368e-01  70 118 180
8.947368e-01  70 118 180 9.473684e-01  59  86 164
9.473684e-01  59  86 164 1.000000e+00  49  54 149
B 165   0  38
F  49  54 149
N   0   0   0

and would be in the required format.

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712
  • 1
    Thanks @ImportanceOfBeingErnest! I did a couple of tests with ``Panopoly`` and it worked perfectly - [example plot attached](http://atmosviz1.york.ac.uk/~ts551/REF_IMAGES/stackoverflow/TEST_CB_ON_2D_SPATIAL_PLOT.png) – tsherwen Jan 19 '18 at 14:50