Coverage for /var/devmt/py/utils4_1.8.0/utils4/cmaps.py: 100%
34 statements
« prev ^ index » next coverage.py v7.6.9, created at 2025-08-26 21:39 +0100
« prev ^ index » next coverage.py v7.6.9, created at 2025-08-26 21:39 +0100
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3"""
4:Purpose: This module provides an easy-access, light-weight wrapper,
5 around ``matplotlib``'s colour maps, and can be used for
6 retrieving and previewing named colour maps.
8:Developer: J Berendt
9:Email: support@s3dev.uk
11:Comments: n/a
13:Examples:
15 Retrieve 5 colours from the 'viridis' colour map in hex format
16 and preview the colours::
18 >>> from utils4.cmaps import cmaps
20 >>> clrs = cmaps.get_cmap('viridis', 15, as_hex=True, preview=True)
21 >>> clrs
23 ['#2d718e', '#297b8e', '#25858e', '#218f8d', '#1f998a',
24 '#20a386', '#26ad81', '#34b679', '#46c06f', '#5cc863',
25 '#73d056', '#8ed645', '#aadc32', '#c5e021', '#fde725']
27 .. figure:: _static/img/cmaps_viridis15.png
28 :scale: 75%
29 :align: center
31 Preview of the requested 'viridis' colour map of 15 colours
34 List named colours from the matplotlib colour palette::
36 >>> from utils4.cmaps import cmaps
38 >>> cmaps.get_named_colours()
40 {'aliceblue': '#F0F8FF',
41 'antiquewhite': '#FAEBD7',
42 'aqua': '#00FFFF',
43 ...,
44 'whitesmoke': '#F5F5F5',
45 'yellow': '#FFFF00',
46 'yellowgreen': '#9ACD32'}
49 List or retrieve colour map names::
51 >>> from utils4.cmaps import cmaps
53 >>> cmaps.view_cmaps(view_only=True)
55 ['magma',
56 'inferno',
57 'plasma',
58 ...,
59 'tab20_r',
60 'tab20b_r',
61 'tab20c_r']
63"""
64# pylint: disable=import-error
65# pylint: disable=invalid-name
66# pylint: disable=wrong-import-order
68import matplotlib
69import matplotlib.pyplot as plt
70import numpy as np
71from typing import Union
74class _Preview: # pragma: nocover
75 """Provide a preview for a given colourmap."""
77 def __init__(self, colours):
78 """_Preview class initialiser.
80 Args:
81 colours (Union[list, np.array]): Iterable of colours for
82 preview.
84 """
85 self._c = colours
86 self._n = len(colours)
87 self._x = None
88 self._y = None
89 self._build_dataset()
91 def plot(self):
92 """Plot to show colours."""
93 w = 6 if self._n < 50 else 10
94 h = w/1.618033
95 _, ax = plt.subplots(figsize=[w, h])
96 ax.scatter(self._x,
97 self._y,
98 marker='o',
99 s=100,
100 c=self._c)
101 plt.show()
103 def _build_dataset(self):
104 """Create a dataset to be plotted."""
105 self._x = np.arange(self._n)
106 self._y = np.sin(self._x*(np.pi/180))
109class CMaps():
110 """Provides an easy-access layer to ``matplotlib``'s colour maps."""
112 @staticmethod
113 def get_cmap(map_name: str,
114 n: int=25,
115 as_hex: bool=False,
116 preview: bool=False) -> Union[list, np.array]:
117 """Get a list of (n) RGBA or Hex colours from a specified map.
119 This colour wrapper is specialised to return (n) colours from
120 a normalised colour map. Meaning, rather than returning the
121 5 lightest colours, or the 200 lightest to medium colours, the
122 lightest colours are removed (as often they are difficult to
123 see in a graph) and the darkest colour is added. The intent
124 is to provide (n) 'usable' colours for graphing.
126 Args:
127 map_name (str): Name of the matplotlib colourmap.
128 n (int, optional): Number of colours to return. Must
129 be >= 255. Defaults to 25.
130 as_hex (bool, optional): Return the colours as a hex string.
131 Defaults to False, which returns colours as RGBA.
132 preview (bool, optional): Preview the colour map. Defaults
133 to False.
135 Raises:
136 ValueError: If the value of ``n`` is not between 1 and 255.
138 Returns:
139 Union[list, np.array]: Iterable of (n) colours.
141 """
142 if (n < 1) | (n > 255):
143 raise ValueError('The value of n must be: 1 <= n <= 255.')
144 norm = matplotlib.colors.Normalize(vmin=-150, vmax=256)
145 cmap = matplotlib.colormaps.get_cmap(map_name)
146 clrs = cmap(norm(range(256)))
147 N = int(256//n)
148 c = clrs[::N]
149 # Trim colours until desired length is met.
150 while len(c) > n:
151 if len(c) - n == 1:
152 c = c[:-1]
153 else:
154 # Shave colours off boths ends until desired length is met.
155 c = c[:-1] if len(c) % 2 == 0 else c[1:]
156 c[-1] = clrs[-1]
157 if as_hex:
158 c_ = [matplotlib.colors.rgb2hex(i) for i in c]
159 c = c_[:]
160 if preview: # pragma: nocover
161 _Preview(colours=c).plot()
162 return c
164 @staticmethod
165 def get_named_colours() -> dict:
166 """Return a dictionary of CSS name and hex value.
168 Returns:
169 dict: A dict of named colours as ``{name: hex_code}`` pairs.
171 """
172 return matplotlib.colors.cnames
174 @staticmethod
175 def view_cmaps(view_only: bool=True) -> Union[list, None]:
176 """Show the available colour map names.
178 Args:
179 view_only (bool, optional): If ``True`` the list will be
180 printed and ``None`` is returned. If ``False``, the list
181 is returned and nothing is printed. Defaults to True.
183 Returns:
184 Union[list, None]: A list of colour maps names if
185 ``view-only`` is False, otherwise None.
187 """
188 c = plt.colormaps()
189 if view_only:
190 print(c)
191 c = None
192 return c
195cmaps = CMaps()