Source code for hermespy.core.visualize

# -*- coding: utf-8 -*-
"""
=============
Visualization
=============
"""

from __future__ import annotations
from typing import Any, overload, Sequence, Tuple

import matplotlib.pyplot as plt
from nptyping import NDArray, Shape

from .executable import Executable

__author__ = "Jan Adler"
__copyright__ = "Copyright 2023, Barkhausen Institut gGmbH"
__credits__ = ["Jan Adler"]
__license__ = "AGPLv3"
__version__ = "1.2.0"
__maintainer__ = "Jan Adler"
__email__ = "jan.adler@barkhauseninstitut.org"
__status__ = "Prototype"


VAT = NDArray[Shape["*, *"], Any]
"""Type alias for a numpy array of matplotlib axes."""


[docs] class Visualizable(object): """Base class for visualizable results.""" @property def title(self) -> str: """Title of the visualizable.""" return self.__class__.__name__ def _get_color_cycle(self) -> Sequence[str]: """Style color rotation. """ with Executable.style_context(): return plt.rcParams["axes.prop_cycle"].by_key()["color"] def _new_axes(self, **kwargs) -> Tuple[plt.Figure, VAT]: """Generate a new figure and axes to plot into. Can be overriden by subclasses to configure custom axes flags. Args: \**kwargs: Additional arguments which cane be used by subclasses. Returns: Tuple of matplotlib figure and axes. """ figure, axes = plt.subplots(1, 1, squeeze=False) return figure, axes def _prepare_axes( self, axes: VAT | None = None, title: str | None = None, **kwargs ) -> Tuple[plt.FigureBase, VAT]: """Prepare axes to plot into. Args: axes (VAT, optional): Axes to plot into. If not provided, a new figure and axes will be generated. See :meth:`Visualizable._new_axes` title (str, optional): Title of the generated figure. Only applied if `axes` are not provided and a new figure is generated. If not specified, :meth:`Visualizable.title` will be applied. Returns: Tuple of the newly generated figure and configured axis. """ figure: plt.FigureBase if axes is None: with Executable.style_context(): figure, axes = self._new_axes(**kwargs) figure.suptitle(self.title if title is None else title) else: if axes.size < 1: raise ValueError("Provided axes array is empty") figure: plt.Figure = axes.flat[0].get_figure() # type: ignore # Make sure we can infer the containing figure from the provided axes if figure is None: raise RuntimeError("Axes are not associated with a figure") return figure, axes @overload def plot(self, axes: VAT) -> plt.FigureBase: ... # pragma: no cover @overload def plot(self) -> plt.FigureBase: ... # pragma: no cover
[docs] def plot(self, axes: VAT | None = None, *, title: str | None = None) -> plt.FigureBase: """Plot a visualizable. Args: axes (VAT, optional): The axis object into which the information should be plotted. If not specified, the routine will generate and return a new figure. title (str, optional): Title of the generated plot. Returns: The newly generated matplotlib figure. """ figure, axes = self._prepare_axes(axes, title) # Visualize the content into the supplied _axes self._plot(axes) # Return a figure if a new one was created return figure
def _plot(self, axes: VAT) -> None: """Subroutine to plot the visualizable into a matplotlib axis. Args: axes (VAT): The axis object into which the information should be plotted. \**kwargs: Additional plotting arguments. """ axis: plt.Axes for axis in axes.flat: # type: ignore axis.text(0.5, 0.5, "NO PLOT AVAILABLE", horizontalalignment="center")