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

1 import matplotlib

2 import numpy as np

3

4

5 def _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