Skip to content

Commit d5a811a

Browse files
committed
fix(render_points): resolve continuous norm for the matplotlib backend
Matplotlib points passed an un-scaled fresh_norm() to ax.scatter and let it autoscale, diverging from shapes/labels: a constant-valued continuous points layer mapped to the cmap floor instead of the [0, 1] reset, and a LogNorm was autoscaled over the raw data (crashing on a 0/negative). Route continuous points through _resolve_continuous_norm so fill and colorbar match the other element types and LogNorm/PowerNorm survive. Kept separate from the core fix as it is the only change that can shift point visual baselines. Adds a regression test for render_points(norm=LogNorm()) over data with a 0.
1 parent dfea23b commit d5a811a

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

src/spatialdata_plot/pl/render.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -698,8 +698,7 @@ def _render_shapes(
698698
nan_count = int(pd.isna(cv).sum())
699699
if nan_count:
700700
logger.warning(
701-
f"Found {nan_count} NaN values in color data. "
702-
"These observations will be colored with the 'na_color'."
701+
f"Found {nan_count} NaN values in color data. These observations will be colored with the 'na_color'."
703702
)
704703
color_spec = color_spec.evolve(color_vector=cv)
705704

@@ -1416,7 +1415,14 @@ def _render_points(
14161415

14171416
trans, trans_data = _prepare_transformation(sdata.points[element], coordinate_system, ax)
14181417

1419-
norm = render_params.cmap_params.fresh_norm()
1418+
# Continuous points resolve their norm through the shared resolver so the fill and colorbar match
1419+
# the shapes/labels treatment (degenerate [0, 1] reset, LogNorm/PowerNorm preserved) instead of
1420+
# letting ax.scatter autoscale a fresh linear norm. Non-continuous keeps the un-scaled fresh norm.
1421+
norm = (
1422+
_resolve_continuous_norm(color_spec.color_vector, render_params.cmap_params)
1423+
if color_spec.is_continuous
1424+
else render_params.cmap_params.fresh_norm()
1425+
)
14201426

14211427
method = render_params.method
14221428

tests/pl/test_render_points.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,18 @@ def test_groups_na_color_none_no_match_points(sdata_blobs: SpatialData):
666666
).pl.show()
667667

668668

669+
def test_render_points_lognorm_with_zeros_does_not_crash(sdata_blobs: SpatialData):
670+
# Regression: matplotlib points resolve their continuous norm through the shared resolver, so a
671+
# LogNorm column containing 0 must derive a positive vmin instead of a LogNorm(vmin=0) that raises.
672+
from matplotlib.colors import LogNorm
673+
674+
n = len(sdata_blobs["blobs_points"])
675+
sdata_blobs["blobs_points"]["counts"] = pd.Series(np.linspace(0.0, 10.0, n)) # includes 0
676+
fig, ax = plt.subplots()
677+
sdata_blobs.pl.render_points("blobs_points", color="counts", norm=LogNorm(), method="matplotlib").pl.show(ax=ax)
678+
plt.close(fig)
679+
680+
669681
@pytest.mark.parametrize("na_color", [None, "red"])
670682
def test_groups_warns_when_no_groups_match_points(sdata_blobs: SpatialData, caplog, na_color):
671683
"""Warning fires regardless of na_color when no groups match."""

0 commit comments

Comments
 (0)