Skip to content

Commit 49acff0

Browse files
authored
Support channels_as_legend for single-channel and sequential renders (#641)
1 parent c2a1ed3 commit 49acff0

4 files changed

Lines changed: 47 additions & 2 deletions

File tree

src/spatialdata_plot/pl/render.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,8 @@ def _render_images(
13541354
)
13551355

13561356
_ax_show_and_transform(stacked, trans_data, ax, **show_kwargs)
1357+
if render_params.channels_as_legend:
1358+
logger.warning("channels_as_legend is not supported for true RGB images and will be ignored.")
13571359
return
13581360

13591361
# 1) Image has only 1 channel
@@ -1386,7 +1388,13 @@ def _render_images(
13861388
is_continuous=True,
13871389
auto_condition=n_channels == 1,
13881390
)
1389-
if wants_colorbar and legend_params.colorbar and colorbar_requests is not None:
1391+
if render_params.channels_as_legend and channel_legend_entries is not None:
1392+
# Sample at 0.75 (upper quarter) for a vivid, non-extreme representative color;
1393+
# consistent with the multi-channel composite path below.
1394+
_collect_channel_legend_entries(
1395+
[channels[0]], [matplotlib.colors.to_hex(cmap(0.75))], channel_legend_entries
1396+
)
1397+
elif wants_colorbar and legend_params.colorbar and colorbar_requests is not None:
13901398
sm = plt.cm.ScalarMappable(cmap=cmap, norm=render_params.cmap_params.norm)
13911399
colorbar_requests.append(
13921400
ColorbarSpec(
69.6 KB
Loading
73.3 KB
Loading

tests/pl/test_render_images.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from spatialdata.models import Image2DModel
1111

1212
import spatialdata_plot # noqa: F401
13+
from spatialdata_plot._logging import logger, logger_warns
1314
from spatialdata_plot.pl.render import _is_rgb_image
1415
from tests.conftest import DPI, PlotTester, PlotTesterMeta, _viridis_with_under_over
1516

@@ -545,13 +546,49 @@ def test_plot_channels_as_legend_legend_lower_right(self, sdata_blobs: SpatialDa
545546
legend_loc="lower right"
546547
)
547548

549+
def test_plot_channels_as_legend_single_channel(self, sdata_blobs: SpatialData):
550+
sdata_blobs.pl.render_images(element="blobs_image", channel=0, channels_as_legend=True).pl.show()
551+
552+
def test_plot_channels_as_legend_sequential_single_channels(self, sdata_blobs_str: SpatialData):
553+
(
554+
sdata_blobs_str.pl.render_images(
555+
element="blobs_image",
556+
channel="c1",
557+
palette=["cyan"],
558+
alpha=0.5,
559+
channels_as_legend=True,
560+
)
561+
.pl.render_images(
562+
element="blobs_image",
563+
channel="c2",
564+
palette=["magenta"],
565+
alpha=0.5,
566+
channels_as_legend=True,
567+
)
568+
.pl.show()
569+
)
570+
548571

549572
class TestChannelsAsCategoriesNonVisual:
550573
"""Non-visual tests for channels_as_legend edge cases."""
551574

552-
def test_channels_as_legend_ignored_for_single_channel(self, sdata_blobs: SpatialData):
575+
def test_channels_as_legend_single_channel_shows_legend_no_colorbar(self, sdata_blobs: SpatialData):
553576
fig, ax = plt.subplots()
554577
sdata_blobs.pl.render_images(element="blobs_image", channel=0, channels_as_legend=True).pl.show(ax=ax)
578+
legend = ax.get_legend()
579+
assert legend is not None
580+
assert "0" in [t.get_text() for t in legend.get_texts()]
581+
assert len(fig.axes) == 1 # no colorbar inset axes
582+
plt.close("all")
583+
584+
def test_channels_as_legend_rgb_warns_and_no_legend(self, caplog):
585+
data = np.zeros((3, 50, 50), dtype=np.float64)
586+
data[0], data[1], data[2] = 0.8, 0.2, 0.1
587+
img = Image2DModel.parse(data, dims=("c", "y", "x"), c_coords=["r", "g", "b"])
588+
sdata = SpatialData(images={"img": img})
589+
fig, ax = plt.subplots()
590+
with logger_warns(caplog, logger, match="not supported for true RGB"):
591+
sdata.pl.render_images("img", channels_as_legend=True).pl.show(ax=ax)
555592
assert ax.get_legend() is None
556593
plt.close("all")
557594

0 commit comments

Comments
 (0)