Coverage for C:\src\imod-python\imod\visualize\common.py: 14%

21 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-08 10:26 +0200

1import matplotlib 

2import numpy as np 

3 

4 

5def _cmapnorm_from_colorslevels(colors, levels): 

6 """ 

7 Create ListedColormap and BoundaryNorm from colors (either a list of colors 

8 or a named colormap) and a list of levels. Number of levels must be at least 

9 two, and number of colors therefore least three. 

10 

11 In the case of a list of colors, the resulting colorbar looks like: 

12 < color 0 | color 1 | color 2 ... n-1 | color n > 

13 ^ ^ ^ 

14 level 0 level 1 level n-1 

15 

16 Parameters 

17 ---------- 

18 

19 colors : list of str, list of RGBA/RGBA tuples, colormap name (str), or matplotlib.colors.Colormap 

20 If list, it should be a Matplotlib acceptable list of colors. Length N. 

21 Accepts both tuples of (R, G, B) and hexidecimal (e.g. `#7ec0ee`). 

22 If str, use an existing Matplotlib colormap. This function will 

23 autmatically add distinctive colors for pixels lower or high than the given 

24 min respectively max level. 

25 If LinearSegmentedColormap, you can use something like 

26 `matplotlib.cm.get_cmap('jet')` as input. This function will not alter 

27 the colormap, so add under- and over-colors yourself. 

28 

29 Looking for good colormaps? Try: http://colorbrewer2.org/ 

30 Choose a colormap, and use the HEX JS array. 

31 levels : listlike of floats or integers 

32 Boundaries between the legend colors/classes. Length: N - 1. 

33 

34 Returns 

35 ------- 

36 cmap : matplotlib.colors.ListedColormap 

37 norm : matplotlib.colors.BoundaryNorm 

38 """ 

39 # check number of levels 

40 if len(levels) < 2: 

41 raise ValueError(f"Number of levels {levels} should exceed 1.") 

42 

43 # check monotonic increasing levels 

44 if not (np.diff(levels) > 0).all(): 

45 raise ValueError(f"Levels {levels} are not monotonic increasing.") 

46 

47 if isinstance(colors, matplotlib.colors.Colormap): 

48 # use given cmap 

49 cmap = colors 

50 else: 

51 nlevels = len(levels) 

52 if isinstance(colors, str): 

53 # Use given cmap, but fix the under and over colors 

54 # The colormap (probably) does not have a nice under and over color. 

55 # So we cant use `cmap = matplotlib.cm.get_cmap(colors)` 

56 cmap = matplotlib.colormaps[colors] 

57 colors = cmap(np.linspace(0, 1, nlevels + 1)) 

58 

59 # Validate number of colors vs number of levels 

60 ncolors = len(colors) 

61 if not nlevels == ncolors - 1: 

62 raise ValueError( 

63 f"Incorrect number of levels. Number of colors is {ncolors}," 

64 f" expected {ncolors - 1} levels, got {nlevels} levels instead." 

65 ) 

66 # Create cmap from given list of colors 

67 cmap = matplotlib.colors.ListedColormap(colors[1:-1]) 

68 cmap.set_under( 

69 colors[0] 

70 ) # this is the color for values smaller than raster.min() 

71 cmap.set_over( 

72 colors[-1] 

73 ) # this is the color for values larger than raster.max() 

74 norm = matplotlib.colors.BoundaryNorm(levels, cmap.N) 

75 return cmap, norm