Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
27ba8d2
Centroid extraction + squidpy obsm["spatial"] caching; shared scatter…
timtreis Jun 8, 2026
3bad4c0
Simplify centroid cache: dedup region mask/keys, fix half-write, trim…
timtreis Jun 8, 2026
23da9bc
Store centroids intrinsic (coordinate-system-independent cache)
timtreis Jun 8, 2026
40d8cef
Simplify centroids: numpy-affine transform on cache hits, drop privat…
timtreis Jun 8, 2026
fc3af02
Add as_points fast mode: render shapes/labels as centroid dots
timtreis Jun 8, 2026
169d897
Fix as_points crash for labels without a color column
timtreis Jun 8, 2026
4678297
Compute label centroids with a streaming, out-of-core bincount aggreg…
timtreis Jun 8, 2026
13b1df3
Merge main; refactor as_points to reuse measure_obs primitives
timtreis Jun 9, 2026
f8918fb
Simplify _render_centroids_as_points: take render_params, not 6 unpac…
timtreis Jun 9, 2026
79892ee
Tidy labels as_points color alignment
timtreis Jun 9, 2026
85692e9
Trim verbose as_points comments to the load-bearing invariants
timtreis Jun 9, 2026
b087c1c
Make labels as_points fast: centroids on the rendered raster, not scale0
timtreis Jun 9, 2026
57bcf8d
Fast axis-bounds in show(): skip per-geometry transform when axis-ali…
timtreis Jun 9, 2026
ae5fcfb
refactor: tidy _get_extent_fast (fold helper, total_bounds, batched c…
timtreis Jun 10, 2026
4b9891c
perf(datashader): use _element_extent_fast for the shapes canvas extent
timtreis Jun 10, 2026
ee2513e
fix(labels): correct as_points no-color rendering + validate size (co…
timtreis Jun 10, 2026
158b108
refactor(labels): dedup as_points size validation, lazy color conversion
timtreis Jun 10, 2026
df14a19
Merge branch 'main' of https://github.com/scverse/spatialdata-plot in…
timtreis Jun 10, 2026
ae2f124
perf(extent): reuse fetched transformations in _element_extent_fast
timtreis Jun 10, 2026
6b78f38
fix(extent): defer all-empty elements to get_extent; trim comments
timtreis Jun 10, 2026
ddb4cc5
test(as_points): add visual tests for shapes/labels centroid mode + size
timtreis Jun 10, 2026
1e88495
test(as_points): add CI-generated baselines for the 4 as_points visua…
timtreis Jun 10, 2026
97c5624
perf(shapes): vectorize the all-empty-geometry guard in show()
timtreis Jun 10, 2026
4c0c478
refactor(points): extract _datashader_points from _render_points
timtreis Jun 11, 2026
5854ae6
feat(as_points): datashader backend + fix shapes centroid coordinates
timtreis Jun 11, 2026
77ac531
test(as_points): datashader backend + shapes non-identity regression
timtreis Jun 11, 2026
b18bb18
test(as_points): CI baselines for datashader as_points visual tests
timtreis Jun 11, 2026
47ae7bc
refactor(as_points): trim comments, drop redundant asarray, fix empty…
timtreis Jun 11, 2026
c50e610
test(as_points): visual test for categorical datashader as_points
timtreis Jun 11, 2026
575cf30
test(as_points): CI baseline for categorical datashader as_points
timtreis Jun 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions src/spatialdata_plot/pl/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,15 @@
_expand_color_panels,
_get_cs_contents,
_get_elements_to_be_rendered,
_get_extent_fast,
_get_valid_cs,
_get_wanted_render_elements,
_maybe_set_colors,
_mpl_ax_contains_elements,
_prepare_cmap_norm,
_prepare_params_plot,
_set_outline,
_validate_as_points_size,
_validate_graph_render_params,
_validate_image_render_params,
_validate_label_render_params,
Expand Down Expand Up @@ -332,6 +334,8 @@ def render_shapes(
colorbar_params: dict[str, object] | None = None,
datashader_reduction: _DsReduction | None = None,
transfunc: Callable[[float], float] | None = None,
as_points: bool = False,
size: float | int = 1.0,
) -> sd.SpatialData:
"""
Render shapes elements in SpatialData.
Expand Down Expand Up @@ -448,6 +452,8 @@ def render_shapes(
sd.SpatialData
A copy of the SpatialData object with the rendering parameters stored in its plotting tree.
"""
if as_points:
_validate_as_points_size(size)
panel_param_dicts = _expand_color_panels(
self._sdata,
color,
Expand Down Expand Up @@ -515,6 +521,8 @@ def render_shapes(
ds_reduction=param_values["ds_reduction"],
colorbar=param_values["colorbar"],
colorbar_params=param_values["colorbar_params"],
as_points=as_points,
size=size,
panel_key=panel_key,
)
n_steps += 1
Expand Down Expand Up @@ -953,6 +961,9 @@ def render_labels(
table_layer: str | None = None,
gene_symbols: str | None = None,
transfunc: Callable[[float], float] | None = None,
as_points: bool = False,
size: float | int = 1.0,
method: str | None = None,
) -> sd.SpatialData:
"""
Render labels elements in SpatialData.
Expand Down Expand Up @@ -1038,12 +1049,18 @@ def render_labels(
in another column of ``var``. Mimics scanpy's ``gene_symbols`` parameter.
transfunc : Callable[[float], float] | None, optional
Optional transformation applied to the continuous color vector before normalization and colormap mapping.
method : str | None, optional
Backend for ``as_points`` centroids: ``'matplotlib'`` or ``'datashader'``. When ``None``,
matplotlib is used unless there are more than ~500k centroids. Datashader is skipped (with a
warning) when the colouring cannot be aggregated (e.g. labels with no color column).

Returns
-------
sd.SpatialData
A copy of the SpatialData object with the rendering parameters stored in its plotting tree.
"""
if as_points:
_validate_as_points_size(size)
panel_param_dicts = _expand_color_panels(
self._sdata,
color,
Expand Down Expand Up @@ -1101,6 +1118,9 @@ def render_labels(
zorder=n_steps,
colorbar=param_values["colorbar"],
colorbar_params=param_values["colorbar_params"],
as_points=as_points,
size=size,
method=method,
panel_key=panel_key,
)
n_steps += 1
Expand Down Expand Up @@ -1812,15 +1832,16 @@ def _draw_colorbar(
empty_shape_elements = [
name
for name in wanted_elements
if name in sdata.shapes and not sdata.shapes[name]["geometry"].apply(lambda g: not g.is_empty).any()
if name in sdata.shapes and sdata.shapes[name]["geometry"].is_empty.all()
]
if empty_shape_elements:
raise ValueError(
f"Cannot render shape element(s) {empty_shape_elements} in coordinate system {cs!r}: "
"all geometries are empty. Drop the element or restore at least one non-empty geometry."
)

extent = get_extent(
# fast path for axis-aligned transforms; identical result, falls back to get_extent otherwise
extent = _get_extent_fast(
sdata,
coordinate_system=cs,
has_images=has_images and wants_images,
Expand Down
Loading
Loading