Bug description
When rendering shapes with the datashader backend and continuous color data, na_color=None (or any transparent na_color) does not work — NaN shapes are rendered as opaque lightgray instead of being invisible.
The matplotlib path handles this correctly.
Root cause
Two issues in _render_shapes (datashader continuous path):
1. Alpha channel stripped from na_color_hex
In src/spatialdata_plot/pl/render.py:533:
na_color_hex = _hex_no_alpha(render_params.cmap_params.na_color.get_hex())
_hex_no_alpha strips the alpha channel, so Color(None) (which stores color="#d3d3d3", alpha="00") becomes "#d3d3d3" — fully opaque grey. This is then passed to ds.tf.shade(nan_agg, cmap=na_color_hex) in _ds_shade_continuous, producing an opaque grey NaN overlay.
2. User's fill_alpha applied to NaN overlay
In src/spatialdata_plot/pl/_datashader.py:252:
shade_kwargs["min_alpha"] = _convert_alpha_to_datashader_range(alpha)
The user's fill_alpha (e.g., alpha=0.7) is applied as min_alpha to the NaN overlay layer. Even if na_color_hex were transparent, this forces the NaN overlay to have nonzero alpha.
Comparison with matplotlib path
The matplotlib path in _get_collection_shape (utils.py) uses na_color.get_hex_with_alpha() — with alpha — so na_color=None correctly produces fully transparent fills for NaN shapes.
Suggested fix
- Pass
na_color_hex with its alpha component to _ds_shade_continuous, or check na_color.alpha == "00" and skip the NaN overlay entirely.
- Do not apply
fill_alpha as min_alpha on the NaN overlay when na_color is fully transparent.
Minimal reproducer
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from spatialdata import SpatialData
from spatialdata.models import ShapesModel
import geopandas as gpd
from shapely import box
import spatialdata_plot # noqa: F401
# Create 20k shapes (triggers datashader auto-switch at >10k)
shapes = gpd.GeoDataFrame(
geometry=[box(i % 100, i // 100, i % 100 + 1, i // 100 + 1) for i in range(20000)]
)
shapes = ShapesModel.parse(shapes)
# Only 5k have actual values; the rest are NaN
values = np.full(20000, np.nan)
values[:5000] = np.random.default_rng(0).uniform(0, 100, 5000)
shapes["value"] = values
sdata = SpatialData(shapes={"grid": shapes})
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# Datashader: NaN shapes appear as grey (BUG)
sdata.pl.render_shapes("grid", color="value", na_color=None, method="datashader").pl.show(ax=ax1)
ax1.set_title("datashader: na_color=None (grey = bug)")
# Matplotlib: NaN shapes are transparent (CORRECT)
sdata.pl.render_shapes("grid", color="value", na_color=None, method="matplotlib").pl.show(ax=ax2)
ax2.set_title("matplotlib: na_color=None (correct)")
fig.savefig("/tmp/na_color_bug.png", dpi=100)
Version info
Observed on current main branch.
Bug description
When rendering shapes with the datashader backend and continuous color data,
na_color=None(or any transparentna_color) does not work — NaN shapes are rendered as opaque lightgray instead of being invisible.The matplotlib path handles this correctly.
Root cause
Two issues in
_render_shapes(datashader continuous path):1. Alpha channel stripped from
na_color_hexIn
src/spatialdata_plot/pl/render.py:533:_hex_no_alphastrips the alpha channel, soColor(None)(which storescolor="#d3d3d3",alpha="00") becomes"#d3d3d3"— fully opaque grey. This is then passed tods.tf.shade(nan_agg, cmap=na_color_hex)in_ds_shade_continuous, producing an opaque grey NaN overlay.2. User's
fill_alphaapplied to NaN overlayIn
src/spatialdata_plot/pl/_datashader.py:252:The user's
fill_alpha(e.g.,alpha=0.7) is applied asmin_alphato the NaN overlay layer. Even ifna_color_hexwere transparent, this forces the NaN overlay to have nonzero alpha.Comparison with matplotlib path
The matplotlib path in
_get_collection_shape(utils.py) usesna_color.get_hex_with_alpha()— with alpha — sona_color=Nonecorrectly produces fully transparent fills for NaN shapes.Suggested fix
na_color_hexwith its alpha component to_ds_shade_continuous, or checkna_color.alpha == "00"and skip the NaN overlay entirely.fill_alphaasmin_alphaon the NaN overlay whenna_coloris fully transparent.Minimal reproducer
Version info
Observed on current
mainbranch.