From 43b84a6834f6a32574cf76adcea9f19cc0cda177 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 17:49:07 +0000 Subject: [PATCH 1/5] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 23.10.0 → 23.10.1](https://github.com/psf/black/compare/23.10.0...23.10.1) - [github.com/astral-sh/ruff-pre-commit: v0.1.1 → v0.1.3](https://github.com/astral-sh/ruff-pre-commit/compare/v0.1.1...v0.1.3) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0aa59803..89e11456 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ ci: skip: [] repos: - repo: https://github.com/psf/black - rev: 23.10.0 + rev: 23.10.1 hooks: - id: black - repo: https://github.com/pre-commit/mirrors-prettier @@ -27,7 +27,7 @@ repos: additional_dependencies: [numpy, types-requests] exclude: tests/|docs/ - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.1 + rev: v0.1.3 hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] From c617d3de54f1a9b2553ab73406104f2dce973658 Mon Sep 17 00:00:00 2001 From: Luca Marconato Date: Mon, 30 Oct 2023 21:15:19 +0100 Subject: [PATCH 2/5] added api for renaming coordinate systems --- src/spatialdata/_core/spatialdata.py | 56 +++++++++++++++++++ .../operations/test_spatialdata_operations.py | 53 ++++++++++++++++++ 2 files changed, 109 insertions(+) diff --git a/src/spatialdata/_core/spatialdata.py b/src/spatialdata/_core/spatialdata.py index 2979f25a..d0506c48 100644 --- a/src/spatialdata/_core/spatialdata.py +++ b/src/spatialdata/_core/spatialdata.py @@ -569,6 +569,62 @@ def filter_by_coordinate_system(self, coordinate_system: str | list[str], filter return SpatialData(**elements, table=table) + def rename_coordinate_systems(self, rename_dict: dict[str, str]) -> None: + """ + Rename coordinate systems. + + Parameters + ---------- + rename_dict + A dictionary mapping old coordinate system names to new coordinate system names. + + Notes + ----- + The method does not allow to rename a coordinate system into an existing one, unless the existing one is also + renamed in the same call. + """ + from spatialdata.transformations.operations import get_transformation, set_transformation + + # check that the rename_dict is valid + old_names = self.coordinate_systems + new_names = list(set(old_names).difference(set(rename_dict.keys()))) + for old_cs, new_cs in rename_dict.items(): + if old_cs not in old_names: + raise ValueError(f"Coordinate system {old_cs} does not exist.") + if new_cs in new_names: + raise ValueError( + "It is not allowed to rename a coordinate system if the new name already exists and " + "if it is not renamed in the same call." + ) + new_names.append(new_cs) + + # rename the coordinate systems + for element in self._gen_elements_values(): + # get the transformations + transformations = get_transformation(element, get_all=True) + assert isinstance(transformations, dict) + + # appends a random suffix to the coordinate system name to avoid collisions + suffixes_to_replace = set() + for old_cs, new_cs in rename_dict.items(): + if old_cs in transformations: + random_suffix = hashlib.sha1(os.urandom(128)).hexdigest()[:8] + transformations[new_cs + random_suffix] = transformations.pop(old_cs) + suffixes_to_replace.add(new_cs + random_suffix) + + # remove the random suffixes + new_transformations = {} + for cs_with_suffix in transformations: + if cs_with_suffix in suffixes_to_replace: + cs = cs_with_suffix[:-8] + new_transformations[cs] = transformations[cs_with_suffix] + suffixes_to_replace.remove(cs_with_suffix) + else: + new_transformations[cs_with_suffix] = transformations[cs_with_suffix] + + # set the new transformations + set_transformation(element=element, transformation=new_transformations, set_all=True) + def transform_element_to_coordinate_system( self, element: SpatialElement, target_coordinate_system: str ) -> SpatialElement: diff --git a/tests/core/operations/test_spatialdata_operations.py b/tests/core/operations/test_spatialdata_operations.py index f2cd2695..c551461c 100644 --- a/tests/core/operations/test_spatialdata_operations.py +++ b/tests/core/operations/test_spatialdata_operations.py @@ -128,6 +128,59 @@ def test_filter_by_coordinate_system_also_table(full_sdata): assert len(filtered_sdata2.table) == len(full_sdata.table) +def test_rename_coordinate_systems(full_sdata): + # all the elements point to global, add new coordinate systems + set_transformation( + element=full_sdata.shapes["circles"], transformation=Identity(), to_coordinate_system="my_space0" + ) + set_transformation(element=full_sdata.shapes["poly"], transformation=Identity(), to_coordinate_system="my_space1") + set_transformation( + element=full_sdata.shapes["multipoly"], transformation=Identity(), to_coordinate_system="my_space2" + ) + + elements_in_global_before = { + name for _, name, _ in full_sdata.filter_by_coordinate_system("global")._gen_elements() + } + + # test a renaming without collisions + full_sdata.rename_coordinate_systems({"my_space0": "my_space00", "my_space1": "my_space11"}) + assert {"my_space00", "my_space11", "global", "my_space2"}.issubset(full_sdata.coordinate_systems) + assert "my_space0" not in full_sdata.coordinate_systems + assert "my_space1" not in full_sdata.coordinate_systems + + # renaming with collisions (my_space2 already exists) + with pytest.raises(ValueError): + full_sdata.rename_coordinate_systems({"my_space00": "my_space2"}) + + # renaming with collisions (my_space3 doesn't exist but it's target of two renamings) + with pytest.raises(ValueError): + full_sdata.rename_coordinate_systems({"my_space00": "my_space3", "my_space11": "my_space3"}) + + # invalid renaming: my_space3 is not a valid coordinate system + with pytest.raises(ValueError): + full_sdata.rename_coordinate_systems({"my_space3": "my_space4"}) + + # invalid renaming: my_space3 is not a valid coordinate system (it doesn't matter if my_space3 is target of one + # renaming, as it doesn't exist at the time of the function call) + with pytest.raises(ValueError): + full_sdata.rename_coordinate_systems( + {"my_space00": "my_space3", "my_space11": "my_space3", "my_space3": "my_space4"} + ) + + # valid renaming with collisions + full_sdata.rename_coordinate_systems({"my_space00": "my_space2", "my_space2": "my_space3"}) + assert get_transformation(full_sdata.shapes["circles"], get_all=True)["my_space2"] == Identity() + assert get_transformation(full_sdata.shapes["multipoly"], get_all=True)["my_space3"] == Identity() + + # renaming without effect + full_sdata.rename_coordinate_systems({"my_space11": "my_space11"}) + assert get_transformation(full_sdata.shapes["poly"], get_all=True)["my_space11"] == Identity() + + # check that all the elements with coordinate system global are still there + elements_in_global_after = {name for _, name, _ in full_sdata.filter_by_coordinate_system("global")._gen_elements()} + assert elements_in_global_before == elements_in_global_after + + def test_concatenate_tables(): """ The concatenation uses AnnData.concatenate(), here we test the From c224bf83f8f6e990d7f95e5921b7cf02830e36fa Mon Sep 17 00:00:00 2001 From: Luca Marconato Date: Mon, 30 Oct 2023 21:16:15 +0100 Subject: [PATCH 3/5] added rename_coordinate_systems() --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbcac708..fb9bb118 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,10 +10,16 @@ and this project adheres to [Semantic Versioning][]. ## [0.0.15] - tbd +### Added + ## [0.0.14] - 2023-10-11 ### Added +#### Minor + +- new API: sdata.rename_coordinate_systems() + #### Major - get_extent() function to compute bounding box of the data From b34e6501345eb8f821967da03105d29685c00998 Mon Sep 17 00:00:00 2001 From: Luca Marconato Date: Mon, 30 Oct 2023 21:18:20 +0100 Subject: [PATCH 4/5] updated changelog with previously closed PRs --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb9bb118..f2fe3358 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ and this project adheres to [Semantic Versioning][]. - new API: sdata.rename_coordinate_systems() +#### Technical + +- decompose affine transformation into simpler transformations +- remove padding for blobs() + #### Major - get_extent() function to compute bounding box of the data From 8711797dbe3f4499b4f1ae800947a5506ef23f69 Mon Sep 17 00:00:00 2001 From: Luca Marconato Date: Thu, 2 Nov 2023 19:16:28 +0100 Subject: [PATCH 5/5] fixed points test range due to new padding of blobs --- tests/core/test_data_extent.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/test_data_extent.py b/tests/core/test_data_extent.py index 204138c1..3b0477cc 100644 --- a/tests/core/test_data_extent.py +++ b/tests/core/test_data_extent.py @@ -59,8 +59,8 @@ def test_get_extent_points(): extent = get_extent(sdata["blobs_points"]) check_test_results0( extent, - min_coordinates=np.array([12.0, 13.0]), - max_coordinates=np.array([500.0, 498.0]), + min_coordinates=np.array([3.0, 4.0]), + max_coordinates=np.array([509.0, 507.0]), axes=("x", "y"), )