Source code for uvisbox.Modules.UncertaintyLobes.uncertainty_lobes_vis

"""
Visualization rendering for uncertainty lobes.

This module renders uncertainty lobe meshes using matplotlib:
    - Draw outer lobes (wedges)
    - Draw inner lobes (wedges)
    - Draw median direction arrows
"""

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors as mcolors
from matplotlib.patches import Polygon


def _validate_alpha(name, value):
    """Validate an opacity value and return it as a float."""
    if not isinstance(value, (int, float, np.number)):
        raise TypeError(f"{name} must be a number between 0 and 1.")
    if not 0 <= value <= 1:
        raise ValueError(f"{name} must be between 0 and 1.")
    return float(value)


[docs] def visualize_uncertainty_lobes( mesh, show_median=True, ax=None, outer_lobe_edge_color='skyblue', outer_lobe_face_color='skyblue', inner_lobe_edge_color='skyblue', inner_lobe_face_color='skyblue', median_arrow_color='blue', outer_lobe_alpha=0.3, inner_lobe_alpha=1.0, median_arrow_alpha=1.0, ): """ Render uncertainty lobe mesh with matplotlib. Draws wedge-shaped glyphs representing directional uncertainty with: - Outer wedges (light blue, semi-transparent) - larger angular spread - Inner wedges (light blue, opaque) - smaller angular spread - Median arrows (blue) - showing the most probable direction Parameters ----------- mesh : dict From uncertainty_lobes_mesh() containing: - 'wedges': outer lobe wedge data - 'inner_wedges': inner lobe wedge data (optional) - 'arrows': median direction arrow data show_median : bool Whether to show median direction arrows (default: True) ax : matplotlib.Axes, optional Existing axis to draw on. If None, creates new figure and axis. outer_lobe_edge_color : color, optional Matplotlib color for outer lobe edges. Default is ``'skyblue'``. outer_lobe_face_color : color, optional Matplotlib color for outer lobe faces. Default is ``'skyblue'``. inner_lobe_edge_color : color, optional Matplotlib color for inner lobe edges. Default is ``'skyblue'``. inner_lobe_face_color : color, optional Matplotlib color for inner lobe faces. Default is ``'skyblue'``. median_arrow_color : color, optional Matplotlib color for median direction arrows. Default is ``'blue'``. outer_lobe_alpha : float, optional Opacity for outer lobe wedges, including their edges. Must be in ``[0, 1]``. Default is ``0.3``. inner_lobe_alpha : float, optional Opacity for inner lobe wedges, including their edges. Must be in ``[0, 1]``. Default is ``1.0``. median_arrow_alpha : float, optional Opacity for median direction arrows. Must be in ``[0, 1]``. Default is ``1.0``. Returns -------- ax : matplotlib.Axes The axis with drawn lobe glyphs """ outer_lobe_alpha = _validate_alpha('outer_lobe_alpha', outer_lobe_alpha) inner_lobe_alpha = _validate_alpha('inner_lobe_alpha', inner_lobe_alpha) median_arrow_alpha = _validate_alpha('median_arrow_alpha', median_arrow_alpha) # Create figure and axis if not provided if ax is None: fig, ax = plt.subplots(figsize=(12, 6)) # Render outer wedges (percentile1) for wedge_data in mesh['wedges']: vertices = wedge_data['vertices'] # Create polygon patch for the wedge wedge_polygon = Polygon( vertices, closed=True, facecolor=mcolors.to_rgba(outer_lobe_face_color, outer_lobe_alpha), edgecolor=mcolors.to_rgba(outer_lobe_edge_color, outer_lobe_alpha), ) ax.add_patch(wedge_polygon) # Render inner wedges (percentile2) if present if mesh['inner_wedges'] is not None: for wedge_data in mesh['inner_wedges']: vertices = wedge_data['vertices'] wedge_polygon = Polygon( vertices, closed=True, facecolor=mcolors.to_rgba(inner_lobe_face_color, inner_lobe_alpha), edgecolor=mcolors.to_rgba(inner_lobe_edge_color, inner_lobe_alpha), ) ax.add_patch(wedge_polygon) # Render median arrows if requested if show_median: arrows = mesh['arrows'] positions = arrows['positions'] directions = arrows['directions'] lengths = arrows['lengths'] for i in range(positions.shape[0]): center = positions[i] direction = directions[i] length = lengths[i] # Arrow endpoint arrow_end = center + direction * length ax.annotate( '', xy=arrow_end, xytext=center, arrowprops=dict( facecolor=median_arrow_color, edgecolor=median_arrow_color, arrowstyle='->', lw=2, mutation_scale=10, alpha=median_arrow_alpha ) ) return ax