From efd7487ffd2bb57c7d400c496343c4f562e86290 Mon Sep 17 00:00:00 2001 From: Sonja Stockhaus Date: Tue, 26 Sep 2023 11:10:51 +0200 Subject: [PATCH 1/4] fix index bug after spatial query --- src/spatialdata_plot/pl/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/spatialdata_plot/pl/utils.py b/src/spatialdata_plot/pl/utils.py index 981701dd..6ae9493e 100644 --- a/src/spatialdata_plot/pl/utils.py +++ b/src/spatialdata_plot/pl/utils.py @@ -223,6 +223,9 @@ def _get_collection_shape( # remove empty points/polygons shapes_df = shapes_df[shapes_df["geometry"].apply(lambda geom: not geom.is_empty)] + # reset index of shapes_df for case of spatial query + shapes_df = shapes_df.reset_index() + rows = [] def assign_fill_and_outline_to_row( From 872bf02fe0583a111eb03d1cecda44ca740c1f77 Mon Sep 17 00:00:00 2001 From: Sonja Stockhaus Date: Tue, 26 Sep 2023 12:25:03 +0200 Subject: [PATCH 2/4] add test + changelog --- CHANGELOG.md | 1 + ...hapes_can_plot_shapes_after_spatial_query.png | Bin 0 -> 4516 bytes tests/pl/test_render_shapes.py | 14 ++++++++++++++ 3 files changed, 15 insertions(+) create mode 100644 tests/_images/Shapes_can_plot_shapes_after_spatial_query.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 27fedfc5..1bea03ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning][]. - Legend order is now deterministic (#143) - Images no longer normalised by default (#150) - Colorbar no longer autoscales to [0, 1] (#155) +- Plotting shapes after a spatial query is now possible (#163) ## [0.0.4] - 2023-08-11 diff --git a/tests/_images/Shapes_can_plot_shapes_after_spatial_query.png b/tests/_images/Shapes_can_plot_shapes_after_spatial_query.png new file mode 100644 index 0000000000000000000000000000000000000000..ff731a5a14bbd39db0500354d68ea659f6b37547 GIT binary patch literal 4516 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%0ALQ&GpvMGK3B4tC` zYh1|wEDzdxzCXNupdEjG#sj8Twd96!bl>qQEW0Q7N$1_-p7+M8&Idp^XTZ*UXSL?E zy@HK#53?)KPVyaezhU}?8fP(cSkWE28~W$YD-+X0>AFB!0+r9eg5Ep#buxhestKcr zL$(!iW?T4M^sdJp=#1%hQU3R`hozBRgEt}$*DYsX^U7!M=c?(P4~AHPykeOGm%V`K zi{P`B07tR$QBG-JBBl@JB$N(|HY zMBe}0o<1|qQx5nrC+I3G3;Jk~-n@jwDpNm_n^mK&+B5|F{X5(sJtgAMPp+aOHdath zPpryJ>!<3!32A{}^gLM}%CvEYxEk5A`OpT!K~=or%S^P^4Y7GK|295thhfzYUXC9A zoo%XQ<4k>wXqvDgk^!n@bi1r4a;y0dqWLnKO2&# zEjltCg!;;YGR}G7i-64Sx_){Q`nL73@9rom$100F+k(V0N9V|$&K5fwa0++I$$IJT zFtsf>o|aLyJH@b$9b;CKuYA=>njK!v8p)OUc*y&ve(uSinUBbD_6DpRJ&)S-B%#dh z-VHOXNzNnTmlOo(dR6z(#ZoA`+3QysTAeiAugckx_|j-qpGGPrU#};veFe11%9WFm zVwxLqXWV%4xSr-#DpYV($Hnp&lj`6|TQkpOw!!!pc11SYUN|VgiB;%gvB3k|JZ0B< zb$M2u%2Ju6=k{IV_yt(^Z&Ek8v8n@f-&gi;GY&XNfF|eTSDsn|MeY@C%|~oX=1bhl zH+{uO$XV=9vgb+VPSI+u`}Kd({|gav9@t^EROgKyk!WJ-iW literal 0 HcmV?d00001 diff --git a/tests/pl/test_render_shapes.py b/tests/pl/test_render_shapes.py index 0a703b2f..32e69fc7 100644 --- a/tests/pl/test_render_shapes.py +++ b/tests/pl/test_render_shapes.py @@ -108,3 +108,17 @@ def test_plot_colorbar_respects_input_limits(self, sdata_blobs: SpatialData): def test_plot_colorbar_can_be_normalised(self, sdata_blobs: SpatialData): sdata_blobs.shapes["blobs_polygons"]["cluster"] = [1, 2, 3, 5, 20] sdata_blobs.pl.render_shapes("blobs_polygons", color="cluster", groups=["c1"], norm=True).pl.show() + + def test_plot_can_plot_shapes_after_spatial_query(self, sdata_blobs: SpatialData): + # subset to only shapes, should be unnecessary after rasterizeation of multiscale images is included + blob = SpatialData.from_elements_dict( + { + "blobs_circles": sdata_blobs.shapes["blobs_circles"], + "blobs_multipolygons": sdata_blobs.shapes["blobs_multipolygons"], + "blobs_polygons": sdata_blobs.shapes["blobs_polygons"], + } + ) + cropped_blob = blob.query.bounding_box( + axes=["x", "y"], min_coordinate=[100, 100], max_coordinate=[300, 300], target_coordinate_system="global" + ) + cropped_blob.pl.render_shapes().pl.show() From 7dfb8798a8a5735f24a903f7cd3a68f73d5649cf Mon Sep 17 00:00:00 2001 From: Sonja Stockhaus Date: Tue, 10 Oct 2023 11:50:14 +0200 Subject: [PATCH 3/4] small bugfix for coloring multiple shapes elements by annotation --- src/spatialdata_plot/pl/render.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/spatialdata_plot/pl/render.py b/src/spatialdata_plot/pl/render.py index 0170f613..106f404b 100644 --- a/src/spatialdata_plot/pl/render.py +++ b/src/spatialdata_plot/pl/render.py @@ -148,6 +148,9 @@ def _render_shapes( if not ( len(set(color_vector)) == 1 and list(set(color_vector))[0] == to_hex(render_params.cmap_params.na_color) ): + # necessary in case different shapes elements are annotated with one table + if color_source_vector is not None: + color_source_vector = color_source_vector.remove_unused_categories() _ = _decorate_axs( ax=ax, cax=cax, From 841f0c7657f07b7eb8950ebcf9344baa3dd6087e Mon Sep 17 00:00:00 2001 From: Sonja Stockhaus Date: Tue, 10 Oct 2023 11:53:01 +0200 Subject: [PATCH 4/4] add tests --- ..._queried_shapes_elements_by_annotation.png | Bin 0 -> 8824 bytes ...olor_two_shapes_elements_by_annotation.png | Bin 0 -> 10991 bytes ...th_annotation_despite_random_shuffling.png | Bin 0 -> 6304 bytes ...th_annotation_despite_random_shuffling.png | Bin 0 -> 8127 bytes tests/pl/test_render_shapes.py | 112 ++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 tests/_images/Shapes_can_color_two_queried_shapes_elements_by_annotation.png create mode 100644 tests/_images/Shapes_can_color_two_shapes_elements_by_annotation.png create mode 100644 tests/_images/Shapes_can_plot_queried_with_annotation_despite_random_shuffling.png create mode 100644 tests/_images/Shapes_can_plot_with_annotation_despite_random_shuffling.png diff --git a/tests/_images/Shapes_can_color_two_queried_shapes_elements_by_annotation.png b/tests/_images/Shapes_can_color_two_queried_shapes_elements_by_annotation.png new file mode 100644 index 0000000000000000000000000000000000000000..6961310db5238d50b924cadb653692b198e5e653 GIT binary patch literal 8824 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 YfTesq__>nx>J~xxB(Ek{En^b&e>cK!wg3PC literal 0 HcmV?d00001 diff --git a/tests/_images/Shapes_can_color_two_shapes_elements_by_annotation.png b/tests/_images/Shapes_can_color_two_shapes_elements_by_annotation.png new file mode 100644 index 0000000000000000000000000000000000000000..3e136c37e4cbb0b1d556ea29834914f38c47982a GIT binary patch literal 10991 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 literal 0 HcmV?d00001 diff --git a/tests/_images/Shapes_can_plot_with_annotation_despite_random_shuffling.png b/tests/_images/Shapes_can_plot_with_annotation_despite_random_shuffling.png new file mode 100644 index 0000000000000000000000000000000000000000..6527969e05bd1d51e6a288c97361596cd64c4c9f GIT binary patch literal 8127 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 literal 0 HcmV?d00001 diff --git a/tests/pl/test_render_shapes.py b/tests/pl/test_render_shapes.py index 3cb9dba1..941b1970 100644 --- a/tests/pl/test_render_shapes.py +++ b/tests/pl/test_render_shapes.py @@ -1,6 +1,7 @@ import anndata import geopandas as gpd import matplotlib +import numpy as np import pandas as pd import scanpy as sc import spatialdata_plot # noqa: F401 @@ -142,3 +143,114 @@ def test_plot_can_plot_shapes_after_spatial_query(self, sdata_blobs: SpatialData axes=["x", "y"], min_coordinate=[100, 100], max_coordinate=[300, 300], target_coordinate_system="global" ) cropped_blob.pl.render_shapes().pl.show() + + def test_plot_can_plot_with_annotation_despite_random_shuffling(self, sdata_blobs: SpatialData): + new_table = sdata_blobs.table.copy() + sdata_blobs.table.obs["region"] = "blobs_circles" + new_table = sdata_blobs.table[:5] + new_table.uns["spatialdata_attrs"]["region"] = "blobs_circles" + new_table.obs["instance_id"] = np.array(range(5)) + + new_table.obs["annotation"] = ["a", "b", "c", "d", "e"] + new_table.obs["annotation"] = new_table.obs["annotation"].astype("category") + + del sdata_blobs.table + sdata_blobs.table = new_table + + # random permutation of table and shapes + sdata_blobs.table.obs = sdata_blobs.table.obs.sample(frac=1, random_state=83) + temp = sdata_blobs["blobs_circles"].sample(frac=1, random_state=47) + del sdata_blobs.shapes["blobs_circles"] + sdata_blobs["blobs_circles"] = temp + + sdata_blobs.pl.render_shapes("blobs_circles", color="annotation").pl.show() + + def test_plot_can_plot_queried_with_annotation_despite_random_shuffling(self, sdata_blobs: SpatialData): + new_table = sdata_blobs.table.copy() + sdata_blobs.table.obs["region"] = "blobs_circles" + new_table = sdata_blobs.table[:5] + new_table.uns["spatialdata_attrs"]["region"] = "blobs_circles" + new_table.obs["instance_id"] = np.array(range(5)) + + new_table.obs["annotation"] = ["a", "b", "c", "d", "e"] + new_table.obs["annotation"] = new_table.obs["annotation"].astype("category") + + del sdata_blobs.table + sdata_blobs.table = new_table + + # random permutation of table and shapes + sdata_blobs.table.obs = sdata_blobs.table.obs.sample(frac=1, random_state=83) + temp = sdata_blobs["blobs_circles"].sample(frac=1, random_state=47) + del sdata_blobs.shapes["blobs_circles"] + sdata_blobs["blobs_circles"] = temp + + # subsetting the data + sdata_cropped = sdata_blobs.query.bounding_box( + axes=("x", "y"), + min_coordinate=[100, 150], + max_coordinate=[400, 250], + target_coordinate_system="global", + filter_table=True, + ) + + # workaround for bug that should be gone in later versions + del sdata_cropped.images["blobs_multiscale_image"] + del sdata_cropped.labels["blobs_labels"] + del sdata_cropped.labels["blobs_multiscale_labels"] + + sdata_cropped.pl.render_shapes("blobs_circles", color="annotation").pl.show() + + def test_plot_can_color_two_shapes_elements_by_annotation(self, sdata_blobs: SpatialData): + new_table = sdata_blobs.table.copy() + sdata_blobs.table.obs["region"] = "blobs_circles" + new_table = sdata_blobs.table[:10] + new_table.uns["spatialdata_attrs"]["region"] = ["blobs_circles", "blobs_polygons"] + new_table.obs["instance_id"] = np.concatenate((np.array(range(5)), np.array(range(5)))) + + new_table.obs.loc[5 * [False] + 5 * [True], "region"] = "blobs_polygons" + new_table.obs["annotation"] = ["a", "b", "c", "d", "e", "v", "w", "x", "y", "z"] + new_table.obs["annotation"] = new_table.obs["annotation"].astype("category") + + del sdata_blobs.table + sdata_blobs.table = new_table + + sdata_blobs.pl.render_shapes(["blobs_circles", "blobs_polygons"], color="annotation").pl.show() + + def test_plot_can_color_two_queried_shapes_elements_by_annotation(self, sdata_blobs: SpatialData): + new_table = sdata_blobs.table.copy() + sdata_blobs.table.obs["region"] = "blobs_circles" + new_table = sdata_blobs.table[:10] + new_table.uns["spatialdata_attrs"]["region"] = ["blobs_circles", "blobs_polygons"] + new_table.obs["instance_id"] = np.concatenate((np.array(range(5)), np.array(range(5)))) + + new_table.obs.loc[5 * [False] + 5 * [True], "region"] = "blobs_polygons" + new_table.obs["annotation"] = ["a", "b", "c", "d", "e", "v", "w", "x", "y", "z"] + new_table.obs["annotation"] = new_table.obs["annotation"].astype("category") + + del sdata_blobs.table + sdata_blobs.table = new_table + + # random permutation of table and shapes + sdata_blobs.table.obs = sdata_blobs.table.obs.sample(frac=1, random_state=83) + temp = sdata_blobs["blobs_circles"].sample(frac=1, random_state=47) + del sdata_blobs.shapes["blobs_circles"] + sdata_blobs["blobs_circles"] = temp + temp = sdata_blobs["blobs_polygons"].sample(frac=1, random_state=71) + del sdata_blobs.shapes["blobs_polygons"] + sdata_blobs["blobs_polygons"] = temp + + # subsetting the data + sdata_cropped = sdata_blobs.query.bounding_box( + axes=("x", "y"), + min_coordinate=[100, 150], + max_coordinate=[350, 300], + target_coordinate_system="global", + filter_table=True, + ) + + # workaround for bug that should be gone in later versions + del sdata_cropped.images["blobs_multiscale_image"] + del sdata_cropped.labels["blobs_labels"] + del sdata_cropped.labels["blobs_multiscale_labels"] + + sdata_cropped.pl.render_shapes(["blobs_circles", "blobs_polygons"], color="annotation").pl.show()