confusius.plotting¶
plotting ¶
Plotting module for fUSI data.
Modules:
-
image–Image visualization utilities for fUSI data.
Classes:
-
VolumePlotter–Manager for volume slice plots with coordinate-based overlay support.
Functions:
-
draw_napari_labels–Open a napari viewer to interactively paint integer labels over fUSI data.
-
labels_from_layer–Convert a napari Labels layer to an integer label map DataArray.
-
plot_carpet–Plot voxel intensities across time as a raster image.
-
plot_contours–Plot mask contours as a grid of 2D slice panels.
-
plot_napari–Display fUSI data using the napari viewer.
-
plot_volume–Plot 2D slices of a volume using matplotlib.
VolumePlotter ¶
Manager for volume slice plots with coordinate-based overlay support.
This class maintains the state of a figure with multiple axes, each representing a slice through a volume at a specific coordinate. It enables overlaying multiple volumes on the same axes by matching coordinates.
Parameters:
-
(slice_mode¶str, default:'z') –The dimension along which slices are taken (e.g.,
"z"). -
(figure¶Figure, default:None) –The figure containing the axes. If not provided, a new figure will be created on the first call to
add_volume. -
(axes¶ndarray, default:None) –2D array of
matplotlib.axes.Axes. If not provided, axes will be created on the first call toadd_volume. -
(black_bg¶bool, default:True) –Whether to set the figure background to black.
-
(yincrease¶bool, default:False) –Whether the y-axis increases upward. When
False, y coordinates decrease upward. -
(xincrease¶bool, default:True) –Whether the x-axis increases to the right.
Methods:
-
add_contours–Add mask contours to existing axes.
-
add_volume–Plot or overlay a volume on the axes.
-
close–Close the figure and release resources.
-
savefig–Save the figure to a file.
-
show–Display the figure.
add_contours ¶
add_contours(
mask: DataArray,
*,
colors: dict[int | str, str] | str | None = None,
linewidths: float = 1.5,
linestyles: str = "solid",
match_coordinates: bool = True,
slice_coords: list[float] | None = None,
**kwargs,
) -> VolumePlotter
Add mask contours to existing axes.
Parameters:
-
(mask¶DataArray) –Integer label map in one of two formats:
- Flat label map: Spatial dims only, e.g.
(z, y, x). Background voxels labeled0; each unique non-zero integer identifies a distinct, non-overlapping region. Theregioncoordinate of the output holds the integer label values. - Stacked mask format: Has a leading
maskdimension followed by spatial dims, e.g.(mask, z, y, x). Each layer has values in{0, region_id}and regions may overlap. Theregioncoordinate of the output holds themaskcoordinate values (e.g., region label).
- Flat label map: Spatial dims only, e.g.
-
(colors¶dict[int | str, str] or str, default:None) –Color specification for contour lines.
dict: maps each label (integer index) or region acronym (string) to a color string.str: applies one color to all regions.None: colors are derived fromattrs["cmap"]andattrs["norm"]when present, otherwise from thetab10/tab20colormap.
-
(linewidths¶float, default:1.5) –Width of contour lines in points.
-
(linestyles¶str, default:"solid") –Line style for contour lines (e.g.
"solid","dashed"). -
(match_coordinates¶bool, default:True) –If
True, overlay contours on axes whose slice coordinate matches the mask. IfFalse, plot sequentially on all axes. -
(slice_coords¶list[float], default:None) –Coordinate values along the plotter's
slice_modeat which to draw contours. Slices are selected by nearest-neighbour lookup. If not provided, all coordinate values alongslice_modeare used. -
–**kwargs¶Additional keyword arguments passed to
matplotlib.axes.Axes.plot.
Returns:
-
VolumePlotter–Returns
selffor method chaining.
Raises:
-
ValueError–If the plotter's
slice_modeis not a dimension ofmask. -
ValueError–If
maskis not 3D or 4D with a leadingmaskdimension.
add_volume ¶
add_volume(
data: DataArray,
*,
slice_coords: Sequence[float] | None = None,
match_coordinates: bool = True,
cmap: str | Colormap | None = None,
norm: Normalize | None = None,
vmin: float | None = None,
vmax: float | None = None,
threshold: float | None = None,
threshold_mode: Literal["lower", "upper"] = "lower",
alpha: float = 1.0,
show_colorbar: bool = True,
cbar_label: str | None = None,
show_titles: bool = True,
show_axis_labels: bool = True,
show_axis_ticks: bool = True,
show_axes: bool = True,
nrows: int | None = None,
ncols: int | None = None,
dpi: int | None = None,
) -> VolumePlotter
Plot or overlay a volume on the axes.
Parameters:
-
(data¶DataArray) –3D volume data. Unitary dimensions (except
slice_mode) are squeezed before processing. -
(slice_coords¶list[float], default:None) –Specific coordinates to plot. If None, uses all coordinates from data.
-
(match_coordinates¶bool, default:True) –If True, match slice coordinates to the stored coordinate mapping (for overlays). If False, plot sequentially on all axes (requires exact axis count match).
-
(cmap¶str or Colormap, default:None) –Colormap. When not provided, falls back to
data.attrs["cmap"]if present, otherwise"gray". -
(norm¶Normalize, default:None) –Normalization instance (e.g.
BoundaryNormfor integer label maps). When not provided, falls back todata.attrs["norm"]if present. When a norm is active,vminandvmaxare ignored. -
(vmin¶float, default:None) –Lower bound of the colormap. Defaults to the 2nd percentile. Ignored when
normis provided explicitly (that is, not just inherited from data attributes). -
(vmax¶float, default:None) –Upper bound of the colormap. Defaults to the 98th percentile. Ignored when
normis provided explicitly (that is, not just inherited from data attributes). -
(threshold¶float, default:None) –Threshold value for masking.
-
(threshold_mode¶(lower, upper), default:"lower") –Whether to mask values below or above threshold.
-
(alpha¶float, default:1.0) –Opacity of the image.
-
(show_colorbar¶bool, default:True) –Whether to add a colorbar.
-
(cbar_label¶str, default:None) –Label for the colorbar.
-
(show_titles¶bool, default:True) –Whether to display subplot titles.
-
(show_axis_labels¶bool, default:True) –Whether to display axis labels.
-
(show_axis_ticks¶bool, default:True) –Whether to display axis tick labels.
-
(show_axes¶bool, default:True) –Whether to show all axis decorations (spines, ticks, labels). When
False, overridesshow_axis_labelsandshow_axis_ticks. -
(nrows¶int, default:None) –Number of rows in the subplot grid when creating a new figure. If not provided, computed automatically.
-
(ncols¶int, default:None) –Number of columns in the subplot grid when creating a new figure. If not provided, computed automatically.
-
(dpi¶int, default:None) –Figure resolution in dots per inch. Ignored when using an existing figure.
Returns:
-
VolumePlotter–Returns self for method chaining.
Raises:
-
ValueError–If no matching coordinates are found or axis count doesn't match.
savefig ¶
Save the figure to a file.
Parameters:
-
(fname¶str) –Path to save the figure. Extension determines format (e.g.,
.png,.pdf). -
–**kwargs¶Additional arguments passed to
matplotlib.figure.Figure.savefig.
Raises:
-
RuntimeError–If called before any data has been plotted.
show ¶
draw_napari_labels ¶
draw_napari_labels(
data: DataArray,
labels_layer_name: str = "labels",
viewer: Viewer | None = None,
**kwargs,
) -> tuple[Viewer, Labels]
Open a napari viewer to interactively paint integer labels over fUSI data.
Displays the data as an image layer and adds an empty Labels layer on top. The user
can paint integer labels directly on the image using napari's brush tool. After
painting, call labels_from_layer with the
returned Labels layer and the original data to obtain an integer label map as a
DataArray with the same spatial coordinates.
Parameters:
-
(data¶DataArray) –Input data array to display as the background image. Typically a time-averaged power Doppler frame, e.g.
data.mean("time"). -
(labels_layer_name¶str, default:"labels") –Name assigned to the Labels layer added to the viewer.
-
(viewer¶Viewer, default:None) –Existing napari viewer to add layers to. If not provided, a new viewer is created via
plot_napari. -
–**kwargs¶Additional keyword arguments forwarded to
plot_naparifor the image layer (e.g.colormap,contrast_limits).
Returns:
-
viewer(Viewer) –The napari viewer instance with the image and Labels layers.
-
labels_layer(Labels) –The empty Labels layer initialised to zeros. After the user paints labels in the viewer, pass this layer to
labels_from_layerto obtain an integer label map.
Notes
The Labels layer is initialised with the same scale and translate
parameters as the image layer so that the napari canvas shows a consistent
physical coordinate frame regardless of voxel spacing or data origin.
Examples:
>>> import xarray as xr
>>> import confusius # Register accessor.
>>> pwd = xr.open_zarr("output.zarr")["power_doppler"].compute()
>>> # Display the time-averaged image and add an interactive Labels layer.
>>> viewer, labels_layer = draw_napari_labels(pwd.mean("time"))
>>> # … paint labels in the viewer …
>>> # Convert painted labels to an integer label map DataArray.
>>> label_map = labels_from_layer(labels_layer, pwd.mean("time"))
labels_from_layer ¶
labels_from_layer(
labels_layer: Labels, data: DataArray
) -> DataArray
Convert a napari Labels layer to an integer label map DataArray.
Reads the integer array painted in labels_layer and wraps it in a DataArray whose
spatial dimensions and coordinates match those of data. The result is compatible
with extract_with_labels,
plot_contours, and
VolumePlotter.add_contours.
Parameters:
-
(labels_layer¶Labels) –A Labels layer populated by the user (e.g. via
draw_napari_labels). Integer values identify distinct regions; zero is the background and is excluded from downstream analyses. -
(data¶DataArray) –Reference data array. Its spatial dimensions and coordinates define the shape and labelling of the output. A time dimension, if present, is ignored: the label map is purely spatial.
Returns:
-
DataArray–Stacked integer DataArray with dims
["mask", *spatial_dims], where themaskcoordinate holds each unique non-zero label integer. Each layermask=khas valueskwhere the user painted labelkand0elsewhere. This format is directly compatible withextract_with_labels,plot_contours, andVolumePlotter.add_contours, and can be sliced by label (e.g.label_map.sel(mask=2)) for per-label display. Theattrsdict carries:"long_name"— human-readable name."labels_layer_name"— name of the source napari layer."rgb_lookup"—dict[int, list[int]]mapping each non-zero label to its[r, g, b]color (0–255) as painted in napari.
Notes
The label array is taken directly from labels_layer.data. No
rasterisation is performed: this is a direct read of the painted values.
Per-label colors are read from labels_layer.get_color(label), which works for both
the default cyclic colormap and any DirectLabelColormap set on the layer.
Examples:
>>> import xarray as xr
>>> import confusius # Register accessor.
>>> pwd = xr.open_zarr("output.zarr")["power_doppler"].compute()
>>> viewer, labels_layer = draw_napari_labels(pwd.mean("time"))
>>> # … paint labels in the viewer …
>>> label_map = labels_from_layer(labels_layer, pwd.mean("time"))
>>> label_map.dims
('mask', 'z', 'y', 'x')
>>> # Slice a single label for display alongside a seed map.
>>> label_map.sel(mask=2)
>>> # Use the label map for region-based analysis.
>>> from confusius.extract import extract_with_labels
>>> signals = extract_with_labels(pwd, label_map)
plot_carpet ¶
plot_carpet(
data: DataArray,
mask: DataArray | None = None,
detrend_order: int | None = None,
standardize: bool = True,
cmap: str | Colormap = "gray",
vmin: float | None = None,
vmax: float | None = None,
decimation_threshold: int | None = 800,
figsize: tuple[float, float] = (10, 5),
title: str | None = None,
black_bg: bool = False,
ax: Axes | None = None,
) -> tuple[Figure | SubFigure, Axes]
Plot voxel intensities across time as a raster image.
A carpet plot (also known as "grayplot" or "Power plot") displays voxel intensities as a 2D raster image with time on the x-axis and voxels on the y-axis. Each row represents one voxel's time series, typically standardized to z-scores.
Parameters:
-
(data¶DataArray) –Input data array with a 'time' dimension.
-
(mask¶DataArray, default:None) –Boolean mask with same spatial dimensions and coordinates as
data. True values indicate voxels to include. If not provided, all non-zero voxels from the data are included. -
(detrend_order¶int, default:None) –Polynomial order for detrending:
0: Remove mean (constant detrending).1: Remove linear trend using least squares regression.2+: Remove polynomial trend of specified order.
If not provided, no detrending is applied.
-
(standardize¶bool, default:True) –Whether to standardize each voxel's time series to z-scores.
-
(cmap¶str, default:"gray") –Matplotlib colormap name.
-
(vmin¶float, default:None) –Minimum value for colormap. If not provided, uses
mean - 2*std. -
(vmax¶float, default:None) –Maximum value for colormap. If not provided, uses
mean + 2*std. -
(decimation_threshold¶int or None, default:800) –If the number of timepoints exceeds this value, data is downsampled along the time axis to improve plotting performance. Set to
Noneto disable downsampling. -
(figsize¶tuple[float, float], default:(10, 5)) –Figure size in inches
(width, height). -
(title¶str, default:None) –Plot title.
-
(black_bg¶bool, default:False) –Whether to use a black figure background with white foreground elements (spines, ticks, labels). Use
Truefor dark-themed figures. -
(ax¶Axes, default:None) –Axes to plot on. If not provided, creates new figure and axes.
Returns:
-
figure(Figure or SubFigure) –Figure object containing the carpet plot.
-
axes(Axes) –Axes object with the carpet plot.
Notes
Complex-valued data is converted to magnitude before processing.
This function was inspired by Nilearn's nilearn.plotting.plot_carpet.
References
-
Power, Jonathan D. “A Simple but Useful Way to Assess fMRI Scan Qualities.” NeuroImage, vol. 154, July 2017, pp. 150–58. DOI.org (Crossref), https://doi.org/10.1016/j.neuroimage.2016.08.009. ↩
Examples:
plot_contours ¶
plot_contours(
mask: DataArray,
*,
colors: dict[int | str, str] | str | None = None,
linewidths: float = 1.5,
linestyles: str = "solid",
slice_mode: str = "z",
slice_coords: list[float] | None = None,
yincrease: bool = False,
xincrease: bool = True,
black_bg: bool = True,
figure: Figure | None = None,
axes: NDArray[Any] | None = None,
**kwargs,
) -> VolumePlotter
Plot mask contours as a grid of 2D slice panels.
Displays contour lines for each labeled region in mask across a grid of subplots.
Each panel shows the contours for one slice along slice_mode, drawn in physical
coordinates when available.
Parameters:
-
(mask¶DataArray) –Integer label map in one of two formats:
- Flat label map: Spatial dims only, e.g.
(z, y, x). Background voxels labeled0; each unique non-zero integer identifies a distinct, non-overlapping region. Theregionscoordinate of the output holds the integer label values. - Stacked mask format: Has a leading
masksdimension followed by spatial dims, e.g.(masks, z, y, x). Each layer has values in{0, region_id}and regions may overlap. Theregionscoordinate of the output holds themaskscoordinate values (e.g., region label).
- Flat label map: Spatial dims only, e.g.
-
(colors¶dict[int | str, str] or str, default:None) –Color specification for contour lines. A
dictmaps each label (integer index or region acronym string) to a color; astrapplies one color to all regions. If not provided, colors are derived fromattrs["cmap"]andattrs["norm"]when present, otherwise from thetab10/tab20colormap. -
(linewidths¶float, default:1.5) –Width of contour lines in points.
-
(linestyles¶str, default:"solid") –Line style for contour lines (e.g.
"solid","dashed"). -
(slice_mode¶str, default:"z") –Dimension along which to slice (e.g.
"x","y","z"). After slicing, each panel must be 2D. -
(slice_coords¶list[float], default:None) –Coordinate values along
slice_modeat which to extract slices. Slices are selected by nearest-neighbour lookup. If not provided, all coordinate values alongslice_modeare used. -
(yincrease¶bool, default:False) –Whether the y-axis increases upward (
True) or downward (False). -
(xincrease¶bool, default:True) –Whether the x-axis increases to the right (
True) or left (False). -
(black_bg¶bool, default:True) –Whether to set the figure background to black.
-
(figure¶Figure, default:None) –Existing figure to draw into. If not provided, a new figure is created.
-
(axes¶ndarray, default:None) –Existing 2D array of
matplotlib.axes.Axesto draw into. If not provided, new axes are created insidefigure. -
–**kwargs¶Additional keyword arguments passed to
matplotlib.axes.Axes.plot.
Returns:
-
VolumePlotter–Object managing the figure, axes, and coordinate mapping for overlays.
Raises:
-
ValueError–If
slice_modeis not a dimension ofmask. -
ValueError–If
maskis not 3D.
Notes
Contours are computed with skimage.measure.find_contours on a binary mask for each
label, then mapped to physical coordinates via linear interpolation between
coordinate centers. Each panel has aspect="equal" so that 1 unit in x matches 1
unit in y.
The returned VolumePlotter stores the
coordinate-to-axis mapping, so you can overlay a volume afterwards with
VolumePlotter.add_volume.
Examples:
>>> import xarray as xr
>>> from confusius.plotting import plot_contours
>>> mask = xr.open_zarr("output.zarr")["roi_mask"]
>>> plotter = plot_contours(mask, slice_mode="z")
plot_napari ¶
plot_napari(
data: DataArray,
show_colorbar: bool = True,
show_scale_bar: bool = True,
dim_order: tuple[str, ...] | None = None,
viewer: Viewer | None = None,
layer_type: Literal["image", "labels"] = "image",
**layer_kwargs,
) -> tuple[Viewer, Image | Labels]
Display fUSI data using the napari viewer.
Parameters:
-
(data¶DataArray) –Input data array to visualize. Expected dimensions are (time, z, y, x) where z is the elevation/stacking axis, y is depth, and x is lateral. Use
dim_orderto specify a different dimension ordering. Can be image data or label/mask data (e.g., ROIs, segmentations). -
(show_colorbar¶bool, default:True) –Whether to show the colorbar. Only applies to image layers.
-
(show_scale_bar¶bool, default:True) –Whether to show the scale bar.
-
(dim_order¶tuple[str, ...], default:None) –Dimension ordering for the spatial axes (last three dimensions). If not provided, the ordering of the last three dimensions in
datais used. -
(viewer¶Viewer, default:None) –Existing napari viewer to add the layer to. If not provided, a new viewer is created.
-
(layer_type¶(image, labels), default:"image") –Type of layer to create. Use "image" for fUSI data and "labels" for ROI masks, segmentations, or other label data.
-
–**layer_kwargs¶Additional keyword arguments passed to the layer creation method (
napari.imshowfor images orviewer.add_labelsfor labels). For image layers, ifdata.attrscontains"cmap"and"colormap"is not inlayer_kwargs, the attribute is used as the colormap. For labels layers, ifdata.attrscontains"cmap"and"norm"(as set by atlas functions) and"colormap"is not inlayer_kwargs, a per-label color dict is built automatically from those attributes.
Returns:
-
viewer(Viewer) –The napari viewer instance with the layer added.
-
layer(Image or Labels) –The layer added to the viewer.
Notes
If all spatial dimensions have coordinates, their spacing is used as the scale parameter for napari to ensure correct physical scaling. If any spatial dimension is missing coordinates, no scaling is applied. The spacing is computed as the median difference between consecutive coordinate values.
When spatial coordinates carry a units attribute (e.g. "m"), the unit list
is forwarded to napari as the units layer parameter, which populates the status
bar with physical coordinates. The scale bar is also updated to reflect the first
found unit; it falls back to "mm" when no units are present on the coordinates.
For unitary dimensions (e.g., a single-slice elevation axis in 2D+t data), the
spacing cannot be inferred from coordinates. In that case, the function looks for a
voxdim attribute on the coordinate variable
(data.coords[dim].attrs["voxdim"]) and uses it as the spacing. If no such
attribute is found, unit spacing is assumed and a warning is emitted.
The first coordinate value of each spatial dimension is used as the translate
parameter so that the image is positioned at its correct physical origin. For
dimensions without coordinates, a translate of 0.0 is used. This ensures that
multiple datasets with different fields of view overlay correctly when added to the
same viewer.
Examples:
>>> import xarray as xr
>>> from confusius.plotting import plot_napari
>>> data = xr.open_zarr("output.zarr")["iq"]
>>> viewer, layer = plot_napari(data)
>>> # Different dimension ordering (e.g., depth, elevation, lateral)
>>> viewer, layer = plot_napari(data, dim_order=("y", "z", "x"))
>>> # Add a second dataset as a new layer in an existing viewer
>>> viewer, layer = plot_napari(data1)
>>> viewer, layer = plot_napari(data2, viewer=viewer)
plot_volume ¶
plot_volume(
data: DataArray,
*,
slice_coords: list[float] | None = None,
slice_mode: str = "z",
cmap: str | Colormap | None = None,
norm: Normalize | None = None,
vmin: float | None = None,
vmax: float | None = None,
threshold: float | None = None,
threshold_mode: Literal["lower", "upper"] = "lower",
alpha: float = 1.0,
show_colorbar: bool = True,
cbar_label: str | None = None,
show_titles: bool = True,
show_axis_labels: bool = True,
show_axis_ticks: bool = True,
show_axes: bool = True,
yincrease: bool = False,
xincrease: bool = True,
black_bg: bool = True,
figure: Figure | None = None,
axes: NDArray[Any] | None = None,
nrows: int | None = None,
ncols: int | None = None,
dpi: int | None = None,
) -> VolumePlotter
Plot 2D slices of a volume using matplotlib.
Displays a series of 2D slices extracted along slice_mode as a grid of subplots.
Each slice is rendered using physical coordinates for axis ticks when available.
Parameters:
-
(data¶DataArray) –Input data array. Unitary dimensions are squeezed before processing. After squeezing, data must be 3D. Complex-valued data is converted to magnitude before display.
-
(slice_coords¶list[float], default:None) –Coordinate values along
slice_modeat which to extract slices. Slices are selected by nearest-neighbour lookup. If not provided, all coordinate values alongslice_modeare used. -
(slice_mode¶str, default:"z") –Dimension along which to slice (e.g.,
"x","y","z","time"). After slicing, each panel must be 2D. -
(cmap¶str or Colormap, default:None) –Colormap. When not provided, falls back to
data.attrs["cmap"]if present, otherwise"gray". -
(norm¶Normalize, default:None) –Normalization instance (e.g.
BoundaryNormfor integer label maps such as atlas annotations). When not provided, falls back todata.attrs["norm"]if present. When a norm is active,vminandvmaxare ignored. -
(vmin¶float, default:None) –Lower bound of the colormap. Defaults to the 2nd percentile. Ignored when a norm is active.
-
(vmax¶float, default:None) –Upper bound of the colormap. Defaults to the 98th percentile. Ignored when a norm is active.
-
(threshold¶float, default:None) –Threshold applied to
|data|. Seethreshold_modefor the masking direction. If not provided, no thresholding is applied. -
(threshold_mode¶(lower, upper), default:"lower") –Controls how
thresholdis applied:"lower": set pixels where|data| < thresholdto NaN."upper": set pixels where|data| > thresholdto NaN.
-
(alpha¶float, default:1.0) –Opacity of the image.
-
(show_colorbar¶bool, default:True) –Whether to add a shared colorbar to the figure.
-
(cbar_label¶str, default:None) –Label for the colorbar.
-
(show_titles¶bool, default:True) –Whether to display subplot titles showing the slice coordinate.
-
(show_axis_labels¶bool, default:True) –Whether to display axis labels (with units when available).
-
(show_axis_ticks¶bool, default:True) –Whether to display axis tick labels.
-
(show_axes¶bool, default:True) –Whether to show all axis decorations (spines, ticks, labels). When
False, overridesshow_axis_labelsandshow_axis_ticks. -
(yincrease¶bool, default:False) –Whether the y-axis increases upward (
True) or downward (False). -
(xincrease¶bool, default:True) –Whether the x-axis increases to the right (
True) or left (False). -
(black_bg¶bool, default:True) –Whether to set the figure background to black.
-
(figure¶Figure, default:None) –Existing figure to draw into. If not provided, a new figure is created.
-
(axes¶ndarray, default:None) –Existing 2D array of
matplotlib.axes.Axesto draw into. Must contain at least as many elements as there are slices. If not provided, new axes are created insidefigure. -
(nrows¶int, default:None) –Number of rows in the subplot grid. If not provided, computed automatically.
-
(ncols¶int, default:None) –Number of columns in the subplot grid. If not provided, computed automatically.
-
(dpi¶int, default:None) –Figure resolution in dots per inch. Ignored when
figureis provided.
Returns:
-
VolumePlotter–Object managing the figure, axes, and coordinate mapping for overlays.
Raises:
-
ValueError–If
slice_modeis not a dimension ofdata. -
ValueError–If
datais not 3D after squeezing unitary dimensions. -
ValueError–If
axesis provided but does not contain enough elements for all slices.
Notes
Rendering is done with pcolormesh, which accepts
coordinate arrays directly and therefore handles non-uniform coordinate spacing
correctly. Because each panel is drawn in physical coordinate space, multiple calls
with different axes elements will overlay correctly as long as the displayed
dimensions are the same.
The two dimensions that remain after slicing define the panel axes: the
first remaining dimension maps to the vertical axis and the second to the
horizontal axis. Coordinates are used directly as axis tick values; each
axis has aspect="equal" so that 1 unit in x matches 1 unit in y.
NaN and Inf values (including those introduced by threshold) are rendered
transparently via a masked array.
When the figure is created internally, layout="constrained" is used so
that subplot titles, axis labels, tick labels, and the colorbar are spaced
automatically without overlapping. When an external figure or axes
is provided, layout management is left to the caller.
Examples:
>>> import xarray as xr
>>> from confusius.plotting import plot_volume
>>> data = xr.open_zarr("output.zarr")["power_doppler"]
>>> plotter = plot_volume(data, slice_mode="z")