from __future__ import annotations
import copy
import warnings
from collections import defaultdict
from typing import Any, Union
import matplotlib as mpl
import matplotlib.colors as colors
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import Colormap, LinearSegmentedColormap, to_rgba
from matplotlib.lines import Line2D
from .cluster import Clustering
from .interface import Interface
[docs]
def anything_to_rgba(color) -> tuple[float]:
if isinstance(color, str):
if color == '':
rgba = (0, 0, 0, 0)
else:
# Convert string colors (names or hex) to RGBA using matplotlib
rgba = to_rgba(color)
elif isinstance(color, (list, tuple)):
if len(color) == 3:
# Assuming RGB, append alpha value 1.0 to make it RGBA
rgba = tuple(color) + (1.0,)
elif len(color) == 4:
rgba = tuple(color)
else:
raise ValueError(
"Array color must be length 3 (RGB) or 4 (RGBA)")
elif isinstance(color, float) or isinstance(color, int):
# Assuming grayscale value, replicate it across R, G, B; set alpha to 1.0
if 0 <= color <= 1:
rgba = (color, color, color, 1.0)
else:
raise ValueError("Float color must be in the range [0, 1]")
else:
raise TypeError("Color must be a string, an array, or a float")
# Ensure all values are in the [0, 1] range
rgba_normalized = tuple(max(0, min(1, c)) for c in rgba)
return rgba_normalized
[docs]
def initialize_colormap(colormap: str | dict[str, Any]) -> tuple[str, Colormap]:
'''
Returns colormap name and colormap
Example: initialize_colormap(
{
"Name": "custom1",
"Colors": [
(255 / 255, 79 / 255, 20 / 255),
(1 / 255, 180 / 255, 155 / 255),
(71 / 255, 71 / 255, 240 / 255),
],
}
'''
if isinstance(colormap, str):
return colormap, plt.get_cmap(colormap)
colormap_name = colormap['Name']
# Check if the colormap already exists
if colormap['Name'] in plt.colormaps():
warnings.warn(
f"Colormap '{colormap_name}' already exists. Using the existing colormap.")
return colormap_name, plt.get_cmap(colormap_name)
# Colormap doesn't exist, create a new one
colors = [anything_to_rgba(color) for color in colormap['Colors']]
new_colormap = LinearSegmentedColormap.from_list(colormap_name, colors)
mpl.colormaps.register(name=colormap_name, cmap=new_colormap)
print(f"Colormap '{colormap_name}' created and registered.")
return colormap_name, new_colormap
[docs]
class ColorScheme:
default_colormap_name, default_colormap = initialize_colormap('coolwarm')
default_scatter_color = default_colormap(0)
default_distribution_color = default_colormap(0)
default_none_color = anything_to_rgba((0, 255, 0))
available_colormaps = plt.colormaps()
available_markers = list(Line2D.markers.keys())
default_markers = ('o', 'X', '<', '>', '^', 'v', '4', '*', '+',
'D', 'H', 'P', 'd', 'h', 'p', 's', '_', '|', ',', '.',)
default_scatter_marker = default_markers[0]
default_none_marker = 'x'
default_centroid_marker = '*'
default_centroid_color = anything_to_rgba('red')
default_timeline_color = anything_to_rgba('grey')
default_timeline_linestyle = '--'
default_line_color = anything_to_rgba('black')
default_line_linestyle = '-'
[docs]
class MethodLogger:
def __init__(self) -> None:
self.methods = {}
def __call__(self, method_name, modes):
"""
Makes the MethodLogger instance callable. It can be used as a decorator to register methods.
Methods:
-----------
property_name : str
The name of the property that the method provides.
"""
def decorator(class_method):
if method_name in self.methods:
raise ValueError(
f"Property {method_name} is already defined.")
self.methods[method_name] = {
'method': class_method, 'modes': modes}
return class_method
return decorator
[docs]
def copy(self) -> ColorScheme.MethodLogger:
# Create a new instance of MethodLogger
new_instance = ColorScheme.MethodLogger()
new_instance.methods = copy.deepcopy(
self.methods) # Deep copy the dictionary
return new_instance
[docs]
def keys(self) -> list[str]:
return list(self.methods.keys())
def __getitem__(self, key):
# Returns None if key is not found
return self.methods.get(key, None)
def __setitem__(self, key, value):
self.methods[key] = value
def __contains__(self, key):
return key in self.methods
def __str__(self):
return str(self.methods)
def __len__(self):
return len(self.methods)
def __iter__(self):
return iter(self.methods)
method_logger = MethodLogger()
def __init__(self, interface: Interface = None, ) -> None:
self.interface = interface
self.clear()
[docs]
def clear(self) -> None:
self._cluster_color_cache = {}
self._node_color_cache = {}
self._cluster_marker_cache = {}
self._node_marker_cache = {}
self._graph_color_cache = {}
[docs]
def get_image(self, settings, group_number: int) -> int:
image = 'Current' if settings is None else settings.get(
'group_number', group_number)
if image == 'Current':
return group_number
image = int(image)
if image < 0:
image += len(self.interface.groups)
return image
[docs]
@staticmethod
def request_to_tuple(request) -> tuple:
def convert(item):
if isinstance(item, dict):
return tuple(sorted((k, convert(v)) for k, v in item.items()))
elif isinstance(item, list):
return tuple(convert(v) for v in item)
else:
return item
return convert(request)
@property
def methods(self) -> list:
"""Returns a list of property names that have been registered."""
return list(self.method_logger.keys())
[docs]
def requires_tracking(self, settings) -> Union[bool, dict]:
mode = settings.get('mode', None)
if mode in ('Cluster Marker', 'Cluster Color', 'Cluster Parameter Color'):
if 'settings' not in settings or 'clustering_settings' not in settings['settings']:
raise ValueError(
f'{mode} requires tracking, but no clustering settings is provided in settings {settings}')
return settings['settings']['clustering_settings']
return False
#############################
# Markers
#############################
[docs]
def get_cluster_marker(self, clustering_settings: dict[str, Any], none_marker: str) -> dict:
request: dict[str, Any] = {
'clustering_settings': clustering_settings, 'none_marker': none_marker}
request_tuple = ColorScheme.request_to_tuple(request)
if request_tuple not in self._cluster_marker_cache:
cluster_names: list[str] = self.interface.cluster_tracker.get_unique_clusters(
clustering_settings)[0]
l = len(self.default_markers)
cluster_markers = {
cluster_name: self.default_markers[i % l] for i, cluster_name in enumerate(cluster_names)}
cluster_markers[None] = none_marker
self._cluster_marker_cache[request_tuple] = cluster_markers
return self._cluster_marker_cache[request_tuple]
[docs]
def get_cluster_node_marker(self, image: int, clustering_settings: dict[str, Any], none_marker: str) -> dict:
request: dict[str, Any] = {
'image': image, 'clustering_settings': clustering_settings, 'none_marker': none_marker}
request_tuple = ColorScheme.request_to_tuple(request)
if request_tuple not in self._node_marker_cache:
clustering: Clustering = self.interface.groups[request['image']].clustering(
**(clustering_settings))
cluster_mapping = clustering.nodes_labels_dict()
cluster_to_marker_mapping = self.get_cluster_marker(
clustering_settings=clustering_settings, none_marker=none_marker)
marker_mapping = {
node: cluster_to_marker_mapping[cluster] for node, cluster in cluster_mapping.items()}
self._node_marker_cache[request_tuple] = defaultdict(
lambda: request['none_marker'], marker_mapping)
return self._node_marker_cache[request_tuple]
[docs]
@method_logger('Scatter Marker', modes=('Constant Marker', 'Cluster Marker'))
def scatter_markers_nodes(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list[str]]:
mode = mode or 'Constant Marker'
image = self.get_image(settings, group_number)
if mode == 'Constant Marker':
if settings is not None and 'Marker' in settings:
return settings['Marker']
return self.default_scatter_marker
elif mode == 'Cluster Marker':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
# marker for nodes that are not in the cluster
none_marker = settings.get('None Marker', self.default_none_marker)
data = self.get_cluster_node_marker(
image=image, clustering_settings=settings['clustering_settings'], none_marker=none_marker)
return [data[node] for node in nodes]
[docs]
@method_logger('Centroid Marker', modes=('Constant Marker', 'Cluster Marker'))
def centroid_markers(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None,
group_number: Union[int, None] = None, mode: str = None, settings: Union[dict, None] = None) -> Union[str, list[str]]:
mode = mode or 'Constant Marker'
image = self.get_image(settings, group_number)
if mode == 'Constant Marker':
if settings is not None and 'Marker' in settings:
return settings['Marker']
return self.default_centroid_marker
elif mode == 'Cluster Marker':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
# marker for nodes that are not in the cluster
none_marker = settings.get('None Marker', self.default_none_marker)
data = self.get_cluster_marker(
clustering_settings=settings['clustering_settings'], none_marker=none_marker)
return [data[cluster] for cluster in clusters]
[docs]
@method_logger('Timeline Style', modes=('Constant Style',))
def timeline_linestyle(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list[str]]:
mode = mode or 'Constant Style'
if mode == 'Constant Style':
if settings is not None and 'linestyle' in settings:
return settings['linestyle']
return self.default_timeline_linestyle
[docs]
@method_logger('Line Style', modes=('Constant Style',))
def line_linestyle(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list[str]]:
mode = mode or 'Constant Style'
if mode == 'Constant Style':
if settings is not None and 'linestyle' in settings:
return settings['linestyle']
return self.default_line_linestyle
#############################
# Colors
#############################
[docs]
def get_cluster_color(self, clustering_settings: dict[str, Any], colormap: str, none_color: str) -> dict:
cluster_request: dict[str, Any] = {
'clustering_settings': clustering_settings, 'colormap': colormap, 'none_color': none_color}
cluster_request_tuple = ColorScheme.request_to_tuple(
cluster_request)
if cluster_request_tuple not in self._cluster_color_cache:
cluster_names: list[str] = self.interface.cluster_tracker.get_unique_clusters(
clustering_settings)[0]
cm = plt.get_cmap(colormap)
color_indices = np.linspace(0, 1, len(cluster_names))
# Map each number to a color in the colormap
cluster_colors = {cluster_name: cm(
color_index) for cluster_name, color_index in zip(cluster_names, color_indices)}
cluster_colors[None] = none_color
self._cluster_color_cache[cluster_request_tuple] = cluster_colors
return self._cluster_color_cache[cluster_request_tuple]
[docs]
def get_cluster_node_color(self, image: int, clustering_settings: dict[str, Any], colormap: str, none_color: str) -> dict:
request: dict[str, Any] = {'image': image,
'clustering_settings': clustering_settings, 'colormap': colormap, 'none_color': none_color}
request_tuple = ColorScheme.request_to_tuple(request)
if request_tuple not in self._node_color_cache:
clustering: Clustering = self.interface.groups[image].clustering(
**clustering_settings)
cluster_mapping = clustering.nodes_labels_dict()
cluster_to_colormapping = self.get_cluster_color(
clustering_settings=clustering_settings, colormap=colormap, none_color=none_color)
colormapping = {
node: cluster_to_colormapping[cluster] for node, cluster in cluster_mapping.items()}
self._node_color_cache[request_tuple] = defaultdict(
lambda: none_color, colormapping)
return self._node_color_cache[request_tuple]
[docs]
def get_parameter_based_cluster_color(self, parameter: str, clustering_settings: dict[str, Any], colormap: str, none_color: str) -> dict:
cluster_request: dict[str, Any] = {'parameters': (
parameter,), 'clustering_settings': clustering_settings, 'colormap': colormap, 'none_color': none_color}
cluster_request_tuple = ColorScheme.request_to_tuple(
cluster_request)
if cluster_request_tuple not in self._cluster_color_cache:
cluster_values: dict[str, float] = self.interface.cluster_tracker.get_final_value(
clustering_settings, parameter)
floats = cluster_values.values()
norm = colors.Normalize(vmin=min(floats), vmax=max(floats))
cm = plt.get_cmap(colormap)
cluster_colors = {cluster_name: cm(
norm(value)) for cluster_name, value in cluster_values.items()}
cluster_colors[None] = none_color
self._cluster_color_cache[cluster_request_tuple] = cluster_colors
return self._cluster_color_cache[cluster_request_tuple]
[docs]
def get_parameter_based_cluster_node_color(self, image: int, parameter: str, clustering_settings: dict[str, Any], colormap: str, none_color: str) -> dict:
request: dict[str, Any] = {'image': image,
'clustering_settings': clustering_settings, 'colormap': colormap, 'none_color': none_color}
request_tuple = ColorScheme.request_to_tuple(request)
if request_tuple not in self._node_color_cache:
clustering: Clustering = self.interface.groups[image].clustering(
**clustering_settings)
cluster_mapping = clustering.nodes_labels_dict()
cluster_to_colormapping = self.get_parameter_based_cluster_color(
parameter=parameter, clustering_settings=clustering_settings, colormap=colormap, none_color=none_color)
colormapping = {
node: cluster_to_colormapping[cluster] for node, cluster in cluster_mapping.items()}
self._node_color_cache[request_tuple] = defaultdict(
lambda: none_color, colormapping)
return self._node_color_cache[request_tuple]
[docs]
@method_logger('Scatter Color', modes=('Constant Color', 'Parameter Colormap', 'Cluster Color', 'Cluster Parameter Color'))
def scatter_colors_nodes(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list, list[list]]:
mode = mode or 'Constant Color'
image = self.get_image(settings, group_number)
if mode == 'Constant Color':
if settings is not None and 'Color' in settings:
return anything_to_rgba(settings['Color'])
return self.default_scatter_color
elif mode == 'Parameter Colormap':
if settings is None or 'parameter' not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "parameter" key is expected')
request_settings = {'parameters': (settings['parameter'],)}
request_settings['scale'] = settings.get('scale', 'Linear')
colormap = settings.get('colormap', self.default_colormap_name)
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
request = {**request_settings,
'colormap': colormap, 'image': image, 'none_color': none_color}
request_tuple = ColorScheme.request_to_tuple(request)
if request_tuple not in self._node_color_cache:
data = self.interface.dynamic_data_cache[image][{
'method': 'calculate_node_values', 'settings': request_settings}]
floats = data[settings['parameter']]
norm = colors.Normalize(vmin=min(floats), vmax=max(floats))
cm = plt.get_cmap(colormap)
self._node_color_cache[request_tuple] = defaultdict(lambda: none_color, {node: cm(
norm(value)) for node, value in zip(data['Nodes'], floats)})
data = self._node_color_cache[request_tuple]
return [data[node] for node in nodes]
elif mode == 'Cluster Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_cluster_node_color(
image=image, clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[node] for node in nodes]
elif mode == 'Cluster Parameter Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
if settings is None or 'parameter' not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "parameter" key is expected')
# request_settings['scale'] = settings.get('scale', 'Linear')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_parameter_based_cluster_node_color(
image=image, parameter=settings['parameter'], clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[node] for node in nodes]
[docs]
@method_logger('Centroid Color', modes=('Constant Color', 'Cluster Color', 'Cluster Parameter Color'))
def centroid_colors(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list, list[list]]:
mode = mode or 'Constant Color'
image = self.get_image(settings, group_number)
if mode == 'Constant Color':
if settings is not None and 'Color' in settings:
return anything_to_rgba(settings['Color'])
return self.default_centroid_color
elif mode == 'Cluster Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_cluster_color(
clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[cluster] for cluster in clusters]
elif mode == 'Cluster Parameter Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
if settings is None or 'parameter' not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "parameter" key is expected')
# request_settings['scale'] = settings.get('scale', 'Linear')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_parameter_based_cluster_color(parameter=settings['parameter'],
clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[cluster] for cluster in clusters]
[docs]
@method_logger('Fill Color', modes=('Cluster Color', 'Cluster Parameter Color'))
def fill_colors(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list, list[list]]:
mode = mode or 'Cluster Color'
image = self.get_image(settings, group_number)
if mode == 'Cluster Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_cluster_color(
clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[cluster] for cluster in clusters]
elif mode == 'Cluster Parameter Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
if settings is None or 'parameter' not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "parameter" key is expected')
# request_settings['scale'] = settings.get('scale', 'Linear')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_parameter_based_cluster_color(parameter=settings['parameter'],
clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[cluster] for cluster in clusters]
[docs]
@method_logger('Distribution Color', modes=('Constant Color',))
def distribution_color(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list[str]]:
mode = mode or 'Constant Color'
if mode == 'Constant Color':
if settings is not None and 'Color' in settings:
return anything_to_rgba(settings['Color'])
return self.default_distribution_color
[docs]
@method_logger('Timeline Color', modes=('Constant Color',))
def timeline_color(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list[str]]:
mode = mode or 'Constant Color'
if mode == 'Constant Color':
if settings is not None and 'Color' in settings:
return anything_to_rgba(settings['Color'])
return self.default_timeline_color
[docs]
@method_logger('Line Color', modes=('Constant Color', 'Parameter Colormap', 'Cluster Color', 'Cluster Parameter Color'))
def line_color(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list[str]]:
mode = mode or 'Constant Color'
image = self.get_image(settings, group_number)
if mode == 'Constant Color':
if settings is not None and 'Color' in settings:
return anything_to_rgba(settings['Color'])
return self.default_line_color
elif mode == 'Parameter Colormap':
if settings is None or 'parameter' not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "parameter" key is expected')
request_settings = {'parameters': (settings['parameter'],)}
request_settings['scale'] = settings.get('scale', 'Linear')
colormap = settings.get('colormap', self.default_colormap_name)
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
request = {**request_settings,
'colormap': colormap, 'image': image, 'none_color': none_color}
request_tuple = ColorScheme.request_to_tuple(request)
if request_tuple not in self._node_color_cache:
data = self.interface.dynamic_data_cache[image][{
'method': 'calculate_node_values', 'settings': request_settings}]
floats = data[settings['parameter']]
norm = colors.Normalize(vmin=min(floats), vmax=max(floats))
cm = plt.get_cmap(colormap)
self._node_color_cache[request_tuple] = defaultdict(lambda: none_color, {node: cm(
norm(value)) for node, value in zip(data['Nodes'], floats)})
data = self._node_color_cache[request_tuple]
return [data[node] for node in nodes]
elif mode == 'Cluster Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_cluster_node_color(
image=image, clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[node] for node in nodes]
elif mode == 'Cluster Parameter Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
if settings is None or 'parameter' not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "parameter" key is expected')
# request_settings['scale'] = settings.get('scale', 'Linear')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_parameter_based_cluster_node_color(
image=image, parameter=settings['parameter'], clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[node] for node in nodes]
[docs]
@method_logger('Graph Line Color', modes=('Constant Color', 'Graph Parameter'))
def graph_line_color(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list[str]]:
'''
Graph parameters functions list can be accessed in Group.common_functions.keys()
'''
mode = mode or 'Constant Color'
image = self.get_image(settings, group_number)
if mode == 'Constant Color':
if settings is not None and 'Color' in settings:
return anything_to_rgba(settings['Color'])
return self.default_line_color
elif mode == 'Graph Parameter':
if settings is None or 'parameter' not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "parameter" key is expected')
colormap = settings.get('colormap', self.default_colormap_name)
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
value_limits = settings.get('value_limits', (0., 1.))
request_settings = {'parameters': (
settings['parameter'],), }
request_settings['function'] = settings.get('function', 'Mean')
request = {**request_settings,
'colormap': colormap, 'image': image, 'none_color': none_color, 'value_limits': value_limits}
request_tuple = ColorScheme.request_to_tuple(request)
if request_tuple not in self._graph_color_cache:
data = self.interface.dynamic_data_cache[image][{
'method': 'calculate_function_of_node_values', 'settings': request_settings}]
value = data[settings['parameter']]
norm = colors.Normalize(
vmin=value_limits[0], vmax=value_limits[1])
cm = plt.get_cmap(colormap)
self._graph_color_cache[request_tuple] = cm(norm(value))
data = self._graph_color_cache[request_tuple]
return data
[docs]
@method_logger('Cluster Line Color', modes=('Constant Color', 'Cluster Color', 'Cluster Parameter Color'))
def cluster_line_colors(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict, None] = None) -> Union[str, list, list[list]]:
mode = mode or 'Constant Color'
image = self.get_image(settings, group_number)
if mode == 'Constant Color':
if settings is not None and 'Color' in settings:
return anything_to_rgba(settings['Color'])
return self.default_line_color
elif mode == 'Cluster Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_cluster_color(
clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[cluster] for cluster in clusters]
elif mode == 'Cluster Parameter Color':
if settings is None or "clustering_settings" not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "clustering_settings" key is expected')
if settings is None or 'parameter' not in settings:
raise ValueError(
f'Settings ({settings}) are not formatted correctly. "parameter" key is expected')
# request_settings['scale'] = settings.get('scale', 'Linear')
colormap = settings.get('colormap', self.default_colormap_name)
# color for nodes that are not in the cluster
none_color = anything_to_rgba(
settings.get('None Color', self.default_none_color))
data = self.get_parameter_based_cluster_color(parameter=settings['parameter'],
clustering_settings=settings['clustering_settings'], colormap=colormap, none_color=none_color)
return [data[cluster] for cluster in clusters]
[docs]
@method_logger('Color Map', modes=('Independent Colormap',))
def colorbar(self, nodes: Union[list[tuple[int]], None] = None, clusters: Union[list[str], None] = None, group_number: Union[int, None] = None,
mode: str = None, settings: Union[dict[str, Any], None] = None) -> Colormap:
mode = mode or 'Independent Colormap'
if mode == 'Independent Colormap':
if settings is not None and 'Color Pallet' in settings:
return plt.get_cmap(settings['Color Pallet'])
return plt.get_cmap(self.default_colormap_name)
[docs]
def copy(self):
# TODO: Implement copy
return ColorScheme(self.interface)
[docs]
def new_interface(self, interface: Interface) -> ColorScheme:
colorscheme = self.copy()
colorscheme.interface = interface
return colorscheme
[docs]
def apply_changes(self, changes):
pass
[docs]
def variation(self, changes):
copy = self.copy()
copy.apply_changes(changes)
return copy
def __getitem__(self, key: str):
return self.method_logger[key]