Source code for hifis_surveyval.plotting.matplotlib_color_map_parameters

# hifis-surveyval
# Framework to help developing analysis scripts for the HIFIS Software survey.
#
# SPDX-FileCopyrightText: 2021 HIFIS Software <support@hifis.net>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Module offers functionality to create color maps and fill pattern maps."""
from enum import Enum
from typing import Dict, List

from matplotlib import colors, pyplot


[docs]class FillPattern(Enum): """Represents different fill patterns in Matplotlib plots.""" PATTERN_01 = 1 PATTERN_02 = 2 PATTERN_03 = 3 PATTERN_04 = 4 PATTERN_05 = 5 PATTERN_06 = 6 PATTERN_07 = 7 PATTERN_08 = 8 PATTERN_09 = 9 PATTERN_10 = 10
[docs]class DefaultFillPattern: """Defines a default fill pattern map to be used in plots.""" # Define mapping from enum FillPattern to string representation of fill # pattern. SLASH_FILL_PATTERN: Dict[FillPattern, str] = { FillPattern.PATTERN_01: '#FFFFFF', FillPattern.PATTERN_02: '/', FillPattern.PATTERN_03: '//', FillPattern.PATTERN_04: '///', FillPattern.PATTERN_05: '////', FillPattern.PATTERN_06: '#000000', }
[docs]class CustomColors(Enum): """Represents different colors in Matplotlib plots.""" CUSTOM_01 = 1 CUSTOM_02 = 2 CUSTOM_03 = 3 CUSTOM_04 = 4 CUSTOM_05 = 5 CUSTOM_06 = 6
[docs]class DefaultColors: """Defines default colors to be used in plots.""" # Define mapping from enum Colors to hex representation of colors. HELMHOLTZ_BLUE: Dict[CustomColors, str] = { CustomColors.CUSTOM_01: "#99BDD9", CustomColors.CUSTOM_02: "#669CC6", CustomColors.CUSTOM_03: "#337BB3", CustomColors.CUSTOM_04: "#005AA0", CustomColors.CUSTOM_05: "#004880", CustomColors.CUSTOM_06: "#002D50", }
[docs]class ColorMapParameters: """Creates a color map and a fill pattern map from parameters."""
[docs] def __init__( self, number_colors_required: int = None, use_color: bool = True, use_pattern: bool = False, color_map_name: str = None, use_inverted_color_map: bool = False, brightness_factor: float = 1.0, custom_color_map: Dict[Enum, str] = DefaultColors.HELMHOLTZ_BLUE, pattern_map: Dict[Enum, str] = DefaultFillPattern.SLASH_FILL_PATTERN ) -> None: """ Initialize parameters and creates color map and fill pattern map. Args: number_colors_required (int): Number to specify how many colors are needed. use_color (bool): Flag to specify whether to fill plot objects with colors. use_pattern (bool): Flag to specify whether to fill plot objects with patterns. color_map_name (str): Name of the color map to use. use_inverted_color_map (bool): Flag to specify whether color map need to be inverted. brightness_factor (float): Number to specify brightness of colors in color map. custom_color_map (Dict[CustomColors, str]): List to specify custom colors in a color map. pattern_map (Dict[FillPattern, str]): Dictionary of FillPattern to hatch pattern string mappings. """ self._number_colors_required: int = number_colors_required self._use_color: bool = use_color self._use_pattern: bool = use_pattern self._color_map_name: str = color_map_name self._use_inverted_color_map: bool = use_inverted_color_map self._brightness_factor: float = brightness_factor self._custom_color_map: Dict[CustomColors, str] = custom_color_map self._fill_pattern_map: Dict[FillPattern, str] = pattern_map self._color_map: colors.Colormap = None self._pattern_map: List[str] = None self.create_color_map_and_patterns()
@property def use_color(self) -> bool: """Access for use color flag.""" return self._use_color @property def use_pattern(self) -> bool: """Access for use pattern flag.""" return self._use_pattern @property def color_map(self) -> colors.Colormap: """Access color map property.""" return self._color_map @property def pattern_map(self) -> List[str]: """Access fill pattern map property.""" return self._pattern_map
[docs] def create_color_map_and_patterns(self) -> None: """Create a color map and fill pattern map from parameters.""" # Custom color map is given if self._color_map_name is None and self._custom_color_map is not None: color_count = len(self._custom_color_map) # Set number of colors required to number of all colors in custom # map if not number is given. if self._number_colors_required is None: self._number_colors_required = color_count # Raise exception if there are not enough colors in the # selected color map.< if self._number_colors_required > color_count: raise NotImplementedError( f"Attempt to plot a chart " f"with more then {color_count} columns." f"Color palette has not enough colors for all of them." ) # Reduce the colormap to have only as much colors as there # are columns so each columns color index matches the column # index. color_map_list = [] for index in range(1, self._number_colors_required + 1): # Get color specified in custom color map at respective # position color = self._custom_color_map[CustomColors(index)] # Use color specified in pattern map if given if not self._use_color and self._use_pattern: if FillPattern(index) in self._fill_pattern_map and \ '#' in self._fill_pattern_map[FillPattern(index)]: # Use bar color specified in pattern map color = self._fill_pattern_map[FillPattern(index)] else: # Use bar color white in case of hatch patterns given color = '#FFFFFF' # Use a brightness factor that modifies the color in # all color components. color_modified = [component * self._brightness_factor for component in colors.to_rgb(color)] color_map_list.append(color_modified) self._color_map = colors.ListedColormap(color_map_list) else: # No custom color map given, hence use pre-defined color map # Use default color map if no color map name is given if self._color_map_name is None: self._color_map_name = "Pastel1" # Use specified color map or the inverted counterpart map_name = self._color_map_name \ if not self._use_inverted_color_map \ else self._color_map_name + "_r" self._color_map = pyplot.get_cmap(map_name) color_count = len(self._color_map.colors) # Raise exception if there are not enough colors in the # selected color map. if self._number_colors_required > color_count: raise NotImplementedError( f"Attempt to plot a chart " f"with more then {color_count} columns." f"Color palette has not enough colors for all of them." ) # Reduce the colormap to have only as much colors as there # are columns so each columns color index matches the column # index. color_map_list = [] for index in range(self._number_colors_required): # Get color specified in color map at respective position color = self._color_map.colors[index] # Use color specified in pattern map if given if not self._use_color and self._use_pattern: if FillPattern(index + 1) in self._fill_pattern_map and \ '#' in self._fill_pattern_map[ FillPattern(index + 1)]: # Use bar color specified in pattern map color = self._fill_pattern_map[FillPattern(index + 1)] else: # Use bar color white in case of hatch patterns given color = '#FFFFFF' # Use a brightness factor that modifies the color in # all color components. color_modified = [component * self._brightness_factor for component in color] color_map_list.append(color_modified) self._color_map = colors.ListedColormap(color_map_list) # Generate pattern map self._pattern_map = None if self._fill_pattern_map is not None: # Raise exception if there are not enough patterns in the selected # pattern map. if self._number_colors_required > len(self._fill_pattern_map): raise NotImplementedError( f"Attempt to plot a chart " f"with more then {color_count} columns." f"Color palette has not enough colors for all of them." ) # Reduce the pattern map to have only as much patterns as there # are columns so each columns pattern index matches the column # index. self._pattern_map = [ self._fill_pattern_map[key] for key in self._fill_pattern_map.keys() ]