diff --git a/src/spatialdata_plot/pl/_palette.py b/src/spatialdata_plot/pl/_palette.py index 9f700181..05e2617b 100644 --- a/src/spatialdata_plot/pl/_palette.py +++ b/src/spatialdata_plot/pl/_palette.py @@ -594,8 +594,9 @@ def make_palette_from_data( element Name of a shapes or points element in *sdata*. color - Column name containing categorical labels (in the element itself - for points, or in the linked table for shapes/labels). + Column name containing categorical labels. The column is first + looked up directly on the element (both for shapes and points); + if not found there, it falls back to the linked AnnData table. palette Source colours. Accepts the same values as :func:`make_palette` (*None*, a list, a named palette, or a diff --git a/src/spatialdata_plot/pl/basic.py b/src/spatialdata_plot/pl/basic.py index 0a99f395..be6cd741 100644 --- a/src/spatialdata_plot/pl/basic.py +++ b/src/spatialdata_plot/pl/basic.py @@ -220,10 +220,10 @@ def render_shapes( them. By default, non-matching elements are hidden. To show non-matching elements, set ``na_color`` explicitly. If element is None, broadcasting behaviour is attempted (use the same values for all elements). - palette : list[str] | str | None - Palette for discrete annotations. List of valid color names that should be used for the categories. Must - match the number of groups. If element is None, broadcasting behaviour is attempted (use the same values for - all elements). If groups is provided but not palette, palette is set to default "lightgray". + palette : dict[str, str] | list[str] | str | None + Palette for discrete annotations. Can be a dictionary mapping category names to colors, a list of valid + color names (must match the number of groups), a single named palette or matplotlib colormap name, or + ``None``. If element is None, broadcasting behaviour is attempted (use the same values for all elements). na_color : ColorLike | None, default "default" (gets set to "lightgray") Color to be used for NA values, if present. Can either be a named color ("red"), a hex representation ("#000000ff") or a list of floats that represent RGB/RGBA values (1.0, 0.0, 0.0, 1.0). When None, the values @@ -245,17 +245,18 @@ def render_shapes( with outline_alpha=1.0 if outline_color does not imply an alpha. For two outlines, alpha values can be passed in a tuple of length 2. cmap : Colormap | str | None, optional - Colormap for discrete or continuous annotations using 'color', see :class:`matplotlib.colors.Colormap`. - norm : bool | Normalize, default False - Colormap normalization for continuous annotations. + Colormap for continuous annotations using 'color', see :class:`matplotlib.colors.Colormap`. + For categorical data, use ``palette`` instead. + norm : Normalize | None, optional + Colormap normalization for continuous annotations, see :class:`matplotlib.colors.Normalize`. scale : float | int, default 1.0 - Value to scale circles, if present. + Value to scale shapes (circles and polygons). method : str | None, optional - Whether to use 'matplotlib' and 'datashader'. When None, the method is - chosen based on the size of the data. - colorbar : - Whether to request a colorbar for continuous colors. Use "auto" (default) for automatic selection. - colorbar_params : + Whether to use ``'matplotlib'`` or ``'datashader'``. When ``None``, the method is + chosen automatically based on the size of the data (datashader for >10 000 elements). + colorbar : bool | str | None, default "auto" + Whether to request a colorbar for continuous colors. Use ``"auto"`` (default) for automatic selection. + colorbar_params : dict[str, object] | None Parameters forwarded to Matplotlib's colorbar alongside layout hints such as ``loc``, ``width``, ``pad``, and ``label``. table_name: str | None @@ -288,14 +289,13 @@ def render_shapes( ----- - Empty geometries will be removed at the time of plotting. - An `outline_width` of 0.0 leads to no border being plotted. - - When passing a color-like to 'color', this has precendence over the potential existence as a column name. + - When passing a color-like to 'color', this has precedence over the potential existence as a column name. Returns ------- sd.SpatialData - The modified SpatialData object with the rendered shapes. + A copy of the SpatialData object with the rendering parameters stored in its plotting tree. """ - # TODO add Normalize object in tutorial notebook and point to that notebook here if "vmin" in kwargs or "vmax" in kwargs: logger.warning("`vmin` and `vmax` are deprecated. Pass matplotlib `Normalize` object to norm instead.") params_dict = _validate_shape_render_params( @@ -396,10 +396,10 @@ def render_points( element : str | None, optional The name of the points element to render. If `None`, all points elements in the `SpatialData` object will be used. - color : str | None, optional + color : ColorLike | None, optional Can either be color-like (name of a color as string, e.g. "red", hex representation, e.g. "#000000" or "#000000ff", or an RGB(A) array as a tuple or list containing 3-4 floats within [0, 1]. If an alpha value is - indicated, the value of `fill_alpha` takes precedence if given) or a string representing a key in + indicated, the value of ``alpha`` takes precedence if given) or a string representing a key in :attr:`sdata.table.obs`. The latter can be used to color by categorical or continuous variables. If `element` is `None`, if possible the color will be broadcasted to all elements. For this, the table in which the color key is found must annotate the respective element (region must be set to the specific element). If @@ -412,27 +412,28 @@ def render_points( them. By default, non-matching points are filtered out entirely. To show non-matching points, set ``na_color`` explicitly. If element is None, broadcasting behaviour is attempted (use the same values for all elements). - palette : list[str] | str | None - Palette for discrete annotations. List of valid color names that should be used for the categories. Must - match the number of groups. If `element` is `None`, broadcasting behaviour is attempted (use the same values - for all elements). If groups is provided but not palette, palette is set to default "lightgray". + palette : dict[str, str] | list[str] | str | None + Palette for discrete annotations. Can be a dictionary mapping category names to colors, a list of valid + color names (must match the number of groups), a single named palette or matplotlib colormap name, or + ``None``. If `element` is `None`, broadcasting behaviour is attempted (use the same values for all + elements). na_color : ColorLike | None, default "default" (gets set to "lightgray") Color to be used for NA values, if present. Can either be a named color ("red"), a hex representation ("#000000ff") or a list of floats that represent RGB/RGBA values (1.0, 0.0, 0.0, 1.0). When None, the values won't be shown. cmap : Colormap | str | None, optional - Colormap for discrete or continuous annotations using 'color', see :class:`matplotlib.colors.Colormap`. If + Colormap for continuous annotations using 'color', see :class:`matplotlib.colors.Colormap`. If no palette is given and `color` refers to a categorical, the colors are sampled from this colormap. - norm : bool | Normalize, default False - Colormap normalization for continuous annotations. + norm : Normalize | None, optional + Colormap normalization for continuous annotations, see :class:`matplotlib.colors.Normalize`. size : float | int, default 1.0 - Size of the points + Size of the points. method : str | None, optional - Whether to use 'matplotlib' and 'datashader'. When None, the method is - chosen based on the size of the data. - colorbar : - Whether to request a colorbar for continuous colors. Use "auto" (default) for automatic selection. - colorbar_params : + Whether to use ``'matplotlib'`` or ``'datashader'``. When ``None``, the method is + chosen automatically based on the size of the data (datashader for >10 000 elements). + colorbar : bool | str | None, default "auto" + Whether to request a colorbar for continuous colors. Use ``"auto"`` (default) for automatic selection. + colorbar_params : dict[str, object] | None Parameters forwarded to Matplotlib's colorbar alongside layout hints such as ``loc``, ``width``, ``pad``, and ``label``. table_name: str | None @@ -458,9 +459,8 @@ def render_points( Returns ------- sd.SpatialData - The modified SpatialData object with the rendered shapes. + A copy of the SpatialData object with the rendering parameters stored in its plotting tree. """ - # TODO add Normalize object in tutorial notebook and point to that notebook here if "vmin" in kwargs or "vmax" in kwargs: logger.warning("`vmin` and `vmax` are deprecated. Pass matplotlib `Normalize` object to norm instead.") params_dict = _validate_points_render_params( @@ -528,7 +528,6 @@ def render_images( channel: list[str] | list[int] | str | int | None = None, cmap: list[Colormap | str] | Colormap | str | None = None, norm: list[Normalize] | Normalize | None = None, - na_color: ColorLike | None = "default", palette: list[str] | str | None = None, alpha: float | int = 1.0, scale: str | None = None, @@ -536,7 +535,6 @@ def render_images( transfunc: Callable[[np.ndarray], np.ndarray] | list[Callable[[np.ndarray], np.ndarray]] | None = None, colorbar: bool | str | None = "auto", colorbar_params: dict[str, object] | None = None, - **kwargs: Any, ) -> sd.SpatialData: """ Render image elements in SpatialData. @@ -563,12 +561,9 @@ def render_images( A single :class:`~matplotlib.colors.Normalize` applies to all channels. A list of :class:`~matplotlib.colors.Normalize` objects applies per-channel (length must match the number of channels). - na_color : ColorLike | None, default "default" (gets set to "lightgray") - Color to be used for NAs values, if present. Can either be a named color ("red"), a hex representation - ("#000000ff") or a list of floats that represent RGB/RGBA values (1.0, 0.0, 0.0, 1.0). When None, the values - won't be shown. palette : list[str] | str | None - Palette to color images. The number of palettes should be equal to the number of channels. + Palette to color images. Can be a single palette name (broadcast to all channels) or a list + matching the number of channels. alpha : float | int, default 1.0 Alpha value for the images. Must be a numeric between 0 and 1. scale : str | None @@ -608,31 +603,35 @@ def render_images( When combined with ``grayscale=True``, ``transfunc`` runs first and ``grayscale`` is applied to the result. - colorbar : - Whether to request a colorbar for continuous colors. Use "auto" (default) for automatic selection. - colorbar_params : + colorbar : bool | str | None, default "auto" + Whether to request a colorbar for continuous colors. Use ``"auto"`` (default) for automatic selection. + colorbar_params : dict[str, object] | None Parameters forwarded to Matplotlib's colorbar alongside layout hints such as ``loc``, ``width``, ``pad``, and ``label``. - kwargs - Additional arguments to be passed to cmap, norm, and other rendering functions. + + Notes + ----- + - **RGB(A) auto-detection**: when the channel names are exactly ``{r, g, b}`` or ``{r, g, b, a}`` + (case-insensitive) and no explicit ``cmap`` or ``palette`` is given, the image is rendered as + true-color RGB(A) without colormaps. + - **Multi-channel compositing**: when multiple channels are rendered with per-channel colormaps, + they are additively blended. Colormaps that go from a color to white (rather than to transparent) + will cause upper layers to occlude lower ones. + - A single ``cmap`` is automatically broadcast to all selected channels. Returns ------- sd.SpatialData - The SpatialData object with the rendered images. + A copy of the SpatialData object with the rendering parameters stored in its plotting tree. """ - # TODO add Normalize object in tutorial notebook and point to that notebook here if grayscale and palette is not None: raise ValueError("Cannot combine grayscale=True with palette.") - if "vmin" in kwargs or "vmax" in kwargs: - logger.warning("`vmin` and `vmax` are deprecated. Pass matplotlib `Normalize` object to norm instead.") params_dict = _validate_image_render_params( self._sdata, element=element, channel=channel, alpha=alpha, palette=palette, - na_color=na_color, cmap=cmap, norm=norm, scale=scale, @@ -668,7 +667,6 @@ def render_images( _prepare_cmap_norm( cmap=c, norm=n, - na_color=param_values["na_color"], ) for c, n in zip(effective_cmap, norms, strict=True) ] @@ -679,8 +677,6 @@ def render_images( cmap_params = _prepare_cmap_norm( cmap=scalar_cmap, norm=norm_scalar, - na_color=param_values["na_color"], - **kwargs, ) sdata.plotting_tree[f"{n_steps + 1}_render_images"] = ImageRenderParams( element=element, @@ -728,7 +724,7 @@ def render_labels( In case of no elements specified, "broadcasting" of parameters is applied. This means that for any particular SpatialElement, we validate whether a given parameter is valid. If not valid for a particular SpatialElement the specific parameter for that particular SpatialElement will be ignored. If you want to set specific parameters - for specific elements please chain the render functions: `pl.render_images(...).pl.render_images(...).pl.show()` + for specific elements please chain the render functions: `pl.render_labels(...).pl.render_labels(...).pl.show()` . Parameters @@ -746,16 +742,17 @@ def render_labels( groups : list[str] | str | None When using `color` and the key represents discrete labels, `groups` can be used to show only a subset of them. By default, non-matching labels are hidden. To show non-matching labels, set ``na_color`` explicitly. - palette : list[str] | str | None - Palette for discrete annotations. List of valid color names that should be used for the categories. Must - match the number of groups. The list can contain multiple palettes (one per group) to be visualized. If - groups is provided but not palette, palette is set to default "lightgray". + palette : dict[str, str] | list[str] | str | None + Palette for discrete annotations. Can be a dictionary mapping category names to colors, a list of valid + color names (must match the number of groups), a single named palette or matplotlib colormap name, or + ``None``. contour_px : int, default 3 Draw contour of specified width for each segment. If `None`, fills entire segment, see: func:`skimage.morphology.erosion`. - cmap : Colormap | str | None - Colormap for continuous annotations, see :class:`matplotlib.colors.Colormap`. - norm : Normalize | None + cmap : Colormap | str | None, optional + Colormap for continuous annotations using 'color', see :class:`matplotlib.colors.Colormap`. + For categorical data, use ``palette`` instead. + norm : Normalize | None, optional Colormap normalization for continuous annotations, see :class:`matplotlib.colors.Normalize`. na_color : ColorLike | None, default "default" (gets set to "lightgray") Color to be used for NAs values, if present. Can either be a named color ("red"), a hex representation @@ -779,9 +776,9 @@ def render_labels( (exception: a dpi is specified in `show()`. Then the image is rasterized to fit the canvas and dpi). 3) "full": render the full image without rasterization. In the case of a multiscale image, the scale with the highest resolution is selected. This can lead to long computing times for large images! - colorbar : - Whether to request a colorbar for continuous colors. Use "auto" (default) for automatic selection. - colorbar_params : + colorbar : bool | str | None, default "auto" + Whether to request a colorbar for continuous colors. Use ``"auto"`` (default) for automatic selection. + colorbar_params : dict[str, object] | None Parameters forwarded to Matplotlib's colorbar alongside layout hints such as ``loc``, ``width``, ``pad``, and ``label``. table_name: str | None @@ -793,14 +790,12 @@ def render_labels( Column name in :attr:`sdata.table.var` to use for looking up ``color``. Use this when ``var_names`` are e.g. ENSEMBL IDs but you want to refer to genes by their symbols stored in another column of ``var``. Mimics scanpy's ``gene_symbols`` parameter. - kwargs - Additional arguments to be passed to cmap and norm. Returns ------- - None + sd.SpatialData + A copy of the SpatialData object with the rendering parameters stored in its plotting tree. """ - # TODO add Normalize object in tutorial notebook and point to that notebook here if "vmin" in kwargs or "vmax" in kwargs: logger.warning("`vmin` and `vmax` are deprecated. Pass matplotlib `Normalize` object to norm instead.") params_dict = _validate_label_render_params( @@ -845,7 +840,6 @@ def render_labels( outline_alpha=param_values["outline_alpha"], outline_color=param_values["outline_color"], fill_alpha=param_values["fill_alpha"], - transfunc=kwargs.get("transfunc"), scale=param_values["scale"], table_name=param_values["table_name"], table_layer=param_values["table_layer"], @@ -875,54 +869,76 @@ def show( dpi: int | None = None, fig: Figure | None = None, title: list[str] | str | None = None, - share_extent: bool = True, pad_extent: int | float = 0, ax: list[Axes] | Axes | None = None, return_ax: bool = False, save: str | Path | None = None, show: bool | None = None, - ) -> sd.SpatialData: + ) -> Axes | list[Axes] | None: """ - Plot the images in the SpatialData object. + Execute the plotting tree and display the final figure. Parameters ---------- - coordinate_systems : - Name(s) of the coordinate system(s) to be plotted. If None, all coordinate systems are plotted. - If a coordinate system doesn't contain any relevant elements (as specified in the render_* calls), - it is automatically not plotted. - figsize : - Size of the figure (width, height) in inches. The size of the actual canvas may deviate from this, - depending on the dpi! In matplotlib, the actual figure size (in pixels) is dpi * figsize. - If None, the default of matlotlib is used (6.4, 4.8) - dpi : - Resolution of the plot in dots per inch (as in matplotlib). - If None, the default of matplotlib is used (100.0). - ax : - Matplotlib axes object to plot on. If None, a new figure is created. - Works only if there is one image in the SpatialData object. - ncols : - Number of columns in the figure. Default is 4. - return_ax : - Whether to return the axes object created. False by default. - show : - Whether to call ``plt.show()`` at the end. If ``None`` (default), the plot is shown - automatically when running in non-interactive mode (scripts) and suppressed in - interactive sessions (e.g. Jupyter). Set to ``False`` to prevent ``plt.show()`` - from being called, which is useful when you want to save or further modify the - figure after calling this method. - colorbar : - Global switch to enable/disable all colorbars. Per-layer settings are ignored when this is False. - colorbar_params : + coordinate_systems : list[str] | str | None + Name(s) of the coordinate system(s) to be plotted. If ``None``, all coordinate systems that contain + relevant elements (as specified in the ``render_*`` calls) are plotted automatically. + legend_fontsize : int | float | str | None + Font size for the legend text. Accepts numeric values or matplotlib font size strings + (e.g. ``"small"``, ``"large"``). + legend_fontweight : int | str, default "bold" + Font weight for the legend text (e.g. ``"bold"``, ``"normal"``). + legend_loc : str | None, default "right margin" + Location of the legend. Standard matplotlib legend locations (e.g. ``"upper left"``) or + ``"right margin"``, ``"left margin"``, ``"top margin"``, ``"bottom margin"`` to place + the legend outside the axes. + legend_fontoutline : int | None + Stroke width for a white outline around legend text, improving readability on busy plots. + na_in_legend : bool, default True + Whether to include NA / unmapped categories in the legend. + colorbar : bool, default True + Global switch to enable/disable all colorbars. Per-layer settings are ignored when this is ``False``. + colorbar_params : dict[str, object] | None Global overrides passed to colorbars for all axes. Accepts the same keys as per-layer ``colorbar_params`` (e.g., ``loc``, ``width``, ``pad``, ``label``). - title : - The title of the plot. If not provided the plot will have the name of the coordinate system as title. + wspace : float | None + Horizontal spacing between panels (passed to :class:`matplotlib.gridspec.GridSpec`). + hspace : float, default 0.25 + Vertical spacing between panels (passed to :class:`matplotlib.gridspec.GridSpec`). + ncols : int, default 4 + Number of columns in the multi-panel grid. + frameon : bool | None + Whether to draw the axes frame. If ``None``, the frame is hidden automatically for multi-panel plots. + figsize : tuple[float, float] | None + Size of the figure ``(width, height)`` in inches. The actual canvas size in pixels is + ``dpi * figsize``. If ``None``, the matplotlib default is used ``(6.4, 4.8)``. + dpi : int | None + Resolution of the plot in dots per inch. If ``None``, the matplotlib default is used ``(100.0)``. + fig : Figure | None + .. deprecated:: + Pass axes created from your figure via ``ax`` instead. + title : list[str] | str | None + Title(s) for the plot. A single string is applied to all panels; a list must match the number + of coordinate systems. If ``None``, each panel is titled with its coordinate system name. + pad_extent : int | float, default 0 + Padding added around the computed spatial extent on all sides. + ax : list[Axes] | Axes | None + Pre-existing matplotlib axes to plot on. Can be a single :class:`~matplotlib.axes.Axes` or a list + matching the number of coordinate systems. If ``None``, a new figure and axes are created. + return_ax : bool, default False + Whether to return the axes object(s) instead of ``None``. + save : str | Path | None + Path to save the figure to. If ``None``, the figure is not saved. + show : bool | None + Whether to call ``plt.show()`` at the end. If ``None`` (default), the plot is shown + automatically when running in non-interactive mode (scripts) and suppressed in + interactive sessions (e.g. Jupyter). When ``ax`` is provided by the user, defaults + to ``False`` to allow further modifications. Returns ------- - sd.SpatialData - A SpatialData object. + Axes | list[Axes] | None + The axes object(s) if ``return_ax=True``, otherwise ``None``. """ _log_context.set("show") # copy the SpatialData object so we don't modify the original @@ -950,7 +966,6 @@ def show( dpi, fig, title, - share_extent, pad_extent, ax, return_ax, diff --git a/src/spatialdata_plot/pl/render.py b/src/spatialdata_plot/pl/render.py index ca67d026..8cfb72e9 100644 --- a/src/spatialdata_plot/pl/render.py +++ b/src/spatialdata_plot/pl/render.py @@ -1305,6 +1305,12 @@ def _render_images( # 2) Image has any number of channels but 1 else: + if n_channels >= 5 and render_params.colorbar == "auto": + logger.info( + "Colorbars are not shown by default for images with 5+ channels. " + "To show individual channel colorbars, render channels separately " + "with `channel=` and `colorbar=True`." + ) layers = {} for ch_idx, ch in enumerate(channels): layers[ch] = img.sel(c=ch).copy(deep=True).squeeze() diff --git a/src/spatialdata_plot/pl/render_params.py b/src/spatialdata_plot/pl/render_params.py index dffb97dd..7cbc68f5 100644 --- a/src/spatialdata_plot/pl/render_params.py +++ b/src/spatialdata_plot/pl/render_params.py @@ -168,7 +168,7 @@ class FigParams: @dataclass class OutlineParams: - """Cmap params.""" + """Outline params.""" outer_outline_color: Color | None = None outer_outline_linewidth: float = 1.5 @@ -214,7 +214,7 @@ class ScalebarParams: @dataclass class ShapesRenderParams: - """Shapes render parameters..""" + """Shapes render parameters.""" cmap_params: CmapParams outline_params: OutlineParams @@ -222,7 +222,6 @@ class ShapesRenderParams: color: Color | None = None col_for_color: str | None = None groups: str | list[str] | None = None - contour_px: int | None = None palette: ListedColormap | dict[str, str] | list[str] | None = None outline_alpha: tuple[float, float] = (1.0, 1.0) fill_alpha: float = 0.3 @@ -240,7 +239,7 @@ class ShapesRenderParams: @dataclass class PointsRenderParams: - """Points render parameters..""" + """Points render parameters.""" cmap_params: CmapParams element: str @@ -262,7 +261,7 @@ class PointsRenderParams: @dataclass class ImageRenderParams: - """Image render parameters..""" + """Image render parameters.""" cmap_params: list[CmapParams] | CmapParams element: str @@ -279,7 +278,7 @@ class ImageRenderParams: @dataclass class LabelsRenderParams: - """Labels render parameters..""" + """Labels render parameters.""" cmap_params: CmapParams element: str @@ -287,12 +286,10 @@ class LabelsRenderParams: col_for_color: str | None = None groups: str | list[str] | None = None contour_px: int | None = None - outline: bool = False palette: ListedColormap | dict[str, str] | list[str] | None = None outline_alpha: float = 1.0 outline_color: Color | None = None fill_alpha: float = 0.4 - transfunc: Callable[[float], float] | None = None scale: str | None = None table_name: str | None = None table_layer: str | None = None diff --git a/src/spatialdata_plot/pl/utils.py b/src/spatialdata_plot/pl/utils.py index 7746c8af..cb1513cf 100644 --- a/src/spatialdata_plot/pl/utils.py +++ b/src/spatialdata_plot/pl/utils.py @@ -2128,7 +2128,6 @@ def _validate_show_parameters( dpi: int | None, fig: Figure | None, title: list[str] | str | None, - share_extent: bool, pad_extent: int | float, ax: list[Axes] | Axes | None, return_ax: bool, @@ -2208,9 +2207,6 @@ def _validate_show_parameters( if title is not None and not isinstance(title, list | str): raise TypeError("Parameter 'title' must be a string or a list of strings.") - if not isinstance(share_extent, bool): - raise TypeError("Parameter 'share_extent' must be a boolean.") - if not isinstance(pad_extent, int | float): raise TypeError("Parameter 'pad_extent' must be numeric.") @@ -2440,8 +2436,9 @@ def _type_check_params(param_dict: dict[str, Any], element_type: str) -> dict[st else: raise TypeError("Parameter 'cmap' must be a string, a Colormap, or a list of these types.") - # validation happens within Color constructor - param_dict["na_color"] = Color(param_dict.get("na_color")) + # validation happens within Color constructor (images don't use na_color) + if "na_color" in param_dict: + param_dict["na_color"] = Color(param_dict.get("na_color")) norm = param_dict.get("norm") if norm is not None: @@ -2870,7 +2867,6 @@ def _validate_image_render_params( channel: list[str] | list[int] | str | int | None, alpha: float | int | None, palette: list[str] | str | None, - na_color: ColorLike | None, cmap: list[Colormap | str] | Colormap | str | None, norm: list[Normalize] | Normalize | None, scale: str | None, @@ -2883,7 +2879,6 @@ def _validate_image_render_params( "channel": channel, "alpha": alpha, "palette": palette, - "na_color": na_color, "cmap": cmap, "norm": norm, "scale": scale, @@ -2941,7 +2936,6 @@ def _validate_image_render_params( f"({', '.join(str(c) for c in channels_to_use)})." ) element_params[el]["palette"] = palette - element_params[el]["na_color"] = param_dict["na_color"] cmap = param_dict["cmap"] if cmap is not None: