|  | 
| 9 | 9 | from spatial_image import to_spatial_image | 
| 10 | 10 | from spatialdata import SpatialData, deepcopy, get_element_instances | 
| 11 | 11 | from spatialdata.models import TableModel | 
|  | 12 | +from spatialdata.transformations import Affine, Scale, Sequence, Translation, set_transformation | 
| 12 | 13 | 
 | 
| 13 | 14 | import spatialdata_plot  # noqa: F401 | 
| 14 | 15 | from tests.conftest import DPI, PlotTester, PlotTesterMeta | 
| @@ -194,3 +195,52 @@ def test_plot_subset_categorical_label_maintains_order_when_palette_overwrite(se | 
| 194 | 195 |         sdata_blobs.pl.render_labels( | 
| 195 | 196 |             "blobs_labels", color="which_max", groups=["channel_0_sum"], palette="red" | 
| 196 | 197 |         ).pl.show(ax=axs[1]) | 
|  | 198 | + | 
|  | 199 | +    def test_plot_can_render_transformed_labels_with_outline(self, sdata_blobs: SpatialData): | 
|  | 200 | +        # Blobs have by default an identity transform. Replace it by a rotated, scaled transform: | 
|  | 201 | +        angle = np.deg2rad(15) | 
|  | 202 | +        axes = ("y", "x") | 
|  | 203 | +        rotation = Affine( | 
|  | 204 | +            matrix=np.array( | 
|  | 205 | +                [ | 
|  | 206 | +                    [np.cos(angle), -np.sin(angle), 0], | 
|  | 207 | +                    [np.sin(angle), np.cos(angle), 0], | 
|  | 208 | +                    [0, 0, 1], | 
|  | 209 | +                ] | 
|  | 210 | +            ), | 
|  | 211 | +            input_axes=axes, | 
|  | 212 | +            output_axes=axes, | 
|  | 213 | +        ) | 
|  | 214 | +        translation = Translation([10, 10], axes=axes) | 
|  | 215 | +        scale = Scale([0.65, 0.65], axes=axes) | 
|  | 216 | +        transform = Sequence([translation, rotation, scale]) | 
|  | 217 | +        set_transformation(sdata_blobs["blobs_labels"], transform) | 
|  | 218 | + | 
|  | 219 | +        # Render without fill, but with outline. | 
|  | 220 | +        sdata_blobs.pl.render_labels( | 
|  | 221 | +            "blobs_labels", | 
|  | 222 | +            fill_alpha=0.0, | 
|  | 223 | +            outline=True, | 
|  | 224 | +            outline_alpha=1.0, | 
|  | 225 | +        ).pl.show() | 
|  | 226 | + | 
|  | 227 | +    def _make_tablemodel_with_categorical_labels(self, sdata_blobs, label): | 
|  | 228 | +        n_obs = max(_get_unique_label_values_as_index(sdata_blobs[label])) | 
|  | 229 | +        adata = AnnData( | 
|  | 230 | +            RNG.normal(size=(n_obs, 10)), | 
|  | 231 | +            obs=pd.DataFrame(RNG.normal(size=(n_obs, 3)), columns=["a", "b", "c"]), | 
|  | 232 | +        ) | 
|  | 233 | +        adata.obs["instance_id"] = np.arange(adata.n_obs) | 
|  | 234 | +        adata.obs["category"] = RNG.choice(["a", "b", "c"], size=adata.n_obs) | 
|  | 235 | +        adata.obs["category"][:3] = ["a", "b", "c"] | 
|  | 236 | +        adata.obs["instance_id"] = list(range(adata.n_obs)) | 
|  | 237 | +        adata.obs["region"] = label | 
|  | 238 | +        table = TableModel.parse( | 
|  | 239 | +            adata=adata, | 
|  | 240 | +            region_key="region", | 
|  | 241 | +            instance_key="instance_id", | 
|  | 242 | +            region=label, | 
|  | 243 | +        ) | 
|  | 244 | +        sdata_blobs["other_table"] = table | 
|  | 245 | +        sdata_blobs["other_table"].obs["category"] = sdata_blobs["other_table"].obs["category"].astype("category") | 
|  | 246 | +        sdata_blobs.pl.render_labels(label, color="category").pl.show() | 
0 commit comments