Source code for radar.utils.plotter.geometry

import polars as pl
import plotly.express as px
from abc import ABC, abstractmethod
from typing import cast

from radar.utils.typing import (
    DataHeader,
)


[docs] class GeometryInterface(ABC): """Abstract base class defining the interface for geometry-related operations."""
[docs] @abstractmethod def geometry(self) -> None: """Process or retrieve geometry data. This method must be implemented by subclasses to define specific geometry behaviors. """ pass
[docs] class Geometry: """A utility class for handling and visualizing spatial geometry data. Attributes: FIGURE_WIDTH (int): Default width of the generated Plotly figure in pixels. FIGURE_HEIGHT (int): Default height of the generated Plotly figure in pixels. DISPLAY_GRID_PIXELS (int): Pixel spacing configuration for the grid layout. """ FIGURE_WIDTH = 800 FIGURE_HEIGHT = 800 DISPLAY_GRID_PIXELS = 100
[docs] @classmethod def _image( cls, df: pl.DataFrame, ) -> None: """Generates and displays a symmetric square scatter plot of X and Y positions. This method plots positional data from a Polars DataFrame, determines a symmetric bounding box based on the maximum data extent, and locks the aspect ratio to ensure accurate geometric scaling. Args: df (pl.DataFrame): The input DataFrame containing spatial coordinates. Must contain columns defined by `DataHeader.X_POS_M` and `DataHeader.Y_POS_M`. Raises: ValueError: If the DataFrame is empty or if the min/max values for the position coordinates cannot be calculated. """ # Assuming you have a column name for the color data, e.g., DataHeader.COLOR_VAL fig = px.scatter( df, x=DataHeader.X_POS_M, y=DataHeader.Y_POS_M, color=df[DataHeader.GEOM_AMP_GAIN_DB], color_continuous_scale=px.colors.sequential.Viridis, width=cls.FIGURE_WIDTH, height=cls.FIGURE_HEIGHT, ) values = [ df[DataHeader.X_POS_M].min(), df[DataHeader.X_POS_M].max(), df[DataHeader.Y_POS_M].min(), df[DataHeader.Y_POS_M].max(), ] if any(v is None for v in values): raise ValueError("Empty dataframe") values = cast(tuple[float, float], values) # Pad the bounding box by 10% to ensure points near edges aren't cut off max_extent = max(abs(v) for v in values) * 1.1 fig.update_xaxes(range=[-max_extent, max_extent]) fig.update_yaxes(range=[-max_extent, max_extent]) fig.update_layout( title="Position Distribution", yaxis_scaleanchor="x", # keeps aspect ratio square coloraxis_colorbar=dict( title="Element Scaling [dB]" ), # <-- Optional: Customize the color axis title ) fig.show()