def bezier(icolors, ncol=255, correctlightness=True, reverse=False, cmap=None, lch=False): """ interpolates between a set of colors using a bezier spline """ # Bezier interpolation functions int2 = lambda c0, c1, t: (1. - t) * c0 + t * c1 int3 = lambda c0, c1, c2, t: (1. - t) * (1. - t) * c0 + 2. * ( 1. - t) * t * c1 + t * t * c2 int4 = lambda c0, c1, c2, c3, t: (1. - t) * (1. - t) * ( 1. - t) * c0 + 3. * (1. - t) * (1. - t) * t * c1 + 3. * ( 1. - t) * t * t * c2 + t * t * t * c3 # Check assert ncol > 1, 'Number of interpolated colours must be > 1' # Convert colours to L*a*b if lch: colors = [rgb2lch(*col2rgb(i)) for i in icolors] # in lab else: colors = [rgb2lab(*col2rgb(i)) for i in icolors] # in lab # Check - 2 icol = len(colors) if (icol > 5): raise ValueError('number of input colors > 5 not supported.') # Check sequential or diverging colours, i.e. check for increasing/decreasing lightness if (icol > 1): lights = np.array([i[0] for i in colors]) dlight = np.diff(lights) if np.all(dlight < 0.) or np.all(dlight >= 0.): idiverge = False # if icol > 4: # raise ValueError('only diverging colours supported in case of 5 input colours.') else: idiverge = True if icol == 4: print('ll', lights) raise ValueError( 'colours must have increasing or decreasing lightness in case of even number of colors: ' + ',' + ','.join([str(i) for i in lights])) # Interpolate colours if icol == 1: lab = [colors[0] for i in range(ncol)] elif icol == 2: lab0 = colors[0] lab1 = colors[1] # linear interpolation lab = list() for it in range(ncol): t = float(it) / float(ncol - 1) if correctlightness: t = correct_lightness(int2, lab0[0], lab1[0], t) lab.append(tuple([int2(lab0[i], lab1[i], t) for i in range(3)])) elif icol == 3: if idiverge: assert ( ncol % 2 ) == 1, 'number of colors has to be odd for bezier interpolation with 3 diverging colors.' ncol2 = ncol // 2 + 1 # two separate interpolations col1 = bezier(icolors[:2], ncol2) col2 = bezier(icolors[1:], ncol2) col1.extend(col2[1:]) if lch: lab = [rgb2lch(*rgb012rgb(*i)) for i in col1] else: lab = [rgb2lab(*rgb012rgb(*i)) for i in col1] else: lab0 = colors[0] lab1 = colors[1] lab2 = colors[2] # quadratic bezier interpolation lab = list() for it in range(ncol): t = float(it) / float(ncol - 1) if correctlightness: t = correct_lightness(int3, lab0[0], lab1[0], lab2[0], t) lab.append( tuple([ int3(lab0[i], lab1[i], lab2[i], t) for i in range(3) ])) elif icol == 4: lab0 = colors[0] lab1 = colors[1] lab2 = colors[2] lab3 = colors[3] # cubic bezier interpolation lab = list() for it in range(ncol): t = float(it) / float(ncol - 1) if correctlightness: t = correct_lightness(int4, lab0[0], lab1[0], lab2[0], lab3[0], t) lab.append( tuple([ int4(lab0[i], lab1[i], lab2[i], lab3[i], t) for i in range(3) ])) elif icol == 5: assert ( ncol % 2 ) == 1, 'number of colors has to be odd for bezier interpolation with 5 colors.' ncol2 = ncol // 2 + 1 col1 = bezier(icolors[:3], ncol2) col2 = bezier(icolors[2:], ncol2) col1.extend(col2[1:]) if lch: lab = [rgb2lch(*rgb012rgb(*i)) for i in col1] else: lab = [rgb2lab(*rgb012rgb(*i)) for i in col1] else: raise ValueError('number of input colors > 5 not supported.') # Reverse colours if reverse: lab = lab[::-1] # rgb [0-1] if lch: cout = [rgb2rgb01(*lch2rgb(*i)) for i in lab] else: cout = [rgb2rgb01(*lab2rgb(*i)) for i in lab] # Register colour map with matplotlib if cmap is not None: from matplotlib.colors import ListedColormap from matplotlib.cm import register_cmap iscmap = ListedColormap(cout, name=cmap, N=ncol) register_cmap(name=cmap, cmap=iscmap) return cout
def sron_colors(cmap='palette1', ncol=9, cname=None, rgb=False, rgb256=False, reverse=False): """ Distinct colour palettes of Paul Tol at SRON - Netherlands Institute for Space Research Definition ---------- def sron_colors(cmap='palette1', ncol=9, cname=None, rgb=False, rgb256=False, reverse=False): Input ----- None Optional Input -------------- cmap Colour palette name palette1: 1-12 colours; orignal palette is ncol=9 palette2: more regular hue and saturation (21 colours) palette2-light: 7 light colours of palette2 palette2-medium: 7 medium colours of palette2 palette2-dark: 7 darker colours of palette2 gray / grey: 4 colours optimized for printing in grey scale ylorbr 3-9 colours from sron_maps('ylorbr') buylrd 3-11 colours from sron_maps('buylrd') rainbow 4-12 colours from sron_maps('rainbow') banded-rainbow 14, 15, 18, 21 banded rainbow schemes ncol number of desired colors if palette1 (1-12) cname if given, name of registered colormap rgb if True, return RGB value tuple between 0 and 1 rgb256 if True, return RGB value tuple between 0 and 255 reverse if True, reverse colormap Output ------ matplotlip listed colormap of ncol colours Restrictions ------------ None Examples -------- >>> print(sron_colors('palette1', 3, rgb256=True)) [(68, 119, 170), (221, 204, 119), (204, 102, 119)] >>> print(sron_colors('palette2-light', rgb256=True)[0]) (119, 170, 221) >>> print(sron_colors('ylorbr', 4, rgb256=True, reverse=True)[0]) (204, 76, 2) License ------- This file is part of the JAMS Python package, distributed under the MIT License. The JAMS Python package originates from the former UFZ Python library, Department of Computational Hydrosystems, Helmholtz Centre for Environmental Research - UFZ, Leipzig, Germany. Copyright (c) 2016 Matthias Cuntz - mc (at) macu (dot) de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. History ------- Written, MC, May 2016 """ from jams.color import hex2rgb01 if cmap.lower() == 'palette1': assert (ncol>0) and (ncol<13), 'palette1 has 1-12 colours.' cols = [] for i in palette1[ncol-1]: cols.append(tuple(hex2rgb01(i))) elif cmap.lower() == 'palette2': cols = [] for i in range(7): cols.append(tuple(hex2rgb01(palette2_light[i]))) cols.append(tuple(hex2rgb01(palette2_medium[i]))) cols.append(tuple(hex2rgb01(palette2_dark[i]))) elif cmap.lower() == 'palette2-light': cols = [] for i in palette2_light: cols.append(tuple(hex2rgb01(i))) elif cmap.lower() == 'palette2-medium': cols = [] for i in palette2_medium: cols.append(tuple(hex2rgb01(i))) elif cmap.lower() == 'palette2-dark': cols = [] for i in palette2_dark: cols.append(tuple(hex2rgb01(i))) elif cmap.lower() == 'grey' or cmap.lower() == 'gray': cols = [] for i in greysafe: cols.append(tuple(hex2rgb01(i))) elif cmap.lower() == 'ylorbr': assert (ncol>2) and (ncol<10), 'ylorbr has 3-9 colours.' cols = [] for i in palette_ylorbr[ncol-3]: cols.append(tuple(hex2rgb01(i))) elif cmap.lower() == 'buylrd': assert (ncol>2) and (ncol<12), 'buylrd has 3-11 colours.' cols = [] for i in palette_buylrd[ncol-3]: cols.append(tuple(hex2rgb01(i))) elif cmap.lower() == 'rainbow': assert (ncol>3) and (ncol<13), 'rainbow has 4-12 colours.' cols = [] for i in palette_rainbow[ncol-4]: cols.append(tuple(hex2rgb01(i))) elif cmap.lower() == 'banded-rainbow': if ncol==14: psel = palette_rainbow_band[0] elif ncol==15: psel = palette_rainbow_band[1] elif ncol==18: psel = palette_rainbow_band[2] elif ncol==21: psel = palette_rainbow_band[3] else: raise ValueError('banded-rainbow palette has 14, 15, 18, or 21 colours.') cols = [] for i in psel: cols.append(tuple(hex2rgb01(i))) else: raise ValueError('Colour palette not known: '+cmap) if reverse: cols = cols[::-1] if (not rgb) and (not rgb256): from matplotlib.colors import ListedColormap ccmap = ListedColormap(cols) if cname: from matplotlib.cm import register_cmap, get_cmap register_cmap(cname, ccmap) return get_cmap(cname) else: return ccmap else: if rgb256: from jams.color import rgb012rgb return [ rgb012rgb(*i) for i in cols ] else: return cols
def sron_maps(cmap, ncol=256, offset=0, upper=1, cname=None, rgb=False, rgb256=False, reverse=False, grey=False, gray=False): """ Colour maps of Paul Tol at SRON - Netherlands Institute for Space Research Definition ---------- def sron_maps(cmap, ncol=256, offset=0, upper=1, cname=None, rgb=False, rgb256=False, reverse=False, grey=False, gray=False): Input ----- cmap Colour map name buylrd: blue-yellow-red diverging rainbow: rainbow ylorbr: yellow-orange-red sequential Optional Input -------------- ncol number of desired colors offset bottom fraction to exclude (0-1) upper upper most fraction included (0-1) cname if given, name of registered colormap rgb if True, return RGB value tuple between 0 and 1 rgb256 if True, return RGB value tuple between 0 and 255 reverse if True, reverse colormap grey if True, return grey equivalent gray same as grey Output ------ matplotlip listed colormap of ncol colours Restrictions ------------ None Examples -------- cmap = sron_maps('rainbow', 256) cc = sron_maps('buylrd', 11, rgb=True) License ------- This file is part of the JAMS Python package, distributed under the MIT License. The JAMS Python package originates from the former UFZ Python library, Department of Computational Hydrosystems, Helmholtz Centre for Environmental Research - UFZ, Leipzig, Germany. Copyright (c) 2016 Matthias Cuntz - mc (at) macu (dot) de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. History ------- Written, MC, May 2016 """ if cmap == 'ylorbr': from scipy.special import erf cols = [] for i in range(ncol): x = offset + float(i)/float(ncol-1) * (upper-offset) if cmap == 'buylrd': cols.append(tuple(buylrd(x))) elif cmap == 'rainbow': cols.append(tuple(rainbow(x))) elif cmap == 'ylorbr': cols.append(tuple(ylorbr(x))) else: raise ValueError('Colour map not known: '+cmap) if reverse: cols = cols[::-1] if grey or gray: for i, cc in enumerate(cols): isgray = 0.2125*cc[0] + 0.7154*cc[1] + 0.072*cc[2] cols[i] = (isgray,isgray,isgray) if (not rgb) and (not rgb256): from matplotlib.colors import ListedColormap ccmap = ListedColormap(cols) if cname: from matplotlib.cm import register_cmap, get_cmap register_cmap(cname, ccmap) return get_cmap(cname) else: return ccmap else: if rgb256: from jams.color import rgb012rgb return [ rgb012rgb(*i) for i in cols ] else: return cols
def get_brewer(cname=None, names=False, rgb=False, rgb256=False, reverse=False, grey=False, gray=False): """ Get colour map either as registered handle or as RGB values (0-255 or 0-1). Examples -------- >>> import numpy as np >>> from jams.autostring import astr >>> cc = get_brewer('blues4',rgb=True) >>> print(astr(np.array(cc[0]), 4)) ['0.9373' '0.9529' '1.0000'] >>> cc = get_brewer('Blues4',rgb256=True) >>> print(cc[0]) (239, 243, 255) >>> cc = get_brewer('bLuEs4',rgb256=True,reverse=True) >>> print(cc[-1]) (239, 243, 255) >>> print(cc[0]) (33, 113, 181) >>> cc = get_brewer('blues4',rgb256=True,grey=True) >>> print(astr(np.array(cc[0]), 4)) ['242.9897' '242.9897' '242.9897'] """ from jams.color import rgb2rgb01, rgb012rgb if names: if names.lower() == 'sequential': return list(brewer_sequential.keys()) elif names.lower() == 'diverging': return list(brewer_diverging.keys()) elif names.lower() == 'qualitative': return list(brewer_qualitative.keys()) elif names.lower() == 'osu': return list(oregon.keys()) elif names.lower() == 'ncl_large': return list(ncl_large.keys()) elif names.lower() == 'ncl_small': return list(ncl_small.keys()) elif names.lower() == 'ncl_meteo_swiss': return list(ncl_meteo_swiss.keys()) elif names.lower() == 'mma' or names.lower() == 'mathematica': return list(mathematica.keys()) else: cmaps = list(all_maps.keys()) return cmaps else: cname = capitalise(cname) cpool = all_maps[cname][:] if (not rgb) and (not rgb256): # register colour map with matplotlib from matplotlib.cm import get_cmap register_brewer(cname, reverse=reverse, grey=grey, gray=gray) return get_cmap(cname) elif rgb: # tuple 0-1 if cname in all255_maps: cpool = [rgb2rgb01(*i) for i in cpool] elif rgb256: # tuple 0-255 if cname not in all255_maps: cpool = [rgb012rgb(*i) for i in cpool] if reverse: cpool = cpool[::-1] if grey or gray: for j in range(len(cpool)): isgray = 0.2125 * cpool[j][0] + 0.7154 * cpool[j][ 1] + 0.072 * cpool[j][2] cpool[j] = (isgray, isgray, isgray) return cpool