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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning][].
### Added

- Multipolygons are now handled correctly (#93)
- Can now scale shapes (#152)
- Can now plot columns from GeoDataFrame (#149)

### Fixed
Expand Down
7 changes: 4 additions & 3 deletions src/spatialdata_plot/pl/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def render_shapes(
elements: str | list[str] | None = None,
color: str | None = None,
groups: str | Sequence[str] | None = None,
size: float = 1.0,
scale: float = 1.0,
outline: bool = False,
outline_width: float = 1.5,
outline_color: str | list[float] = "#000000ff",
Expand All @@ -171,7 +171,7 @@ def render_shapes(
groups
For discrete annotation in ``color``, select which values
to plot (other values are set to NAs).
size
scale
Value to scale circles, if present.
outline
If `True`, a thin border around points/shapes is plotted.
Expand Down Expand Up @@ -212,11 +212,12 @@ def render_shapes(
na_color=na_color, # type: ignore[arg-type]
**kwargs,
)
outline_params = _set_outline(size, outline, outline_width, outline_color)
outline_params = _set_outline(outline, outline_width, outline_color)
sdata.plotting_tree[f"{n_steps+1}_render_shapes"] = ShapesRenderParams(
elements=elements,
color=color,
groups=groups,
scale=scale,
outline_params=outline_params,
layer=layer,
cmap_params=cmap_params,
Expand Down
2 changes: 1 addition & 1 deletion src/spatialdata_plot/pl/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def _render_shapes(
shapes = gpd.GeoDataFrame(shapes, geometry="geometry")
_cax = _get_collection_shape(
shapes=shapes,
s=render_params.size,
s=render_params.scale,
c=color_vector,
render_params=render_params,
rasterized=sc_settings._vector_friendly,
Expand Down
2 changes: 1 addition & 1 deletion src/spatialdata_plot/pl/render_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class ShapesRenderParams:
palette: ListedColormap | str | None = None
outline_alpha: float = 1.0
fill_alpha: float = 0.3
size: float = 1.0
scale: float = 1.0
transfunc: Callable[[float], float] | None = None


Expand Down
22 changes: 15 additions & 7 deletions src/spatialdata_plot/pl/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def _get_collection_shape(
Args:
- shapes (list[GeoDataFrame]): List of geometrical shapes.
- c: Color parameter.
- s (float): Size of the shape.
- s (float): Scale of the shape.
- norm: Normalization for the color map.
- fill_alpha (float, optional): Opacity for the fill color.
- outline_alpha (float, optional): Opacity for the outline.
Expand Down Expand Up @@ -241,21 +241,30 @@ def assign_fill_and_outline_to_row(
geom = row["geometry"]
if geom.geom_type == "Polygon":
row = row.to_dict()
row["geometry"] = mplp.Polygon(geom.exterior.coords, closed=True)
coords = np.array(geom.exterior.coords)
centroid = np.mean(coords, axis=0)
scaled_coords = [(centroid + (np.array(coord) - centroid) * s).tolist() for coord in geom.exterior.coords]
row["geometry"] = mplp.Polygon(scaled_coords, closed=True)
assign_fill_and_outline_to_row(shapes, fill_c, outline_c, row, idx)
rows.append(row)

elif geom.geom_type == "MultiPolygon":
mp = _make_patch_from_multipolygon(geom)
for _, m in enumerate(mp):
# mp = _make_patch_from_multipolygon(geom)
for polygon in geom.geoms:
mp_copy = row.to_dict()
mp_copy["geometry"] = m
coords = np.array(polygon.exterior.coords)
centroid = np.mean(coords, axis=0)
scaled_coords = [(centroid + (coord - centroid) * s).tolist() for coord in coords]
mp_copy["geometry"] = mplp.Polygon(scaled_coords, closed=True)
assign_fill_and_outline_to_row(shapes, fill_c, outline_c, mp_copy, idx)
rows.append(mp_copy)

elif geom.geom_type == "Point":
row = row.to_dict()
row["geometry"] = mplp.Circle((geom.x, geom.y), radius=row["radius"])
scaled_radius = row["radius"] * s
row["geometry"] = mplp.Circle(
(geom.x, geom.y), radius=scaled_radius
) # Circle is always scaled from its center
assign_fill_and_outline_to_row(shapes, fill_c, outline_c, row, idx)
rows.append(row)

Expand Down Expand Up @@ -576,7 +585,6 @@ def _prepare_cmap_norm(


def _set_outline(
size: float,
outline: bool = False,
outline_width: float = 1.5,
outline_color: str | list[float] = "#0000000ff", # black, white
Expand Down
Binary file added tests/_images/Shapes_can_scale_shapes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/pl/test_render_shapes.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,6 @@ def test_plot_can_color_from_geodataframe(self, sdata_blobs: SpatialData):
elements="blobs_polygons",
color="value",
).pl.show()

def test_plot_can_scale_shapes(self, sdata_blobs: SpatialData):
sdata_blobs.pl.render_shapes(elements="blobs_circles", scale=0.5).pl.show()