Skip to content

Commit 76440e0

Browse files
committed
refactor(as_points): dedup fast-extent idiom, fix stale 50k docs
- extract _fast_extent(element, cs) helper; the '_element_extent_fast(...) or get_extent(...)' idiom was duplicated verbatim at the shapes as_points site and the datashader-canvas helper - fix docstrings that still said '~500k' after the threshold dropped to 50k - trim _render_centroids_as_points docstring and a non-actionable comment No behavior change: _fast_extent is identical to the inline form (verified == get_extent on blobs), render_points byte-identical.
1 parent 38086e2 commit 76440e0

4 files changed

Lines changed: 15 additions & 18 deletions

File tree

src/spatialdata_plot/pl/basic.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1051,7 +1051,7 @@ def render_labels(
10511051
Optional transformation applied to the continuous color vector before normalization and colormap mapping.
10521052
method : str | None, optional
10531053
Backend for ``as_points`` centroids: ``'matplotlib'`` or ``'datashader'``. When ``None``,
1054-
matplotlib is used unless there are more than ~500k centroids. Datashader is skipped (with a
1054+
matplotlib is used unless there are more than ~50k centroids. Datashader is skipped (with a
10551055
warning) when the colouring cannot be aggregated (e.g. labels with no color column).
10561056
10571057
Returns

src/spatialdata_plot/pl/render.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
_convert_shapes,
6767
_datashader_canvas_from_dataframe,
6868
_decorate_axs,
69-
_element_extent_fast,
69+
_fast_extent,
7070
_get_collection_shape,
7171
_get_colors_for_categorical_obs,
7272
_get_extent_and_range_for_datashader_canvas,
@@ -775,10 +775,7 @@ def _render_shapes(
775775
fig_params=fig_params,
776776
legend_params=legend_params,
777777
colorbar_requests=colorbar_requests,
778-
# fast corner-transform extent (avoids transforming every geometry); identical for
779-
# axis-aligned transforms, falls back to get_extent for rotation/shear
780-
axes_extent=_element_extent_fast(sdata_filt.shapes[element], coordinate_system)
781-
or get_extent(sdata_filt.shapes[element], coordinate_system=coordinate_system),
778+
axes_extent=_fast_extent(sdata_filt.shapes[element], coordinate_system),
782779
)
783780
return
784781

@@ -1164,11 +1161,9 @@ def _render_centroids_as_points(
11641161
) -> None:
11651162
"""Render one dot per cell at ``(x, y)`` (coordinate-system coords), colored like the fill.
11661163
1167-
Shared "fast mode" for shapes/labels. Backend is matplotlib unless ``render_params.method`` or the
1168-
size threshold selects datashader (and the colouring supports it). ``axes_extent`` is the element's
1169-
extent in the coordinate system (the frame the axes will use), which the datashader backend rasterizes
1170-
over so its dots match the matplotlib markers. ``norm``/``na_color`` are explicit because they differ
1171-
between the shapes and labels paths.
1164+
Shared "fast mode" for shapes/labels; backend chosen by ``_resolve_as_points_method``. ``axes_extent``
1165+
(the element's extent, i.e. the frame the axes will use) is what the datashader backend rasterizes over
1166+
so its dots match the matplotlib markers.
11721167
"""
11731168
method = _resolve_as_points_method(render_params, n=len(x), allow_datashader=allow_datashader)
11741169
if method == "datashader":

src/spatialdata_plot/pl/render_params.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ class LabelsRenderParams:
334334
# Fast mode: render each label as a single dot at its centroid instead of the mask.
335335
as_points: bool = False
336336
size: float = 1.0 # marker size for as_points (matplotlib scatter ``s``)
337-
# Backend for the as_points centroids: None auto-selects (datashader above ~500k dots).
337+
# Backend for the as_points centroids: None auto-selects (datashader above ~50k dots).
338338
method: str | None = None
339339
# Multi-panel color: when set, this render entry belongs to the panel identified by this
340340
# color key. ``None`` means the entry is shared across every panel (e.g. a background layer).

src/spatialdata_plot/pl/utils.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3976,11 +3976,7 @@ def _get_extent_and_range_for_datashader_canvas(
39763976
coordinate_system: str,
39773977
fig_params: FigParams,
39783978
) -> tuple[Any, Any, list[Any], list[Any], Any]:
3979-
# The corner-transform fast path avoids transforming every geometry just to size the canvas;
3980-
# it is identical for axis-aligned transforms and returns None (-> exact get_extent) otherwise.
3981-
extent = _element_extent_fast(spatial_element, coordinate_system) or get_extent(
3982-
spatial_element, coordinate_system=coordinate_system
3983-
)
3979+
extent = _fast_extent(spatial_element, coordinate_system)
39843980
x_ext = [float(extent["x"][0]), float(extent["x"][1])]
39853981
y_ext = [float(extent["y"][0]), float(extent["y"][1])]
39863982
return _compute_datashader_canvas_params(x_ext, y_ext, fig_params)
@@ -4803,7 +4799,7 @@ def measure_obs(
48034799
# spatialdata's `get_extent(..., exact=True)` transforms every shapes/points geometry (O(N)) just to
48044800
# take a bounding box. For an axis-aligned transform (scale/flip/90deg-rotation/axis-swap + translation)
48054801
# the exact extent equals the bbox of the *transformed corners*, so we transform 4 corners instead;
4806-
# rotation/shear and other element types fall back to `get_extent`. Self-contained for upstreaming.
4802+
# rotation/shear and other element types fall back to `get_extent`.
48074803

48084804

48094805
def _is_axis_aligned(linear2x2: ArrayLike, *, rtol: float = 1e-9) -> bool:
@@ -4864,6 +4860,12 @@ def _element_extent_fast(
48644860
return {"x": (float(tc[:, 0].min()), float(tc[:, 0].max())), "y": (float(tc[:, 1].min()), float(tc[:, 1].max()))}
48654861

48664862

4863+
def _fast_extent(element: Any, coordinate_system: str) -> dict[str, tuple[float, float]]:
4864+
"""Element extent via the fast corner-transform; identical to ``get_extent`` but avoids transforming
4865+
every geometry for axis-aligned transforms (falls back to ``get_extent`` for rotation/shear)."""
4866+
return _element_extent_fast(element, coordinate_system) or get_extent(element, coordinate_system=coordinate_system)
4867+
4868+
48674869
def _get_extent_fast(
48684870
sdata: SpatialData,
48694871
coordinate_system: str,

0 commit comments

Comments
 (0)