diff --git a/laser_echo_analysis/src/laser_echo_analysis/visualization.py b/laser_echo_analysis/src/laser_echo_analysis/visualization.py new file mode 100644 index 0000000..09f89d5 --- /dev/null +++ b/laser_echo_analysis/src/laser_echo_analysis/visualization.py @@ -0,0 +1,72 @@ +from __future__ import annotations +import logging +from pathlib import Path +from typing import Dict, Any +import matplotlib.pyplot as plt + + +OUTPUT_PATH = Path("output/laser_analysis_plot.png") + + +class VisualizationError(Exception): + """Custom exception for visualization-related errors.""" + pass + + +def visualize_results(analysis_results: Dict[str, Any]) -> None: + """Erzeugt eine grafische Darstellung der Analyseergebnisse. + + Args: + analysis_results (Dict[str, Any]): Ergebnisse aus der Analyse mit Schlüsseln + 'peak', 'average_noise' und 'signal_to_noise_ratio'. + + Raises: + VisualizationError: Wenn die Eingabedaten unvollständig oder ungültig sind. + """ + required_keys = {"peak", "average_noise", "signal_to_noise_ratio"} + if not isinstance(analysis_results, dict): + raise VisualizationError("analysis_results must be a dictionary.") + if not required_keys.issubset(analysis_results.keys()): + missing = required_keys - analysis_results.keys() + raise VisualizationError(f"Missing required analysis keys: {missing}") + + # Validate numeric values + for key in required_keys: + value = analysis_results[key] + if not isinstance(value, (int, float)): + raise VisualizationError(f"Value for '{key}' must be numeric, got {type(value)}") + + logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") + logging.info("Creating visualization of analysis results...") + + try: + fig, ax = plt.subplots(figsize=(6, 4)) + metrics = list(required_keys) + values = [analysis_results[k] for k in metrics] + + bars = ax.bar(metrics, values, color=['#1f77b4', '#ff7f0e', '#2ca02c']) + ax.set_title("Laser Echo Analysis Results") + ax.set_ylabel("Value") + ax.grid(axis='y', linestyle='--', alpha=0.7) + + # Add value labels on top of bars + for bar in bars: + height = bar.get_height() + ax.annotate(f'{height:.2f}', + xy=(bar.get_x() + bar.get_width() / 2, height), + xytext=(0, 3), # offset + textcoords="offset points", + ha='center', va='bottom') + + OUTPUT_PATH.parent.mkdir(parents=True, exist_ok=True) + plt.tight_layout() + plt.savefig(OUTPUT_PATH) + plt.close(fig) + logging.info(f"Visualization successfully saved to {OUTPUT_PATH!s}.") + + except Exception as exc: + logging.exception("Failed to create or save the visualization.") + raise VisualizationError(str(exc)) from exc + + +__all__ = ["visualize_results"]