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

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. 

7 

8:Developer: J Berendt 

9:Email: support@s3dev.uk 

10 

11:Comments: n/a 

12 

13:Examples: 

14 

15 Retrieve 5 colours from the 'viridis' colour map in hex format 

16 and preview the colours:: 

17 

18 >>> from utils4.cmaps import cmaps 

19 

20 >>> clrs = cmaps.get_cmap('viridis', 15, as_hex=True, preview=True) 

21 >>> clrs 

22 

23 ['#2d718e', '#297b8e', '#25858e', '#218f8d', '#1f998a', 

24 '#20a386', '#26ad81', '#34b679', '#46c06f', '#5cc863', 

25 '#73d056', '#8ed645', '#aadc32', '#c5e021', '#fde725'] 

26 

27 .. figure:: _static/img/cmaps_viridis15.png 

28 :scale: 75% 

29 :align: center 

30 

31 Preview of the requested 'viridis' colour map of 15 colours 

32 

33 

34 List named colours from the matplotlib colour palette:: 

35 

36 >>> from utils4.cmaps import cmaps 

37 

38 >>> cmaps.get_named_colours() 

39 

40 {'aliceblue': '#F0F8FF', 

41 'antiquewhite': '#FAEBD7', 

42 'aqua': '#00FFFF', 

43 ..., 

44 'whitesmoke': '#F5F5F5', 

45 'yellow': '#FFFF00', 

46 'yellowgreen': '#9ACD32'} 

47 

48 

49 List or retrieve colour map names:: 

50 

51 >>> from utils4.cmaps import cmaps 

52 

53 >>> cmaps.view_cmaps(view_only=True) 

54 

55 ['magma', 

56 'inferno', 

57 'plasma', 

58 ..., 

59 'tab20_r', 

60 'tab20b_r', 

61 'tab20c_r'] 

62 

63""" 

64# pylint: disable=import-error 

65# pylint: disable=invalid-name 

66# pylint: disable=wrong-import-order 

67 

68import matplotlib 

69import matplotlib.pyplot as plt 

70import numpy as np 

71from typing import Union 

72 

73 

74class _Preview: # pragma: nocover 

75 """Provide a preview for a given colourmap.""" 

76 

77 def __init__(self, colours): 

78 """_Preview class initialiser. 

79 

80 Args: 

81 colours (Union[list, np.array]): Iterable of colours for 

82 preview. 

83 

84 """ 

85 self._c = colours 

86 self._n = len(colours) 

87 self._x = None 

88 self._y = None 

89 self._build_dataset() 

90 

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() 

102 

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)) 

107 

108 

109class CMaps(): 

110 """Provides an easy-access layer to ``matplotlib``'s colour maps.""" 

111 

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. 

118 

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. 

125 

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. 

134 

135 Raises: 

136 ValueError: If the value of ``n`` is not between 1 and 255. 

137 

138 Returns: 

139 Union[list, np.array]: Iterable of (n) colours. 

140 

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 

163 

164 @staticmethod 

165 def get_named_colours() -> dict: 

166 """Return a dictionary of CSS name and hex value. 

167 

168 Returns: 

169 dict: A dict of named colours as ``{name: hex_code}`` pairs. 

170 

171 """ 

172 return matplotlib.colors.cnames 

173 

174 @staticmethod 

175 def view_cmaps(view_only: bool=True) -> Union[list, None]: 

176 """Show the available colour map names. 

177 

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. 

182 

183 Returns: 

184 Union[list, None]: A list of colour maps names if 

185 ``view-only`` is False, otherwise None. 

186 

187 """ 

188 c = plt.colormaps() 

189 if view_only: 

190 print(c) 

191 c = None 

192 return c 

193 

194 

195cmaps = CMaps()