Coverage for /var/devmt/py/utils4_1.7.0/utils4/cmaps.py: 100%

34 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-21 17:18 +0000

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:Platform: Linux/Windows | Python 3.7+ 

9:Developer: J Berendt 

10:Email: support@s3dev.uk 

11 

12:Comments: n/a 

13 

14:Examples: 

15 

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

17 and preview the colours:: 

18 

19 >>> from utils4.cmaps import cmaps 

20 

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

22 >>> clrs 

23 

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

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

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

27 

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

29 :scale: 75% 

30 :align: center 

31 

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

33 

34 

35 List named colours from the matplotlib colour palette:: 

36 

37 >>> from utils4.cmaps import cmaps 

38 

39 >>> cmaps.get_named_colours() 

40 

41 {'aliceblue': '#F0F8FF', 

42 'antiquewhite': '#FAEBD7', 

43 'aqua': '#00FFFF', 

44 ..., 

45 'whitesmoke': '#F5F5F5', 

46 'yellow': '#FFFF00', 

47 'yellowgreen': '#9ACD32'} 

48 

49 

50 List or retrieve colour map names:: 

51 

52 >>> from utils4.cmaps import cmaps 

53 

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

55 

56 ['magma', 

57 'inferno', 

58 'plasma', 

59 ..., 

60 'tab20_r', 

61 'tab20b_r', 

62 'tab20c_r'] 

63 

64""" 

65# pylint: disable=import-error 

66# pylint: disable=invalid-name 

67# pylint: disable=wrong-import-order 

68 

69import matplotlib 

70import matplotlib.pyplot as plt 

71import numpy as np 

72from typing import Union 

73 

74 

75class _Preview: # pragma: nocover 

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

77 

78 def __init__(self, colours): 

79 """_Preview class initialiser. 

80 

81 Args: 

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

83 preview. 

84 

85 """ 

86 self._c = colours 

87 self._n = len(colours) 

88 self._x = None 

89 self._y = None 

90 self._build_dataset() 

91 

92 def plot(self): 

93 """Plot to show colours.""" 

94 w = 6 if self._n < 50 else 10 

95 h = w/1.618033 

96 _, ax = plt.subplots(figsize=[w, h]) 

97 ax.scatter(self._x, 

98 self._y, 

99 marker='o', 

100 s=100, 

101 c=self._c) 

102 plt.show() 

103 

104 def _build_dataset(self): 

105 """Create a dataset to be plotted.""" 

106 self._x = np.arange(self._n) 

107 self._y = np.sin(self._x*(np.pi/180)) 

108 

109 

110class CMaps(): 

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

112 

113 @staticmethod 

114 def get_cmap(map_name: str, 

115 n: int=25, 

116 as_hex: bool=False, 

117 preview: bool=False) -> Union[list, np.array]: 

118 """Get a list of (n) RGBA or Hex colours from a specified map. 

119 

120 This colour wrapper is specialised to return (n) colours from 

121 a normalised colour map. Meaning, rather than returning the 

122 5 lightest colours, or the 200 lightest to medium colours, the 

123 lightest colours are removed (as often they are difficult to 

124 see in a graph) and the darkest colour is added. The intent 

125 is to provide (n) 'usable' colours for graphing. 

126 

127 Args: 

128 map_name (str): Name of the matplotlib colourmap. 

129 n (int, optional): Number of colours to return. Must 

130 be >= 255. Defaults to 25. 

131 as_hex (bool, optional): Return the colours as a hex string. 

132 Defaults to False, which returns colours as RGBA. 

133 preview (bool, optional): Preview the colour map. Defaults 

134 to False. 

135 

136 Raises: 

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

138 

139 Returns: 

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

141 

142 """ 

143 if (n < 1) | (n > 255): 

144 raise ValueError('The value of n must be: 1 <= n <= 255.') 

145 norm = matplotlib.colors.Normalize(vmin=-150, vmax=256) 

146 cmap = matplotlib.colormaps.get_cmap(map_name) 

147 clrs = cmap(norm(range(256))) 

148 N = int(256//n) 

149 c = clrs[::N] 

150 # Trim colours until desired length is met. 

151 while len(c) > n: 

152 if len(c) - n == 1: 

153 c = c[:-1] 

154 else: 

155 # Shave colours off boths ends until desired length is met. 

156 c = c[:-1] if len(c) % 2 == 0 else c[1:] 

157 c[-1] = clrs[-1] 

158 if as_hex: 

159 c_ = [matplotlib.colors.rgb2hex(i) for i in c] 

160 c = c_[:] 

161 if preview: # pragma: nocover 

162 _Preview(colours=c).plot() 

163 return c 

164 

165 @staticmethod 

166 def get_named_colours() -> dict: 

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

168 

169 Returns: 

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

171 

172 """ 

173 return matplotlib.colors.cnames 

174 

175 @staticmethod 

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

177 """Show the available colour map names. 

178 

179 Args: 

180 view_only (bool, optional): If ``True`` the list will be 

181 printed and ``None`` is returned. If ``False``, the list 

182 is returned and nothing is printed. Defaults to True. 

183 

184 Returns: 

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

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

187 

188 """ 

189 c = plt.colormaps() 

190 if view_only: 

191 print(c) 

192 c = None 

193 return c 

194 

195 

196cmaps = CMaps()