@@ -243,13 +243,18 @@ def render_shapes(
243243 Width of the border. If 2 values are given (tuple), 2 borders are shown with these widths (outer & inner).
244244 If `outline_color` and/or `outline_alpha` are used to indicate that one/two outlines should be drawn, the
245245 default outline widths 1.5 and 0.5 are used for outer/only and inner outline respectively.
246- outline_color : ColorLike | tuple[ColorLike], optional
246+ outline_color : ColorLike | tuple[ColorLike] | str , optional
247247 Color of the border. Can either be a named color ("red"), a hex representation ("#000000") or a list of
248248 floats that represent RGB/RGBA values (1.0, 0.0, 0.0, 1.0). If the hex representation includes alpha, e.g.
249249 "#000000ff", and `outline_alpha` is not given, this value controls the opacity of the outline. If 2 values
250250 are given (tuple), 2 borders are shown with these colors (outer & inner). If `outline_width` and/or
251251 `outline_alpha` are used to indicate that one/two outlines should be drawn, the default outline colors
252252 "#000000" and "#ffffff are used for outer/only and inner outline respectively.
253+ A string that is not a recognized color is interpreted as a column key (in `obs` of the annotating table
254+ or in the element's own dataframe), mirroring how ``color`` is parsed. The outline is then colored
255+ per-shape using the same ``palette`` / ``cmap`` / ``na_color`` as the fill. When both ``color`` and
256+ ``outline_color`` resolve to columns, two stacked legends are drawn. Column-based outline coloring is
257+ only supported for a single outline (not the 2-tuple form).
253258 outline_alpha : float | int | tuple[float | int, float | int] | None, optional
254259 Alpha value for the outline of shapes. Invisible by default, meaning outline_alpha=0.0 if both outline_color
255260 and outline_width are not specified. Else, outlines are rendered with the alpha implied by outline_color, or
@@ -346,6 +351,8 @@ def render_shapes(
346351 element = element ,
347352 color = param_values ["color" ],
348353 col_for_color = param_values ["col_for_color" ],
354+ col_for_outline_color = param_values ["col_for_outline_color" ],
355+ outline_table_name = param_values ["outline_table_name" ],
349356 groups = param_values ["groups" ],
350357 scale = param_values ["scale" ],
351358 outline_params = outline_params ,
@@ -387,6 +394,8 @@ def render_points(
387394 colorbar : bool | str | None = "auto" ,
388395 colorbar_params : dict [str , object ] | None = None ,
389396 datashader_reduction : _DsReduction | None = None ,
397+ density : bool = False ,
398+ density_how : Literal ["linear" , "log" , "cbrt" , "eq_hist" ] = "linear" ,
390399 transfunc : Callable [[float ], float ] | None = None ,
391400 ) -> sd .SpatialData :
392401 """
@@ -457,13 +466,38 @@ def render_points(
457466 in another column of ``var``. Mimics scanpy's ``gene_symbols`` parameter.
458467 datashader_reduction : Literal["sum", "mean", "any", "count", "std", "var", "max", "min"] | None, optional
459468 Reduction method for datashader when coloring by continuous values. When ``None``, defaults to ``"sum"``.
469+ density : bool, default False
470+ Render the points as a 2-D count density via datashader instead of plotting individual markers.
471+ When ``True``, ``method`` is forced to ``"datashader"`` (passing ``method="matplotlib"`` raises).
472+ Density supports ``color=None`` (plain density) or a categorical ``color`` column (per-category
473+ density via :func:`datashader.by`). A continuous ``color`` column or a literal color value
474+ (e.g. ``"red"``) raises an error. Under ``density=True`` the following parameters are ignored
475+ (with a warning if explicitly set): ``size``, ``transfunc``, ``norm.vmin/vmax``, and
476+ ``datashader_reduction``.
477+ density_how : Literal["linear", "log", "cbrt", "eq_hist"], default "linear"
478+ How datashader maps aggregated counts to color intensity. ``"linear"`` (default) keeps the
479+ colorbar axis as a count; ``"log"`` and ``"cbrt"`` compress dynamic range; ``"eq_hist"``
480+ equalizes the histogram (rank-based, surfaces the most structure but the colorbar axis is
481+ no longer a count). Ignored when ``density=False``.
460482 transfunc : Callable[[float], float] | None, optional
461483 Optional transformation applied to the continuous color vector before normalization and colormap mapping.
462484
463485 Returns
464486 -------
465487 sd.SpatialData
466488 A copy of the SpatialData object with the rendering parameters stored in its plotting tree.
489+
490+ Examples
491+ --------
492+ Plain density of all transcripts:
493+
494+ >>> sdata.pl.render_points("transcripts", density=True).pl.show()
495+
496+ Per-gene density with a categorical palette:
497+
498+ >>> sdata.pl.render_points(
499+ ... "transcripts", color="gene", groups=["Gad1", "Slc17a7"], palette="tab20", density=True
500+ ... ).pl.show()
467501 """
468502 params_dict = _validate_points_render_params (
469503 self ._sdata ,
@@ -482,6 +516,10 @@ def render_points(
482516 colorbar = colorbar ,
483517 colorbar_params = colorbar_params ,
484518 gene_symbols = gene_symbols ,
519+ density = density ,
520+ density_how = density_how ,
521+ transfunc = transfunc ,
522+ method = method ,
485523 )
486524
487525 if method is not None :
@@ -490,6 +528,9 @@ def render_points(
490528 if method not in ["matplotlib" , "datashader" ]:
491529 raise ValueError ("Parameter 'method' must be either 'matplotlib' or 'datashader'." )
492530
531+ if density and method is None :
532+ method = "datashader"
533+
493534 sdata = self ._copy ()
494535 sdata = _verify_plotting_tree (sdata )
495536 n_steps = len (sdata .plotting_tree .keys ())
@@ -517,6 +558,8 @@ def render_points(
517558 ds_reduction = param_values ["ds_reduction" ],
518559 colorbar = param_values ["colorbar" ],
519560 colorbar_params = param_values ["colorbar_params" ],
561+ density = density ,
562+ density_how = density_how ,
520563 )
521564 n_steps += 1
522565
@@ -811,11 +854,14 @@ def render_labels(
811854 fill_alpha : float | int | None, optional
812855 Alpha value for the fill of the labels. By default, it is set to 0.4 or, if a color is given that implies
813856 an alpha, that value is used for `fill_alpha`.
814- outline_color : ColorLike | None
857+ outline_color : ColorLike | str | None
815858 Color of the outline of the labels. Can either be a named color ("red"), a hex representation
816859 ("#000000") or a list of floats that represent RGB/RGBA values (1.0, 0.0, 0.0, 1.0). If ``None``,
817860 the outline inherits from the ``color`` parameter when it is a literal color, or uses data-driven
818861 per-label colors when ``color`` refers to a column.
862+ A string that is not a recognized color is interpreted as a column key (in `obs` of the annotating
863+ table), mirroring how ``color`` is parsed. The outline is then colored per-label using the same
864+ ``palette`` / ``cmap`` / ``na_color`` as the fill.
819865 scale : str | None
820866 Influences the resolution of the rendering. Possibilities for setting this parameter:
821867 1) None (default). The image is rasterized to fit the canvas size. For multiscale images, the best scale
@@ -881,6 +927,8 @@ def render_labels(
881927 element = element ,
882928 color = param_values ["color" ],
883929 col_for_color = param_values ["col_for_color" ],
930+ col_for_outline_color = param_values ["col_for_outline_color" ],
931+ outline_table_name = param_values ["outline_table_name" ],
884932 groups = param_values ["groups" ],
885933 contour_px = param_values ["contour_px" ],
886934 cmap_params = cmap_params ,
@@ -1047,6 +1095,8 @@ def show(
10471095 na_in_legend : bool = True ,
10481096 colorbar : bool = True ,
10491097 colorbar_params : dict [str , object ] | None = None ,
1098+ legend_title : str | None = None ,
1099+ outline_legend_title : str | None = None ,
10501100 wspace : float | None = None ,
10511101 hspace : float = 0.25 ,
10521102 ncols : int = 4 ,
@@ -1091,6 +1141,12 @@ def show(
10911141 colorbar_params : dict[str, object] | None
10921142 Global overrides passed to colorbars for all axes. Accepts the same keys as per-layer ``colorbar_params``
10931143 (e.g., ``loc``, ``width``, ``pad``, ``label``).
1144+ legend_title : str | None
1145+ Title for the fill categorical legend. When both fill and outline are colored by an obs column, the
1146+ two legends default to ``"fill"`` / ``"outline"`` to disambiguate; pass an explicit string to override
1147+ the fill title. Set to ``None`` (default) to keep the auto-title behavior.
1148+ outline_legend_title : str | None
1149+ Title for the outline categorical legend. Mirrors ``legend_title`` for the outline channel.
10941150 wspace : float | None
10951151 Horizontal spacing between panels (passed to :class:`matplotlib.gridspec.GridSpec`).
10961152 hspace : float, default 0.25
@@ -1327,6 +1383,8 @@ def show(
13271383 legend_fontoutline = legend_fontoutline ,
13281384 na_in_legend = na_in_legend ,
13291385 colorbar = colorbar ,
1386+ legend_title = legend_title ,
1387+ outline_legend_title = outline_legend_title ,
13301388 )
13311389
13321390 def _draw_colorbar (
0 commit comments