raddb.viz package#

Submodules#

raddb.viz.interactive module#

raddb/viz/interactive.py#

Interactive Area-of-Interest selection on an ipyleaflet map (Jupyter).

Draw a shape on the map and it is dispatched to the matching RadDB method:

rectangle -> crop_by_bbox polygon -> crop_by_polygone marker -> crop_around_point (distance from the widget) polyline -> extract_cross_section (first & last vertex)

The map is WGS-84 (lon/lat), so the drawn coordinates are passed with crs=4326 (reprojected to LV95 internally). Each call returns a new data-carrying RadDB. The drawn AOI can be saved as GeoJSON and reloaded later via crop_by_polygone("aoi.geojson").

This is an optional UI layer — it needs ipyleaflet + ipywidgets and a Jupyter frontend. The hand-typed / shapefile crop methods are unaffected.

class raddb.viz.interactive.AOISelector(db, radars=None, center=None, zoom=8, point_radius_m=15000.0)[source][source]#

Bases: object

Interactive map to draw an AOI and crop a DataFrame with it.

Displayed automatically in Jupyter (returns from RadDB.interactive_crop()). After drawing a shape and clicking Apply crop, the result is available on:

  • result — the cropped RadDB,

  • kind — which crop ran (bbox/polygon/point/cross_section),

  • feature — the drawn GeoJSON feature (lon/lat).

Parameters:
  • db (RadDB) – A data-carrying RadDB (from RadDB.open()).

  • radars (list of str, optional) – Radars to mark on the map. Default: the radars in db.

  • center ((lat, lon), optional) – Initial map centre. Default: mean of the radar sites, else Switzerland.

  • zoom (int) –

  • point_radius_m (float) – Default distance (metres) for the marker (point) crop.

display()[source][source]#

raddb.viz.plot module#

raddb/plot.py#

PPI, RHI, and latent-space scatter plots for RadDB.

A radar gate is not a rectangle: it is a curved trapezoid in geographic space whose footprint depends on range, azimuth, elevation, and Earth curvature. The right way to render it is to feed matplotlib’s pcolormesh the 2-D centroid coordinates of every gate — matplotlib then builds the correct curved quadrilaterals automatically.

The reconstructed DataTree already carries the per-gate (lon, lat, alt, x, y, z) coords on every sweep Dataset (attached during parquet_to_datatree), so these plot functions do not need any geometry computation of their own.

If cartopy is available the PPI is drawn on an Azimuthal Equidistant map centred on the radar site (coastlines, borders, gridlines). This applies to both coords="geo" and coords="cartesian" — in the latter case the axes tick labels are formatted in km from the radar.

raddb.viz.plot.plot_aoi_quicklook(aoi_geom, selected=None, radars=None, base_path=None, context='switzerland', ax=None, figsize=(9, 9), title=None, range_rings_km=(100,), show_gates=False, gate_sample=50000, xlim=None, ylim=(1040000, 1310000), save_path=None)[source][source]#

Map an AOI on a country-scale background for a quick sanity-check.

Answers “is my AOI where I think it is?”: the AOI footprint (red) drawn on the Switzerland outline with the involved radar sites, in a square national-extent view. Rendered in Swiss LV95 (EPSG:2056), axis units km. The Swiss outline comes from cartopy’s cached Natural Earth data; everything else is dependency-free, so the map still draws (without the outline) if that data is missing.

Parameters:
  • aoi_geom (shapely geometry) – AOI footprint in EPSG:2056 (Polygon for bbox/polygon/point AOIs; LineString for a cross-section line).

  • selected (pd.DataFrame, optional) – Selected gates carrying x_2056 / y_2056. Only scattered when show_gates=True — off by default so the map stays readable.

  • radars (list of str, optional) – Radar letters to mark (needs base_path to load their site coords).

  • base_path (str or Path, optional) – RadDB archive base directory, for loading radar site coordinates.

  • context (str, shapely geometry, GeoDataFrame, or None) – Map background. "switzerland" (default) draws the national outline; None draws none; a geometry/GeoDataFrame draws a custom context.

  • ax (matplotlib Axes, optional) – Draw into an existing axis instead of creating a figure.

  • figsize (tuple) – Default (9, 9) — square, sized to show the whole country.

  • title (str, optional) –

  • range_rings_km (number or iterable of number, optional) – Range-ring radii (km) drawn dashed around each radar site. Accepts a single value (100) or several ((50, 100)); None omits them.

  • show_gates (bool) – Scatter the selected gate centroids (default False).

  • gate_sample (int) – Cap the number of scattered centroids (random subsample); None = all.

  • xlim ((min, max) in EPSG:2056 metres, optional) – Axis limits. xlim defaults to auto (fills the context/AOI extent); ylim defaults to the Swiss north band (1.04–1.31 Mm ≈ North 40–310 km). Pass None to either for auto-framing of that axis.

  • ylim ((min, max) in EPSG:2056 metres, optional) – Axis limits. xlim defaults to auto (fills the context/AOI extent); ylim defaults to the Swiss north band (1.04–1.31 Mm ≈ North 40–310 km). Pass None to either for auto-framing of that axis.

  • save_path (str or Path, optional) – If given, save the figure (dpi=150, tight).

Return type:

(fig, ax)

raddb.viz.plot.plot_cross_section(df_cs, variable: str = 'DBZH', ax=None, figsize=(12, 5), title: str | None = None, add_colorbar: bool = True, edgecolor='none', xlim=None, ylim=None, **plot_kwargs)[source][source]#

Render a vertical cross-section from a RadDB.crop_cross_section() result.

Each row’s cs_polygon — the gate’s 4-corner polygon in the (distance-along-line, altitude) plane — is drawn as a filled patch coloured by variable (per-variable colormap defaults apply, incl. discrete HC classes). Axes: distance along the section line [km] (from p1) vs altitude [km ASL].

Note: pass a single volume (filter by volume_time) and ideally a single radar — overlapping radars/volumes draw on top of each other.

Parameters:
  • df_cs (pd.DataFrame) – Output of RadDB.crop_cross_section() (needs cs_polygon + variable columns).

  • variable (str) – Column to colour by (default "DBZH").

  • ax (matplotlib Axes, optional) –

  • figsize (tuple) –

  • title (str, optional) –

  • add_colorbar (bool) –

  • edgecolor (matplotlib color) – Patch edge colour (default "none"; e.g. "k" to outline gates).

  • xlim ((dmin_km, dmax_km), optional) – Along-section distance limits in km.

  • ylim ((zmin_km, zmax_km), optional) – Altitude limits in km.

  • **plot_kwargscmap / vmin / vmax / norm overrides.

Return type:

(fig, ax, collection)

raddb.viz.plot.plot_grid(ds, variable: str = 'DBZH', radar=None, z: float | None = None, iz: int | None = None, time=None, ax=None, figsize=(8, 7), title: str | None = None, add_colorbar: bool = True, use_cartopy: bool | None = None, xlim=None, ylim=None, **plot_kwargs)[source][source]#

Map one horizontal slice of an RadDB.aoi_to_grid() Dataset.

Selects one radar (or a pair for a difference map), one altitude layer, and (if present) one volume time, then draws the (y, x) slice in the Swiss LV95 frame (East/North km ticks, optional cartopy country borders) — the same frame as the AOI quicklook and coords="swiss" PPIs.

Parameters:
  • ds (xr.Dataset) – Output of RadDB.aoi_to_grid() (dims (time?, radar, z, y, x)).

  • variable (str) – Data variable to draw (default "DBZH").

  • radar (str or (str, str), optional) – A radar letter selects that radar’s layer (inferred when the grid holds exactly one). A pair ("P", "L") draws the per-voxel difference P L with a symmetric diverging colormap — the cross-radar comparison the per-radar grid exists for.

  • z (float, optional) – Altitude of the layer to draw (m ASL); the nearest z bin is used.

  • iz (int, optional) – Alternative to z: direct index into the z dimension.

  • time (str or datetime, optional) – Volume time (nearest match); required only when the grid has several.

  • ax (matplotlib Axes, optional) –

  • figsize – Usual matplotlib options.

  • title – Usual matplotlib options.

  • add_colorbar – Usual matplotlib options.

  • use_cartopy (bool, optional) – Draw LV95-reprojected country borders (auto when cartopy is available).

  • xlim ((min, max) in LV95 metres, optional) –

  • ylim ((min, max) in LV95 metres, optional) –

  • **plot_kwargscmap / vmin / vmax / norm overrides.

Return type:

(fig, ax, mesh)

raddb.viz.plot.plot_latent_scatter(df: DataFrame, config: list[dict], figsize: tuple[float, float] | None = None, fig_height: float = 4.6, **scatter_kwargs)[source][source]#

Publication-ready 2×3 AMT latent-space scatter figure.

Creates a 2-row × 3-column figure with width=6.9 inches (AMT full-column width). Each subplot shows a scatter of df["L1"] vs df["L2"] coloured by one radar variable. A compact inset colorbar with a white semi-transparent background is placed inside each subplot.

x-tick labels and x-axis labels are shown only on the bottom row. y-tick labels and y-axis labels are shown only on the first column.

Parameters:
  • df (pd.DataFrame) – Must contain columns "L1", "L2", and the variable column named in each panel’s "var" key.

  • config (list of dict) –

    Exactly 6 panel descriptors, one per subplot (row-major: panels 0-2 fill row 0, panels 3-5 fill row 1). Each dict must have:

    • "var" : str — column in df to use for colouring.

    • "label" : str — colorbar label text.

    • "cmap" : str or Colormap — colormap passed to scatter.

    • "norm" : matplotlib Normalize, optional — normalisation.

    • "cbar_kwargs" : dict — extra kwargs forwarded to fig.colorbar() (e.g. {"ticks": [...]}, {"extend": "both"}).

    • "scatter_kwargs" : dict, optional — extra kwargs for ax.scatter() (e.g. {"s": 1, "alpha": 0.5}).

  • figsize (tuple, optional) – Override figure size. Default (6.9, fig_height).

  • fig_height (float) – Figure height in inches. Default 4.6.

  • **scatter_kwargs – Global fallback kwargs for ax.scatter() (overridden per-panel by config[i]["scatter_kwargs"]).

Returns:

  • fig (matplotlib.figure.Figure)

  • axes (ndarray of shape (2, 3))

  • Example config

  • ————–

  • config = [

    {“var”: “DBZH”, “label”: “DBZH [dBZ]”, “cmap”: “turbo”,

    ”norm”: Normalize(-10, 60), “cbar_kwargs”: {}, “scatter_kwargs”: {“s”: 0.5}},

    {“var”: “ZDR”, “label”: “ZDR [dB]”, “cmap”: “RdBu_r”,

    ”norm”: Normalize(-2, 5), “cbar_kwargs”: {}, “scatter_kwargs”: {“s”: 0.5}},

  • ]

raddb.viz.plot.plot_ppi(dt, sweep, variable: str, ax=None, use_cartopy: bool | None = None, coords: str = 'geo', add_range_rings: bool = True, add_colorbar: bool = True, title: str | None = None, figsize: tuple[float, float] = (6, 6), xlim: tuple[float, float] | None = None, ylim: tuple[float, float] | None = None, **plot_kwargs)[source][source]#

Plan Position Indicator from a single sweep of a reconstructed DataTree.

Parameters:
  • dt (xr.DataTree or xr.Dataset) – Reconstructed DataTree (from parquet_to_datatree) or single sweep Dataset.

  • sweep (int or str) – Sweep index (3) or group name ("sweep_3"). Ignored if dt is already a Dataset.

  • variable (str) – Variable to plot: "DBZH", "ZDR", "RHOHV", "PHIDP", "HZT", "TEMP", "HC_MCH", "HC_PYART".

  • ax (matplotlib.axes.Axes, optional) –

  • use_cartopy (bool, optional) – If None (default), auto-detect: use cartopy when installed. Pass False to force plain matplotlib. Pass True to require cartopy (raises ImportError if missing).

  • coords ({"geo", "cartesian", "swiss"}) –

    Coordinate frame for the axes:

    • "geo"(longitude, latitude) degrees.

    • "cartesian"(x, y) km relative to the radar (origin at the site). With cartopy this is an AEQD map centred on the radar.

    • "swiss" (aka "lv95" / "2056") — absolute Swiss LV95 (x_2056, y_2056), axis ticks in km (E from the 2 000 km false-easting, N from 1 000 km). This is the same frame as the AOI quicklook, so a cropped-df PPI lines up with crop_* overlays. With cartopy (use_cartopy) it overlays country borders reprojected to LV95 as a basemap; otherwise the same plot without borders.

  • add_range_rings (bool) – For cartesian / swiss modes, draw 50/100/150 km range rings.

  • add_colorbar (bool) –

  • title (str, optional) –

  • figsize (tuple) – Figure size in inches. Default (6, 6).

  • xlim (tuple, optional) – x-axis limits in the natural units of the chosen coords (degrees for "geo", km for "cartesian").

  • ylim (tuple, optional) – y-axis limits (same units as xlim).

  • **plot_kwargs – Forwarded to pcolormesh (overrides defaults for cmap, vmin, vmax, norm, etc.).

Return type:

matplotlib.collections.QuadMesh

raddb.viz.plot.plot_rhi(dt, azimuth: float, variable: str, radar: str = '', az_tol: float = 1.0, max_range_km: float | None = None, max_height_km: float | None = 20.0, ke: float = 1.3333333333333333, ax=None, add_colorbar: bool = True, title: str | None = None, figsize: tuple[float, float] = (10, 4), **plot_kwargs)[source][source]#

Pseudo-RHI from a volume PPI scan — PyART’s cross_section_ppi.

For every sweep in the DataTree, selects the ray whose azimuth is closest to azimuth (within az_tol, wrap-around safe), regrids all rays to a common range axis, and renders a 2-D (ground_range, height_ASL) pcolormesh using gate edges computed with the 4/3 Earth-radius model — so curved-trapezoid gates and Earth curvature are both physically correct.

Parameters:
  • dt (xr.DataTree) – Reconstructed DataTree (from parquet_to_datatree).

  • azimuth (float) – Target azimuth in degrees (0..360; 0 = North, clockwise).

  • variable (str) –

  • radar (str, optional) – Radar identifier shown in the plot title (e.g. "A").

  • az_tol (float) – Maximum allowed angular distance between the requested azimuth and the nearest available ray.

  • max_range_km (float, optional) – Clip the ground-range axis.

  • max_height_km (float, optional) – Clip the height axis.

  • ke (float) – Effective Earth radius factor (default 4/3, pyart standard).

  • ax (see plot_ppi.) –

  • add_colorbar (see plot_ppi.) –

  • title (see plot_ppi.) –

  • figsize (see plot_ppi.) –

  • **plot_kwargs (see plot_ppi.) –

Return type:

matplotlib.collections.QuadMesh

raddb.viz.profiling module#

raddb/viz/profiling.py#

Profiling dashboards for raddb.helper.StageTimer records.

Moved verbatim from raddb/helper.py so that all plotting code lives under raddb.viz. The functions import matplotlib lazily (inside the function body), so importing this module stays lightweight.

raddb.viz.profiling.plot_profiling_dashboard(timer: StageTimer, title_prefix: str = '', save_path: str | None = None)[source][source]#

3x2 profiling dashboard: stage totals, distribution pie, per-volume bars, stage breakdown per volume, and mean time per sweep line chart.

raddb.viz.profiling.plot_stage_totals(timer: StageTimer, title: str = 'Pipeline Total Time per Stage', save_path: str | None = None)[source][source]#

Horizontal bar chart: total wall-clock time per pipeline stage.

raddb.viz.profiling.plot_sweep_timing(timer: StageTimer, title: str = 'Processing Time per Sweep', save_path: str | None = None)[source][source]#

Box-plot: distribution of total sweep processing time across volumes.

raddb.viz.profiling.plot_volume_timing(timer: StageTimer, title: str = 'Processing Time per Volume', save_path: str | None = None)[source][source]#

Stacked bar chart: per-volume time breakdown by pipeline stage.

raddb.viz.report_hc_reference_figure module#

Generate the hydrometeor-classification reference figure for the report.

The figure shows a single PPI sweep with: 1. MeteoSwiss operational hydrometeor classification (HC_MCH) 2. PyART-based hydrometeor classification (HC_PYART) 3. Gate-level agreement between the two stored labels

The agreement panel is intended as an illustrative reminder that the two classification products are reference labels, not absolute ground truth.

raddb.viz.report_hc_reference_figure.main() int[source][source]#

raddb.viz.report_raddb_figures module#

Generate RadDB report figures.

This script is a report-focused version of the multi-feature panel section in examples/basic_usage.py. It generates:

  1. A 2x3 PPI panel for DBZH, ZDR, KDP, RHOHV, TEMP, HC_PYART.

  2. A 2x2 RHI panel for DBZH, ZDR, KDP, RHOHV.

raddb.viz.report_raddb_figures.create_ppi_panel(dt, output_path: Path) Path[source][source]#
raddb.viz.report_raddb_figures.create_rhi_panel(dt, output_path: Path) Path[source][source]#
raddb.viz.report_raddb_figures.load_panel_datatree()[source][source]#
raddb.viz.report_raddb_figures.main() int[source][source]#

Module contents#

raddb.viz — plotting, profiling dashboards, and report figures.

  • plot: PPI / RHI / latent-space plotting for reconstructed DataTrees.

  • profiling: dashboards for raddb.helper.StageTimer records.

  • report_*: standalone report scripts (run as python -m raddb.viz.report_raddb_figures), flagged for refactor into reusable visualization utilities.