Skip to content

Commit e0098dd

Browse files
authored
refactor: collapse duplicated orchestration across the _render_* quartet (#717)
1 parent 4ba13a3 commit e0098dd

2 files changed

Lines changed: 171 additions & 166 deletions

File tree

src/spatialdata_plot/pl/_datashader.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,90 @@ def _ds_shade_categorical(
321321
return _apply_user_alpha(shaded, alpha)
322322

323323

324+
def _shade_datashader_aggregate(
325+
cvs: ds.Canvas,
326+
frame: Any,
327+
*,
328+
col_for_color: str | None,
329+
color_vector: Any,
330+
color_by_categorical: bool,
331+
norm: Normalize | None,
332+
cmap: Any,
333+
na_color: Color,
334+
alpha: float,
335+
ds_reduction: _DsReduction | None,
336+
default_reduction: _DsReduction,
337+
kind: Literal["shapes", "points"],
338+
spread_px: int | None = None,
339+
shade_how: str = "linear",
340+
density: bool = False,
341+
uniform_alpha: bool = False,
342+
strip_alpha_hex: bool = False,
343+
) -> tuple[Any, Any | None, tuple[Any, Any] | None, Any]:
344+
"""Aggregate ``frame`` over ``cvs`` and shade it; the core shared by shapes and points.
345+
346+
Runs the steps every datashader caller repeats: ``_ds_aggregate`` -> ``_apply_ds_norm`` ->
347+
transparent-NA guard -> ``_build_color_key`` -> categorical/continuous shade dispatch. Returns
348+
``(shaded, nan_shaded, reduction_bounds, color_vector)``; ``color_vector`` is returned because the
349+
points path strips hex alpha off it and the caller's legend must match. Callers keep their
350+
element-specific prep (geometry transform / point parse), outline rendering, ``_render_ds_image``
351+
and ``_build_ds_colorbar``.
352+
"""
353+
agg, reduction_bounds, nan_agg = _ds_aggregate(
354+
cvs, frame, col_for_color, color_by_categorical, ds_reduction, default_reduction, kind
355+
)
356+
agg, color_span = _apply_ds_norm(agg, norm)
357+
na_color_hex = _hex_no_alpha(na_color.get_hex())
358+
if na_color.is_fully_transparent():
359+
nan_agg = None
360+
color_key = _build_color_key(frame, col_for_color, color_by_categorical, color_vector, na_color_hex)
361+
362+
if (
363+
strip_alpha_hex
364+
and color_vector is not None
365+
and len(color_vector) > 0
366+
and isinstance(color_vector[0], str)
367+
and color_vector[0].startswith("#")
368+
):
369+
# color_vector usually holds only a few distinct hex strings (one per category), so strip
370+
# alpha on the unique values and map back rather than parsing once per point.
371+
unique_hex, inverse = np.unique(color_vector, return_inverse=True)
372+
color_vector = np.asarray([_hex_no_alpha(c) for c in unique_hex])[inverse]
373+
374+
# density without a color column collapses to a sequential count gradient; everything else with no
375+
# explicit continuous value (categorical or no color) goes through the categorical shader.
376+
plain_density = density and col_for_color is None
377+
nan_shaded = None
378+
if not plain_density and (color_by_categorical or col_for_color is None):
379+
shaded = _ds_shade_categorical(
380+
agg,
381+
color_key,
382+
color_vector,
383+
alpha,
384+
spread_px=spread_px,
385+
how=shade_how,
386+
density=density,
387+
uniform_alpha=uniform_alpha,
388+
)
389+
else:
390+
shaded, nan_shaded, reduction_bounds = _ds_shade_continuous(
391+
agg,
392+
color_span,
393+
norm,
394+
cmap,
395+
alpha,
396+
reduction_bounds,
397+
nan_agg,
398+
na_color_hex,
399+
spread_px=spread_px,
400+
ds_reduction=ds_reduction,
401+
how=shade_how,
402+
uniform_alpha=uniform_alpha,
403+
)
404+
405+
return shaded, nan_shaded, reduction_bounds, color_vector
406+
407+
324408
# ---------------------------------------------------------------------------
325409
# Image rendering
326410
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)