Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions src/spatialdata_plot/pl/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -895,11 +895,17 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
transformed_geometry = shapes["geometry"].transform(
lambda x: (np.hstack([x, np.ones((x.shape[0], 1))]) @ tm.T)[:, :2]
)
transformed_geodataframe = gpd.GeoDataFrame(
data=shapes.drop("geometry", axis=1),
geometry=transformed_geometry,
)
# Geometry is already in the target CS, so tag the wrapped element with an identity transform
# there. Depending on the pandas/geopandas version the constructor above may carry over the
# source element's transform attrs; drop them first so parse does not see conflicting ones.
transformed_geodataframe.attrs.pop("transform", None)
transformed_element = ShapesModel.parse(
gpd.GeoDataFrame(
data=shapes.drop("geometry", axis=1),
geometry=transformed_geometry,
)
transformed_geodataframe,
transformations={coordinate_system: Identity()},
)

if len(transformed_element) == 0:
Expand All @@ -908,7 +914,7 @@ def _draw_centroids(xy: np.ndarray, radius: float | None = None) -> None:
return

plot_width, plot_height, x_ext, y_ext, factor = _get_extent_and_range_for_datashader_canvas(
transformed_element, "global", fig_params
transformed_element, coordinate_system, fig_params
)

cvs = ds.Canvas(plot_width=plot_width, plot_height=plot_height, x_range=x_ext, y_range=y_ext)
Expand Down
13 changes: 3 additions & 10 deletions tests/pl/test_render_shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1331,10 +1331,10 @@ def test_plot_can_handle_non_numeric_radius_values(sdata_blobs: SpatialData):
def test_groups_filtering_preserves_transformation(sdata_blobs: SpatialData):
"""Regression test for #420: groups filtering must not strip coordinate-system metadata.

Simulates the exact sequence that ``_render_shapes`` performs —
Simulates the sequence that ``_render_shapes`` performs —
filter_by_coordinate_system -> groups boolean-index -> reset_index ->
re-assign to sdata_filt -> GeoDataFrame re-wrap — then asserts that
``_prepare_transformation`` can still retrieve the correct transformation.
re-assign to sdata_filt — then asserts that ``_prepare_transformation``
can still retrieve the correct transformation.
"""
from spatialdata_plot.pl._datashader import _prepare_transformation

Expand All @@ -1354,20 +1354,13 @@ def test_groups_filtering_preserves_transformation(sdata_blobs: SpatialData):
keep = shapes["cluster"] == "c1"
shapes = shapes[keep].reset_index(drop=True)
sdata_filt["blobs_polygons"] = shapes
# GeoDataFrame re-wrap strips .attrs (this is what _render_shapes does next)
shapes = gpd.GeoDataFrame(shapes, geometry="geometry")

# sdata_filt's element must still carry the correct transformation
trans, _ = _prepare_transformation(sdata_filt.shapes["blobs_polygons"], cs)
matrix = trans.get_matrix()
np.testing.assert_allclose(matrix[0, 0], scale_factor, err_msg="x-scale lost after groups filtering")
np.testing.assert_allclose(matrix[1, 1], scale_factor, err_msg="y-scale lost after groups filtering")

# The GeoDataFrame re-wrap strips attrs — reading the transform from
# the re-wrapped object must fail, proving why early capture matters.
with pytest.raises(AssertionError):
_prepare_transformation(shapes, cs)


def test_plot_can_handle_mixed_numeric_and_color_data(sdata_blobs: SpatialData):
"""Test that mixed numeric and color-like data raises a clear error."""
Expand Down
Loading