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:
objectInteractive 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 croppedRadDB,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.
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 whenshow_gates=True— off by default so the map stays readable.radars (list of str, optional) – Radar letters to mark (needs
base_pathto 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;Nonedraws 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));Noneomits 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.
xlimdefaults to auto (fills the context/AOI extent);ylimdefaults to the Swiss north band (1.04–1.31 Mm ≈ North 40–310 km). PassNoneto either for auto-framing of that axis.ylim ((min, max) in EPSG:2056 metres, optional) – Axis limits.
xlimdefaults to auto (fills the context/AOI extent);ylimdefaults to the Swiss north band (1.04–1.31 Mm ≈ North 40–310 km). PassNoneto 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 byvariable(per-variable colormap defaults apply, incl. discrete HC classes). Axes: distance along the section line [km] (fromp1) 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()(needscs_polygon+variablecolumns).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_kwargs –
cmap/vmin/vmax/normoverrides.
- 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 differenceP − Lwith 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
zbin is used.iz (int, optional) – Alternative to
z: direct index into thezdimension.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_kwargs –
cmap/vmin/vmax/normoverrides.
- 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"]vsdf["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.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 indfto use for colouring."label": str — colorbar label text."cmap": str or Colormap — colormap passed toscatter."norm": matplotlib Normalize, optional — normalisation."cbar_kwargs": dict — extra kwargs forwarded tofig.colorbar()(e.g.{"ticks": [...]},{"extend": "both"})."scatter_kwargs": dict, optional — extra kwargs forax.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 byconfig[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 ifdtis 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
Falseto force plain matplotlib. PassTrueto 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/swissmodes, 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 forcmap,vmin,vmax,norm, etc.).
- Return type:
- 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(withinaz_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:
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.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_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:
A 2x3 PPI panel for DBZH, ZDR, KDP, RHOHV, TEMP, HC_PYART.
A 2x2 RHI panel for DBZH, ZDR, KDP, RHOHV.
Module contents#
raddb.viz — plotting, profiling dashboards, and report figures.
plot: PPI / RHI / latent-space plotting for reconstructed DataTrees.profiling: dashboards forraddb.helper.StageTimerrecords.report_*: standalone report scripts (run aspython -m raddb.viz.report_raddb_figures), flagged for refactor into reusable visualization utilities.