# -*- python -*-
# -*- coding: utf-8 -*-
#
# This file is part of the colormap software
#
# Copyright (c) 2011-20134
#
# File author(s): Thomas Cokelaer <cokelaer@gmail.com>
#
# Distributed under the GPLv3 License.
# See accompanying file LICENSE.txt or copy at
# http://www.gnu.org/licenses/gpl-3.0.html
#
# Website: https://github.com/cokelaer/colormap
# Documentation: http://packages.python.org/colormap
#
##############################################################################
"""Utilities provided in this module can be found either in the
standard Python module called :mod:`colorsys` or in matplotlib.colors
(e.g rgb2hex) or are original to this module (e.g., rgb2huv)
"""
# matplotlib dependence is only inside Colormap class
import colorsys
from easydev.tools import check_param_in_list, swapdict, check_range
from colormap import xfree86
from colormap.xfree86 import XFree86_colors
__all__ = ["HEX", "Color", "hex2web", "web2hex", "hex2rgb", "hex2dec",
"rgb2hex", "rgb2hsv", "hsv2rgb", "rgb2hls", "hls2rgb","yuv2rgb", "rgb2yuv",
"to_intensity", "yuv2rgb_int", "rgb2yuv_int", "Colormap"
]
[docs]def hex2web(hexa):
"""Convert hexadecimal string (6 digits) into *web* version (3 digits)
.. doctest::
>>> from colormap.colors import hex2web
>>> hex2web("#FFAA11")
'#FA1'
.. seealso:: :func:`web2hex`, :func:`hex2rgb`
:func:`rgb2hex`, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`,
:func:`hls2rgb`
"""
hexa = HEX().get_standard_hex_color(hexa)
return "#" + hexa[1::2]
[docs]def web2hex(web):
"""Convert *web* hexadecimal string (3 digits) into 6 digits version
.. doctest::
>>> from colormap.colors import web2hex
>>> web2hex("#FA1")
'#FFAA11'
.. seealso:: :func:`hex2web`, :func:`hex2rgb`
:func:`rgb2hex`, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`,
:func:`hls2rgb`
"""
return HEX().get_standard_hex_color(web)
[docs]def hex2rgb(hexcolor, normalise=False):
"""This function converts a hex color triplet into RGB
Valid hex code are:
* #FFF
* #0000FF
* 0x0000FF
* 0xFA1
.. doctest::
>>> from colormap.colors import hex2rgb
>>> hex2rgb("#FFF", normalise=False)
(255, 255, 255)
>>> hex2rgb("#FFFFFF", normalise=True)
(1.0, 1.0, 1.0)
.. seealso:: :func:`hex2web`, :func:`web2hex`,
:func:`rgb2hex`, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`,
:func:`hls2rgb`
"""
hexcolor = HEX().get_standard_hex_color(hexcolor)[1:]
r, g, b = int(hexcolor[0:2], 16), int(hexcolor[2:4], 16), int(hexcolor[4:6], 16)
if normalise:
r, g, b = _normalise(r, g, b)
return r, g, b
[docs]def rgb2hex(r, g, b, normalised=False):
"""Convert RGB to hexadecimal color
:param: can be a tuple/list/set of 3 values (R,G,B)
:return: a hex vesion ofthe RGB 3-tuple
.. doctest::
>>> from colormap.colors import rgb2hex
>>> rgb2hex(0,0,255, normalised=False)
'#0000FF'
>>> rgb2hex(0,0,1, normalised=True)
'#0000FF'
.. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb`
, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`,
:func:`hls2rgb`
"""
if normalised:
r, g, b = _denormalise(r, g, b, mode="rgb")
check_range(r, 0, 255)
check_range(g, 0, 255)
check_range(b, 0, 255)
return '#%02X%02X%02X' % (r, g, b)
[docs]def rgb2hls(r, g, b, normalised=True):
"""Convert an RGB value to an HLS value.
:param bool normalised: if *normalised* is True, the input RGB triplet
should be in the range 0-1 (0-255 otherwise)
:return: the HLS triplet. If *normalised* parameter is True, the output
triplet is in the range 0-1; otherwise, H in the range 0-360 and LS
in the range 0-100.
.. doctest::
>>> from colormap.colors import rgb2hls
>>> rgb2hls(255,255,255, normalised=False)
(0.0, 1.0, 0.0)
.. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb`
:func:`rgb2hex`, :func:`hsv2rgb`,
:func:`hls2rgb`
"""
# rgb_to_hsv expects normalised values !
if normalised:
upper = 1
else:
upper = 255
check_range(r, 0, upper)
check_range(g, 0, upper)
check_range(b, 0, upper)
if normalised==False:
r, g, b = _normalise(r, g, b)
h, l, s = colorsys.rgb_to_hls(r, g, b)
return h, l, s
[docs]def rgb2hsv(r, g, b, normalised=True):
"""Convert an RGB value to an HSV value.
:param bool normalised: if *normalised* is True, the input RGB triplet
should be in the range 0-1 (0-255 otherwise)
:return: the HSV triplet. If *normalised* parameter is True, the output
triplet is in the range 0-1; otherwise, H in the range 0-360 and LS
in the range 0-100.
.. doctest::
>>> from colormap.colors import rgb2hsv
>>> rgb2hsv(0.5,0,1)
(0.75, 1, 1)
.. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb`
:func:`rgb2hex`, :func:`hsv2rgb`, :func:`rgb2hls`,
:func:`hls2rgb`
"""
# rgb_to_hsv expects normalised values !
if normalised:
upper = 1
else:
upper = 255
check_range(r, 0, upper)
check_range(g, 0, upper)
check_range(b, 0, upper)
if normalised==False:
r, g, b = _normalise(r, g, b)
h, s, v = colorsys.rgb_to_hsv(r, g, b)
return h,s,v
[docs]def hsv2rgb(h, s, v, normalised=True):
"""Convert a hue-saturation-value (HSV) value to a red-green-blue (RGB).
:param bool normalised: If *normalised* is True, the input HSV triplet
should be in the range 0-1; otherwise, H in the range 0-360 and LS
in the range 0-100.
:return: the RGB triplet. The output
triplet is in the range 0-1 whether the input is normalised or not.
.. doctest::
>>> from colormap.colors import hsv2rgb
>>> hsv2rgb(0.5,1,1, normalised=True) # doctest: +SKIP
(0, 1, 1)
.. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb`
:func:`rgb2hex`, :func:`rgb2hsv`, :func:`rgb2hls`,
:func:`hls2rgb`
.. seealso:: :func:`rgb2hex`
"""
if normalised:
upper = 1
else:
upper = 100
if normalised:
uppera = 1
else:
uppera = 360
check_range(h, 0, uppera)
check_range(s, 0, upper)
check_range(v, 0, upper)
if normalised == False:
h, s, v = _normalise(h, s, v, mode="hsv")
return colorsys.hsv_to_rgb(h, s, v)
[docs]def hls2rgb(h, l, s, normalised=True):
"""Convert an HLS value to a RGB value.
:param bool normalised: If *normalised* is True, the input HLS triplet
should be in the range 0-1; otherwise, H in the range 0-360 and LS
in the range 0-100.
:return: the RGB triplet. The output
triplet is in the range 0-1 whether the input is normalised or not.
.. doctest::
>>> from colormap.colors import hls2rgb
>>> hls2rgb(360, 50, 60, normalised=False) # doctest: +SKIP
(0.8, 0.2, 0.2)
.. seealso:: :func:`hex2web`, :func:`web2hex`, :func:`hex2rgb`
:func:`rgb2hex`, :func:`rgb2hsv`, :func:`hsv2rgb`, :func:`rgb2hls`,
"""
if normalised:
upper = 1
else:
upper = 100
if normalised:
uppera = 1
else:
uppera = 360
check_range(h, 0, uppera)
check_range(s, 0, upper)
check_range(l, 0, upper)
if normalised == False:
h, l, s = _normalise(h, l, s, mode="hls")
return colorsys.hls_to_rgb(h, l, s)
[docs]def hex2dec(data):
"""convert integer (data) into hexadecimal."""
return int(data, 16)/255.
[docs]def rgb2yuv(r, g, b):
"""Convert RGB triplet into YUV
:return: YUV triplet with values between 0 and 1
`YUV wikipedia <http://en.wikipedia.org/wiki/YUV>`_
.. warning:: expected input must be between 0 and 1
.. note:: the constants referenc used is Rec. 601
"""
check_range(r, 0, 1)
check_range(g, 0, 1)
check_range(b, 0, 1)
#y = int(0.299 * r + 0.587 * g + 0.114 * b)
#u = int(-0.14713 * r + -0.28886 * g + 0.436 * b)
#v = int(0.615 * r + -0.51499 * g + -0.10001 * b)
y = 0.299 * r + 0.587 * g + 0.114 * b
u = -32591.0/221500.0 * r + -63983.0/221500.0 * g + 0.436 * b
v = 0.615 * r + -72201./140200 * g + -7011/70100. * b
return (y, u, v)
[docs]def yuv2rgb(y, u, v):
"""Convert YUV triplet into RGB
`YUV <http://en.wikipedia.org/wiki/YUV>`_
.. warning:: expected input must be between 0 and 255 (not normalised)
"""
check_range(y, 0,1)
check_range(u, 0, 1)
check_range(v, 0, 1)
A, B, C, D = 701.0/615.0, 25251.0/63983.0, 209599.0/361005.0, 443.0/218.0
r = y + A * v
g = y - B * u - C * v
b = y + D * u
return (r, g, b)
[docs]def rgb2yuv_int(r, g, b):
"""Convert RGB triplet into YUV
`YUV wikipedia <http://en.wikipedia.org/wiki/YUV>`_
.. warning:: expected input must be between 0 and 255 (not normalised)
"""
check_range(r, 0, 255)
check_range(g, 0, 255)
check_range(b, 0, 255)
y = int(0.299 * r + 0.587 * g + 0.114 * b)
u = int(-32591.0/221500.0 * r + -63983.0/221500.0 * g + 0.436 * b)
v = int(0.615 * r + -72201./140200 * g + -7011/70100. * b)
return (y, u, v)
[docs]def yuv2rgb_int(y, u, v):
"""Convert YUV triplet into RGB
`YUV <http://en.wikipedia.org/wiki/YUV>`_
.. warning:: expected input must be between 0 and 255 (not normalised)
"""
check_range(y, 0, 255)
check_range(u, 0, 255)
check_range(v, 0, 255)
r = int(y + 1.13983 * v)
g = int(y - 0.39465 * u - 0.58060 * v)
b = int(y + 2.03211 * u)
return (r, g, b)
def _denormalise(r, g, b, mode="rgb"):
check_param_in_list(mode, ["rgb", "hls", "hsv"])
if mode == "rgb":
return r*255., g*255., b*255.
elif mode in ["hls", "hsv"]:
return r*360., g*100., b*100.
def _normalise(r, g, b, mode="rgb"):
check_param_in_list(mode, ["rgb", "hls", "hsv"])
if mode == "rgb":
return r/255., g/255., b/255.
elif mode in ["hls", "hsv"]:
return r/360., g/100., b/100.
[docs]def to_intensity(n):
"""Return intensity
:param n: value between 0 and 1
:return: value between 0 and 255; round(n*127.5+127.5)
"""
check_range(n, 0, 1)
return int(round(n * 127.5 + 127.5))
[docs]class HEX(object):
"""Class to check the validity of an hexadecimal string and get standard string
By standard, we mean #FFFFFF (6 digits)
::
>>> h = HEX()
>>> h.is_valid_hex_color("#FFFF00")
True
"""
def __init__(self):
pass
[docs] def is_valid_hex_color(self, value, verbose=True):
"""Return True is the string can be interpreted as hexadecimal color
Valid formats are
* #FFF
* #0000FF
* 0x0000FF
* 0xFA1
"""
try:
self.get_standard_hex_color(value)
return True
except Exception as err:
if verbose:
print(err)
return False
[docs] def get_standard_hex_color(self, value):
"""Return standard hexadecimal color
By standard, we mean a string that starts with # sign followed by 6
character, e.g. #AABBFF
"""
if isinstance(value, str)==False:
raise TypeError("value must be a string")
if len(value) <= 3:
raise ValueError("input string must be of type 0xFFF, 0xFFFFFF or #FFF or #FFFFFF")
if value.startswith("0x") or value.startswith("0X"):
value = value[2:]
elif value.startswith("#"):
value = value[1:]
else:
raise ValueError("hexa string must start with a '#' sign or '0x' string")
value = value.upper()
# Now, we have either FFFFFF or FFF
# now check the length
for x in value:
if x not in "0123456789ABCDEF":
raise ValueError("Found invalid hexa character {0}".format(x))
if len(value) == 6 or len(value) == 8:
value = "#" + value[0:6]
elif len(value) == 3:
value = "#" + value[0]*2 + value[1]*2 + value[2]*2
else:
raise ValueError("hexa string should be 3, 6 or 8 digits. if 8 digits, last 2 are ignored")
return value
[docs]class Color(HEX):
"""Class to ease manipulation and conversion between color codes
You can create an instance in many differen ways. You can either use a
human-readable name as long as it is part of the
`XFree86 list <http://en.wikipedia.org/wiki/X11_color_names>`_
You can also provide a hexadecimal string (either 3 or 6 digits). You can
use triplets of values corresponding to the RGB, HSV or HLS conventions.
Here are some examples:
.. doctest::
from colormap import Color
Color("red") # human XFree86 compatible representation
Color("#f00") # standard 3 hex digits
Color("#ff0000") # standard 6 hex digits
Color(hsv=(0,1,0.5))
Color(hls=(0, 1, 0.5)) # HLS triplet
Color(rgb=(1, 0, 0)) # RGB triplet
Color(Color("red")) # using an instance of :class:`Color`
Note that the RGB, HLS and HSV triplets use normalised values. If you need
to normalise the triplet, you can use :mod:`colormap.colors._normalise` that
provides a function to normalise RGB, HLS and HSV triplets::
colors._normalise(*(255, 255, 0), mode="rgb")
colors._normalise(*(360, 50, 100), mode="hls")
If you provide a string, it has to be a valid string from XFree86.
In addition to the official names, the lower case names are valid. Besides,
there are names with spaces. The equivalent names without space are also
valid. Therefore the name "Spring Green", which is an official name can be
provided as "Spring Green", "spring green", "springgreen" or "SpringGreen".
"""
# Get official color names
colors = XFree86_colors.copy()
# add color names without spaces
aliases = dict([(x.replace(" ", ""),x) for x in colors.keys() if " " in x])
# add color names without spaces in lower cases
aliases.update([(x.replace(" ", "").lower(),x) for x in colors.keys() if " " in x])
# add color names in lower case
aliases.update(dict([(x.lower(),x) for x in colors.keys()]))
aliases.update(dict([(x,x) for x in colors.keys()]))
# keep track of all possible names
color_names = sorted(list(set(list(colors.keys()) +list( aliases.keys()))))
def __init__(self, name=None, rgb=None, hls=None, hsv=None):
super(Color, self).__init__()
self._name = None
self._mode = None
self._rgb = None
# Does the user provided the name argument (first one) as a string ?
if isinstance(name, str):
# if so, it can be a valid human name (e.g., red) or an hex
# assuming that valid hexadecimal starts with # or 0x,
# if we can interpret the string as an hexadecimal, we are done
if self.is_valid_hex_color(name, verbose=False):
self.hex = name
else:
# if not, then, the user probably provided a valid color name
# the property will check the validity.
self.name = name[:]
#all other input parameters are ignored
elif name == None:
if rgb:
self.rgb = rgb
elif hls:
self.hls = hls
elif hsv:
self.hsv = hsv
else:
raise ValueError("You must set one of the parameter")
elif isinstance(name, Color):
self.rgb = name.rgb
else:
raise ValueError("name parameter must be a string")
def _get_name(self):
return self._name
def _set_name(self, name):
check_param_in_list(name, self.color_names)
name = self.aliases[name]
self._name = name
# set hex and rgb at the same time based on the name
self.hex = self.colors[name]
name = property(_get_name, _set_name)
color = property(_get_name, _set_name)
def _get_hex(self):
return self._hex
def _set_hex(self, value):
# hex is an approximation made of 255 bits so do not define rgb here
if self.is_valid_hex_color(value):
value = self.get_standard_hex_color(value)
self._hex = value
if self._hex in self.colors.values():
self._name = swapdict(self.colors, check_ambiguity=False)[self._hex]
else:
self._name = "undefined"
self._rgb = hex2rgb(self._hex, normalise=True)
else:
# just to warn the user
self.get_standard_hex_color(value)
hex = property(_get_hex, _set_hex, doc="getter/setter the hexadecimal value.")
def _get_rgb(self):
return self._rgb
def _set_rgb(self, value):
# set name, hex and rgb
self.hex = rgb2hex(*value , normalised=True)
# must reset rgb with its real value (set_hex may round the rgb)
# in _set_hex
self._rgb = value
rgb = property(_get_rgb, _set_rgb,
doc="getter/setter the RGB values (3-length tuple)")
def _get_hsv(self):
hsv = rgb2hsv(*self.rgb)
return hsv
def _set_hsv(self, value):
# TODO: value must be normalised
self.rgb = hsv2rgb(*value)
hsv = property(_get_hsv, _set_hsv,
doc="getter/setter the HSV values (3-length tuple)")
def _get_hls(self):
hls = rgb2hls(*self.rgb)
return hls
def _set_hls(self, value):
#hls = _normalise(*value, mode="hls")
#else:
hls = value
self.rgb = hls2rgb(*hls)
hls = property(_get_hls, _set_hls,
doc="getter/setter the HLS values (3-length tuple)")
def _get_lightness(self):
return self.hls[1]
def _set_lightness(self, lightness):
h, l, s = self.hls
self.hls = (h, lightness, s)
lightness = property(_get_lightness, _set_lightness,
doc="getter/setter the lightness in the HLS triplet")
def _get_saturation_hls(self):
return self.hls[2]
def _set_saturation_hls(self, saturation):
h, l, s = self.hls
self.hls = (h, l, saturation)
saturation_hls = property(_get_saturation_hls, _set_saturation_hls,
doc="getter/setter the saturation in the HLS triplet")
def _get_hue(self):
return self.hls[0]
def _set_hue(self, hue):
h, l, s = self.hls
self.hls = (hue, l, s)
hue = property(_get_hue, _set_hue,
doc="getter/setter the saturation in the HLS triplet")
def _get_red(self):
return self.rgb[0]
def _set_red(self, red):
r, g, b = self.rgb
self.rgb = (red,g,b)
red = property(_get_red, _set_red,
doc="getter/setter for the red color in RGB triplet")
def _get_green(self):
return self.rgb[1]
def _set_green(self, green):
r, g, b = self.rgb
self.rgb = (r, green, b)
green = property(_get_green, _set_green,
doc="getter/setter for the green color in RGB triplet")
def _get_blue(self):
return self.rgb[2]
def _set_blue(self, blue):
r, g, b = self.rgb
self.rgb = (r, g, blue)
blue = property(_get_blue, _set_blue,
doc="getter/setter for the blue color in RGB triplet")
def _get_value(self):
return self.hls[0]
def _set_value(self, value):
h, s, v = self.hsv
self.hsv = (h, s, value)
value = property(_get_value, _set_value,
doc="getter/setter the value in the HSV triplet")
def _get_yiq(self):
return colorsys.rgb_to_yiq(*self.rgb)
yiq = property(_get_yiq, doc="Getter for the YIQ triplet")
def __str__(self):
txt = 'Color {0}\n'.format(self.name)
txt+= ' hexa code: {0}\n'.format(self.hex)
txt+= ' RGB code: {0}\n'.format(self.rgb)
txt+= ' RGB code (un-normalised): {0}\n\n'.format([x*255 for x in self.rgb])
txt+= ' HSV code: {0}\n'.format(self.hsv)
txt+= ' HSV code: (un-normalised) {0} {1} {2}\n\n'.format(self.hsv[0]*360, self.hsv[1]*100, self.hsv[2]*100)
txt+= ' HLS code: {0}\n'.format(self.hls)
txt+= ' HLS code: (un-normalised) {0} {1} {2}\n\n'.format(self.hls[0]*360, self.hls[1]*100, self.hls[2]*100)
return txt
[docs]class Colormap(object):
"""Class to create matplotlib colormap
This example show how to get the pre-defined colormap called *heat*
.. plot::
:include-source:
from pylab import *
from colormap.colors import Colormap
c = Colormap()
cmap = c.get_cmap_heat()
c.test_colormap(cmap)
You may be more interested in building your own colormap::
# design your own colormap
d = {'blue': [0,0,0,1,1,1,0],
'green':[0,1,1,1,0,0,0],
'red': [1,1,0,0,0,1,1]}
cmap = c.cmap(d, reverse=False)
# see the results
c.test_colormap(cmap)
If you want a simple linear colormap, you can use the example above,
or use the :meth:`cmap_linear`. For instance for a diverging colormap
from red to green (with with color in between)::
cmap = c.cmap_linear("red", "white", "green")
c.test_colormap(cmap)
Even simpler, you can use a bicolor colormap :meth:`cmap_bicolor`. For instance
for a red to green colormap::
cmap = c.cmap_bicolor("red", "green")
c.test_colormap(cmap)
From matplotlib documentation, colormaps falls into 4 categories:
#. Sequential schemes for unipolar data that progresses from low to high
#. Diverging schemes for bipolar data that emphasizes positive or
negative deviations from acentral value
#. Cyclic schemes meant for plotting values that wrap around at the
endpoints, such as phase angle, wind direction, or time of day
#. Qualitative schemes for nominal data that has no inherent ordering,
where color is used only to distinguish categories
:references: matplotlib documentation and examples
http://matplotlib.org/examples/color/colormaps_reference.html
"""
def _get_colormap_mpl(self):
from matplotlib.pyplot import colormaps as _cmaps
return _cmaps()
colormaps = property(_get_colormap_mpl)
def _get_sequentials(self):
return ['Blues', 'BuGn', 'BuPu', 'GnBu', 'Greens', 'Greys', 'OrRd',
'Oranges', 'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu',
'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']
sequentials = property(_get_sequentials)
def _get_sequentials2(self):
return ['afmhot', 'autumn', 'bone', 'cool', 'copper',
'gist_heat', 'gray', 'hot', 'pink',
'spring', 'summer', 'winter']
sequentials2 = property(_get_sequentials2)
def _get_diverging(self):
return ['BrBG', 'PRGn', 'PiYG', 'PuOr', 'RdBu', 'RdGy', 'RdYlBu',
'RdYlGn', 'Spectral', 'bwr', 'coolwarm', 'seismic']
diverging = property(_get_diverging)
def _get_diverging_black(self):
return ['red_black_sky', 'red_black_blue', 'red_black_green', 'yellow_black_blue',
'yellow_black_sky', 'red_black_orange', 'pink_black_green(w3c)'
]
diverging_black = property(_get_diverging_black)
def _get_qualitative(self):
return ['Accent', 'Dark2', 'Paired', 'Pastel1', 'Pastel2',
'Set1', 'Set2', 'Set3']
qualitative = property(_get_qualitative)
def _get_misc(self):
return ['gist_earth', 'terrain', 'ocean', 'gist_stern',
'brg', 'CMRmap', 'cubehelix', 'gnuplot', 'gnuplot2', 'gist_ncar',
'nipy_spectral', 'jet', 'rainbow', 'gist_rainbow', 'hsv', 'flag', 'prism']
misc = property(_get_misc)
[docs] def plot_rgb_from_hex_list(self, cols):
"""This functions takes a list of hexadecimal values and plots
the RGB curves. This can be handy to figure out the RGB functions
to be used in the :meth:`get_cmap`.
.. plot::
:include-source:
:width: 60%
from colormap.colors import Colormap
c = Colormap()
t = ['#FF0000FF', '#FF4D00FF', '#FF9900FF', '#FFE500FF',
'#CCFF00FF', '#80FF00FF', '#33FF00FF', '#00FF19FF',
'#00FF66FF', '#00FFB2FF', '#00FFFFFF', '#00B3FFFF',
'#0066FFFF', '#001AFFFF', '#3300FFFF', '#7F00FFFF',
'#CC00FFFF','#FF00E6FF','#FF0099FF', '#FF004DFF']
c.plot_rgb_from_hex_list(t)
"""
import pylab
red = [hex2rgb(x)[0]/255. for x in cols]
blue = [hex2rgb(x)[2]/255. for x in cols]
green = [hex2rgb(x)[1]/255. for x in cols]
x = pylab.linspace(0, 1, len(cols))
pylab.clf()
pylab.plot(x, red, 'ro-')
pylab.plot(x, green, 'go-')
pylab.plot(x, blue, 'bo-')
pylab.ylim([-0.1, 1.1])
[docs] def cmap_bicolor(self, color1, color2, reverse=False, N=256):
"""Provide 3 colors in format accepted by :class:`Color`
::
>>> red = Color('red')
>>> white = Color('white')
>>> cmap = cmap_bicolor(red, white)
"""
c1 = Color(color1)
c2 = Color(color2)
dico = {'red': [c1.red, c2.red],
'green':[c1.green, c2.green],
'blue':[c1.blue, c2.blue]}
return self.cmap(dico, reverse=reverse, N=N)
[docs] def cmap_linear(self, color1, color2, color3, reverse=False, N=256):
"""Provide 3 colors in format accepted by :class:`Color`
::
red = Color('red')
cmap = cmap_linear(red, 'white', '#0000FF')
"""
c1 = Color(color1)
c2 = Color(color2)
c3 = Color(color3)
dico = {'red': [c1.red, c2.red, c3.red],
'green':[c1.green, c2.green, c3.green],
'blue':[c1.blue, c2.blue, c3.blue]}
return self.cmap(dico, reverse=reverse, N=N)
[docs] def cmap(self, colors=None, reverse=False, N=256):
"""Return a colormap object to be used within matplotlib
:param dict colors: a dictionary that defines the RGB colors to be
used in the colormap. See :meth:`get_cmap_heat` for an example.
:param bool reverse: reverse the colormap is set to True (defaults to False)
:param int N: Defaults to 50
"""
# matplotlib colormaps
if colors in self.colormaps:
if reverse and colors.endswith("_r") is False:
colors += "_r"
from matplotlib.cm import get_cmap
return get_cmap(colors)
# custom ones
elif colors in self.diverging_black:
c1, c2, c3 = colors.split("_")
# special case of sky, which does not exists
c3 = c3.replace("sky", "deep sky blue")
return self.cmap_linear(c1, c2, c3)
elif colors == 'heat':
return self.get_cmap_heat()
elif colors == 'heat_r':
return self.get_cmap_heat_r()
# Keep these dependencies inside the function to allow
# installation of colormap without those dependencies
import numpy as np
# extracted from R, heat.colors(20)
if reverse:
for k in colors.keys():
colors[k].reverse()
# If index not given, RGB colors are evenly-spaced in colormap.
index = np.linspace(0, 1, len(colors['red']))
# Adapt color_data to the form expected by LinearSegmentedColormap.
color_data = dict((key, [(x, y, y) for x, y in zip(index, value)])
for key, value in list(colors.items()))
import matplotlib
f = matplotlib.colors.LinearSegmentedColormap
m = f('my_color_map', color_data, N)
return m
[docs] def get_cmap_heat(self):
"""Return a heat colormap matplotlib-compatible colormap
This heat colormap should be equivalent to heat.colors() in R.
::
>>> from colormap.colors import Colormap
>>> cmap = Colormap.get_cmap_heat()
You can generate the colormap based solely on this information for the RGB
functions along::
d= { 'blue':[0,0,0,0,1],
'green':[0,.35,.7,1,1],
'red':[1,1,1,1,1]}
cmap = Colormap.get_cmap(d)
"""
return self.cmap(
{ 'blue':[0, 0, 0, 0, 1],
'green':[0, .35, .7, 1, 1],
'red':[1, 1, 1, 1, 1]}, reverse=False)
[docs] def get_cmap_heat_r(self):
"""Return a heat colormap matplotlib-compatible colormap
Same as :meth:`get_cmap_heat` but reversed
"""
return self.cmap(
{ 'blue':[0, 0, 0, 0, 1],
'green':[0, .35, .7, 1, 1],
'red':[1, 1, 1, 1, 1]}, reverse=True)
[docs] def get_cmap_rainbow(self):
"""colormap similar to rainbow colormap from R
.. note:: The red is actually appearing on both sides... Yet
this looks like what is coded in R 3.0.1
"""
return self.cmap(
{ 'blue': [0, 0, 0, 1, 1, 1, 0],
'green':[0, 1, 1, 1, 0, 0, 0],
'red': [1, 1, 0, 0, 0, 1, 1]}, reverse=False)
def get_cmap_red_green(self):
return self.cmap(
{ 'green': [0, 0.4, 0.6, .75, .8, .9, 1, .9, .8, .6],
'blue' : [0, .4, .6, .75, .8, .7, .6, .35, .17, .1],
'red': [1, 1, 1, 1, 1, .9, .8, .6, .3, .1]}, reverse=True)
[docs] def test_colormap(self, cmap=None):
"""plot one colormap for testing
By default, test the :meth:`get_cmap_heat`
"""
if cmap is None:
cmap = self.get_cmap_heat()
import numpy as np
from pylab import clf, pcolor, colorbar, show, linspace, axis
A, B = np.meshgrid(linspace(0, 10, 100), linspace(0, 10, 100))
clf()
pcolor((A-5)**2+(B-5)**2, cmap=cmap)
colorbar()
show()
axis('off')
[docs] def plot_colormap(self, cmap_list=None):
"""cmap_list list of valid cmap or name of a set (sequential,
diverging,)
if none, plot all known colors
.. .. plot::
.. :width:80%
.. :include-source:
.. from colormap import Colormap
.. c = Colormap()
.. c.plot_colormap('sequential')
"""
from pylab import subplots
if isinstance(cmap_list, str):
if cmap_list in ['sequentials','sequentials2','qualitative',
'misc','diverging', 'diverging_black']:
cmap_list = getattr(self, cmap_list)
else:
cmap_list = [cmap_list]
if isinstance(cmap_list, list) is not True:
raise TypeError("""input must be a list of srtings or a single string. Each string should be found. For a user-defined cmap, use test_colormap""")
for this in cmap_list:
if this not in self.colormaps and this not in self.diverging_black:
raise ValueError("unknown colormap name. Please check valid names in colormaps attribute")
nrows = len(cmap_list)
gradient = [x/255. for x in range(0,256)]
gradient = [gradient, gradient]
#np.vstack((gradient, gradient))
fig, axes = subplots(nrows=nrows)
fig.subplots_adjust(top=0.95, bottom=0.05, left=0.05, right=0.8)
for ax, name in zip(axes, cmap_list):
ax.imshow(gradient, aspect='auto', cmap=self.cmap(name))
pos = list(ax.get_position().bounds)
x_text = pos[2] + 0.08
y_text = pos[1] + pos[3]/2.
fig.text(x_text, y_text, name, va='center', ha='left', fontsize=10)
# Turn off *all* ticks & spines, not just the ones with colormaps.
for ax in axes:
ax.set_axis_off()