diff --git a/src/spatialdata_plot/pl/render.py b/src/spatialdata_plot/pl/render.py index 7e1b8073..de534f46 100644 --- a/src/spatialdata_plot/pl/render.py +++ b/src/spatialdata_plot/pl/render.py @@ -1621,6 +1621,13 @@ def _render_labels( is_label=True, ) + if np.issubdtype(label.dtype, np.floating): + raise ValueError( + f"Label element '{element}' has dtype {label.dtype}. Label arrays must use an " + f"integer dtype (e.g. int32 or uint16). Cast before plotting, e.g.:\n" + f" sdata['{element}'] = sdata['{element}'].astype('int32')" + ) + # rasterize spatial image if necessary to speed up performance if rasterize: label = _rasterize_if_necessary( diff --git a/tests/pl/test_render_labels.py b/tests/pl/test_render_labels.py index 9a8057c1..f4c5001e 100644 --- a/tests/pl/test_render_labels.py +++ b/tests/pl/test_render_labels.py @@ -473,3 +473,20 @@ def track(x): plt.close(fig) assert called, "transfunc was not called for continuous labels data" + + +@pytest.mark.parametrize("dtype", [np.float16, np.float32, np.float64]) +def test_render_labels_rejects_float_dtype(dtype): + # Regression test for #606: float-dtype labels must raise a clear + # ValueError naming the element and dtype, not a cryptic skimage TypeError. + arr = np.zeros((20, 20), dtype=dtype) + arr[3:8, 3:8] = 1 + arr[12:17, 12:17] = 2 + sdata = SpatialData(labels={"lbl": Labels2DModel.parse(arr, dims=["y", "x"])}) + + fig, ax = plt.subplots() + try: + with pytest.raises(ValueError, match=r"Label element 'lbl'.*integer dtype"): + sdata.pl.render_labels("lbl").pl.show(ax=ax) + finally: + plt.close(fig)