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 numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning][].
### Added

- added SpatialData.subset() API
- added SpatialData.locate_element() API

### Fixed

Expand Down
87 changes: 29 additions & 58 deletions src/spatialdata/_core/spatialdata.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -312,24 +312,19 @@ def _init_add_element(self, name: str, element_type: str, overwrite: bool) -> za
return elem_group
return root

def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
def locate_element(self, element: SpatialElement) -> list[str] | None:
"""
Find the SpatialElement within the SpatialData object.
Locate a SpatialElement within the SpatialData object and, if found, returns its Zarr path relative to the root.

Parameters
----------
element
The queried SpatialElement


Returns
-------
name and type of the element

Raises
------
ValueError
the element is not found or found multiple times in the SpatialData object
A list of Zarr paths of the element relative to the root (multiple copies of the same element are allowed), or
None if the element is not found.
"""
found: list[SpatialElement] = []
found_element_type: list[str] = []
Expand All@@ -341,39 +336,8 @@ def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
found_element_type.append(element_type)
found_element_name.append(element_name)
if len(found) == 0:
raise ValueError("Element not found in the SpatialData object.")
if len(found) > 1:
raise ValueError(
f"Element found multiple times in the SpatialData object."
f"Found {len(found)} elements with names: {found_element_name},"
f" and types: {found_element_type}"
)
assert len(found_element_name) == 1
assert len(found_element_type) == 1
return found_element_name[0], found_element_type[0]

def contains_element(self, element: SpatialElement, raise_exception: bool = False) -> bool:
"""
Check if the SpatialElement is contained in the SpatialData object.

Parameters
----------
element
The SpatialElement to check
raise_exception
If True, raise an exception if the element is not found. If False, return False if the element is not found.

Returns
-------
True if the element is found; False otherwise (if raise_exception is False).
"""
try:
self._locate_spatial_element(element)
return True
except ValueError as e:
if raise_exception:
raise e
return False
return None
Comment thread
LucaMarconato marked this conversation as resolved.
return [f"{found_element_type[i]}/{found_element_name[i]}" for i in range(len(found))]

def _write_transformations_to_disk(self, element: SpatialElement) -> None:
"""
Expand All@@ -388,25 +352,32 @@ def _write_transformations_to_disk(self, element: SpatialElement) -> None:

transformations = get_transformation(element, get_all=True)
assert isinstance(transformations, dict)
found_element_name, found_element_type = self._locate_spatial_element(element)

located = self.locate_element(element)
if located is None:
raise ValueError(
"Cannot save the transformation to the element as it has not been found in the SpatialData object"
)
if self.path is not None:
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)
for path in located:
found_element_type, found_element_name = path.split("/")
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)

overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)
overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)

overwrite_coordinate_transformations_non_raster(group=group, axes=axes, transformations=transformations)
else:
raise ValueError("Unknown element type")
overwrite_coordinate_transformations_non_raster(
group=group, axes=axes, transformations=transformations
)
else:
raise ValueError("Unknown element type")

def filter_by_coordinate_system(self, coordinate_system: str | list[str], filter_table: bool = True) -> SpatialData:
"""
Expand Down
8 changes: 4 additions & 4 deletions src/spatialdata/transformations/operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,8 +68,8 @@ def set_transformation(
assert to_coordinate_system is None
_set_transformations(element, transformation)
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either set a transformation to an element "
Expand DownExpand Up@@ -164,8 +164,8 @@ def remove_transformation(
assert to_coordinate_system is None
_set_transformations(element, {})
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either remove a transformation from an "
Expand Down
9 changes: 4 additions & 5 deletions tests/core/operations/test_spatialdata_operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -310,15 +310,14 @@ def test_concatenate_sdatas(full_sdata: SpatialData) -> None:


def test_locate_spatial_element(full_sdata: SpatialData) -> None:
assert full_sdata._locate_spatial_element(full_sdata.images["image2d"]) == ("image2d", "images")
assert full_sdata.locate_element(full_sdata.images["image2d"])[0] == "images/image2d"
im = full_sdata.images["image2d"]
del full_sdata.images["image2d"]
with pytest.raises(ValueError, match="Element not found in the SpatialData object."):
full_sdata._locate_spatial_element(im)
assert full_sdata.locate_element(im) is None
full_sdata.images["image2d"] = im
full_sdata.images["image2d_again"] = im
with pytest.raises(ValueError):
full_sdata._locate_spatial_element(im)
paths = full_sdata.locate_element(im)
assert len(paths) == 2


def test_get_item(points: SpatialData) -> None:
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Made _locate_spatial_element public, renamed to locate_element() by LucaMarconato · Pull Request #427 · scverse/spatialdata · GitHub
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 numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning][].
### Added

- added SpatialData.subset() API
- added SpatialData.locate_element() API

### Fixed

Expand Down
87 changes: 29 additions & 58 deletions src/spatialdata/_core/spatialdata.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -312,24 +312,19 @@ def _init_add_element(self, name: str, element_type: str, overwrite: bool) -> za
return elem_group
return root

def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
def locate_element(self, element: SpatialElement) -> list[str] | None:
"""
Find the SpatialElement within the SpatialData object.
Locate a SpatialElement within the SpatialData object and, if found, returns its Zarr path relative to the root.

Parameters
----------
element
The queried SpatialElement


Returns
-------
name and type of the element

Raises
------
ValueError
the element is not found or found multiple times in the SpatialData object
A list of Zarr paths of the element relative to the root (multiple copies of the same element are allowed), or
None if the element is not found.
"""
found: list[SpatialElement] = []
found_element_type: list[str] = []
Expand All@@ -341,39 +336,8 @@ def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
found_element_type.append(element_type)
found_element_name.append(element_name)
if len(found) == 0:
raise ValueError("Element not found in the SpatialData object.")
if len(found) > 1:
raise ValueError(
f"Element found multiple times in the SpatialData object."
f"Found {len(found)} elements with names: {found_element_name},"
f" and types: {found_element_type}"
)
assert len(found_element_name) == 1
assert len(found_element_type) == 1
return found_element_name[0], found_element_type[0]

def contains_element(self, element: SpatialElement, raise_exception: bool = False) -> bool:
"""
Check if the SpatialElement is contained in the SpatialData object.

Parameters
----------
element
The SpatialElement to check
raise_exception
If True, raise an exception if the element is not found. If False, return False if the element is not found.

Returns
-------
True if the element is found; False otherwise (if raise_exception is False).
"""
try:
self._locate_spatial_element(element)
return True
except ValueError as e:
if raise_exception:
raise e
return False
return None
Comment thread
LucaMarconato marked this conversation as resolved.
return [f"{found_element_type[i]}/{found_element_name[i]}" for i in range(len(found))]

def _write_transformations_to_disk(self, element: SpatialElement) -> None:
"""
Expand All@@ -388,25 +352,32 @@ def _write_transformations_to_disk(self, element: SpatialElement) -> None:

transformations = get_transformation(element, get_all=True)
assert isinstance(transformations, dict)
found_element_name, found_element_type = self._locate_spatial_element(element)

located = self.locate_element(element)
if located is None:
raise ValueError(
"Cannot save the transformation to the element as it has not been found in the SpatialData object"
)
if self.path is not None:
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)
for path in located:
found_element_type, found_element_name = path.split("/")
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)

overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)
overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)

overwrite_coordinate_transformations_non_raster(group=group, axes=axes, transformations=transformations)
else:
raise ValueError("Unknown element type")
overwrite_coordinate_transformations_non_raster(
group=group, axes=axes, transformations=transformations
)
else:
raise ValueError("Unknown element type")

def filter_by_coordinate_system(self, coordinate_system: str | list[str], filter_table: bool = True) -> SpatialData:
"""
Expand Down
8 changes: 4 additions & 4 deletions src/spatialdata/transformations/operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,8 +68,8 @@ def set_transformation(
assert to_coordinate_system is None
_set_transformations(element, transformation)
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either set a transformation to an element "
Expand DownExpand Up@@ -164,8 +164,8 @@ def remove_transformation(
assert to_coordinate_system is None
_set_transformations(element, {})
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either remove a transformation from an "
Expand Down
9 changes: 4 additions & 5 deletions tests/core/operations/test_spatialdata_operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -310,15 +310,14 @@ def test_concatenate_sdatas(full_sdata: SpatialData) -> None:


def test_locate_spatial_element(full_sdata: SpatialData) -> None:
assert full_sdata._locate_spatial_element(full_sdata.images["image2d"]) == ("image2d", "images")
assert full_sdata.locate_element(full_sdata.images["image2d"])[0] == "images/image2d"
im = full_sdata.images["image2d"]
del full_sdata.images["image2d"]
with pytest.raises(ValueError, match="Element not found in the SpatialData object."):
full_sdata._locate_spatial_element(im)
assert full_sdata.locate_element(im) is None
full_sdata.images["image2d"] = im
full_sdata.images["image2d_again"] = im
with pytest.raises(ValueError):
full_sdata._locate_spatial_element(im)
paths = full_sdata.locate_element(im)
assert len(paths) == 2


def test_get_item(points: SpatialData) -> None:
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Made _locate_spatial_element public, renamed to locate_element() by LucaMarconato · Pull Request #427 · scverse/spatialdata · GitHub
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 numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning][].
### Added

- added SpatialData.subset() API
- added SpatialData.locate_element() API

### Fixed

Expand Down
87 changes: 29 additions & 58 deletions src/spatialdata/_core/spatialdata.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -312,24 +312,19 @@ def _init_add_element(self, name: str, element_type: str, overwrite: bool) -> za
return elem_group
return root

def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
def locate_element(self, element: SpatialElement) -> list[str] | None:
"""
Find the SpatialElement within the SpatialData object.
Locate a SpatialElement within the SpatialData object and, if found, returns its Zarr path relative to the root.

Parameters
----------
element
The queried SpatialElement


Returns
-------
name and type of the element

Raises
------
ValueError
the element is not found or found multiple times in the SpatialData object
A list of Zarr paths of the element relative to the root (multiple copies of the same element are allowed), or
None if the element is not found.
"""
found: list[SpatialElement] = []
found_element_type: list[str] = []
Expand All@@ -341,39 +336,8 @@ def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
found_element_type.append(element_type)
found_element_name.append(element_name)
if len(found) == 0:
raise ValueError("Element not found in the SpatialData object.")
if len(found) > 1:
raise ValueError(
f"Element found multiple times in the SpatialData object."
f"Found {len(found)} elements with names: {found_element_name},"
f" and types: {found_element_type}"
)
assert len(found_element_name) == 1
assert len(found_element_type) == 1
return found_element_name[0], found_element_type[0]

def contains_element(self, element: SpatialElement, raise_exception: bool = False) -> bool:
"""
Check if the SpatialElement is contained in the SpatialData object.

Parameters
----------
element
The SpatialElement to check
raise_exception
If True, raise an exception if the element is not found. If False, return False if the element is not found.

Returns
-------
True if the element is found; False otherwise (if raise_exception is False).
"""
try:
self._locate_spatial_element(element)
return True
except ValueError as e:
if raise_exception:
raise e
return False
return None
Comment thread
LucaMarconato marked this conversation as resolved.
return [f"{found_element_type[i]}/{found_element_name[i]}" for i in range(len(found))]

def _write_transformations_to_disk(self, element: SpatialElement) -> None:
"""
Expand All@@ -388,25 +352,32 @@ def _write_transformations_to_disk(self, element: SpatialElement) -> None:

transformations = get_transformation(element, get_all=True)
assert isinstance(transformations, dict)
found_element_name, found_element_type = self._locate_spatial_element(element)

located = self.locate_element(element)
if located is None:
raise ValueError(
"Cannot save the transformation to the element as it has not been found in the SpatialData object"
)
if self.path is not None:
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)
for path in located:
found_element_type, found_element_name = path.split("/")
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)

overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)
overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)

overwrite_coordinate_transformations_non_raster(group=group, axes=axes, transformations=transformations)
else:
raise ValueError("Unknown element type")
overwrite_coordinate_transformations_non_raster(
group=group, axes=axes, transformations=transformations
)
else:
raise ValueError("Unknown element type")

def filter_by_coordinate_system(self, coordinate_system: str | list[str], filter_table: bool = True) -> SpatialData:
"""
Expand Down
8 changes: 4 additions & 4 deletions src/spatialdata/transformations/operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,8 +68,8 @@ def set_transformation(
assert to_coordinate_system is None
_set_transformations(element, transformation)
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either set a transformation to an element "
Expand DownExpand Up@@ -164,8 +164,8 @@ def remove_transformation(
assert to_coordinate_system is None
_set_transformations(element, {})
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either remove a transformation from an "
Expand Down
9 changes: 4 additions & 5 deletions tests/core/operations/test_spatialdata_operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -310,15 +310,14 @@ def test_concatenate_sdatas(full_sdata: SpatialData) -> None:


def test_locate_spatial_element(full_sdata: SpatialData) -> None:
assert full_sdata._locate_spatial_element(full_sdata.images["image2d"]) == ("image2d", "images")
assert full_sdata.locate_element(full_sdata.images["image2d"])[0] == "images/image2d"
im = full_sdata.images["image2d"]
del full_sdata.images["image2d"]
with pytest.raises(ValueError, match="Element not found in the SpatialData object."):
full_sdata._locate_spatial_element(im)
assert full_sdata.locate_element(im) is None
full_sdata.images["image2d"] = im
full_sdata.images["image2d_again"] = im
with pytest.raises(ValueError):
full_sdata._locate_spatial_element(im)
paths = full_sdata.locate_element(im)
assert len(paths) == 2


def test_get_item(points: SpatialData) -> None:
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Made _locate_spatial_element public, renamed to locate_element() by LucaMarconato · Pull Request #427 · scverse/spatialdata · GitHub
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 numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning][].
### Added

- added SpatialData.subset() API
- added SpatialData.locate_element() API

### Fixed

Expand Down
87 changes: 29 additions & 58 deletions src/spatialdata/_core/spatialdata.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -312,24 +312,19 @@ def _init_add_element(self, name: str, element_type: str, overwrite: bool) -> za
return elem_group
return root

def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
def locate_element(self, element: SpatialElement) -> list[str] | None:
"""
Find the SpatialElement within the SpatialData object.
Locate a SpatialElement within the SpatialData object and, if found, returns its Zarr path relative to the root.

Parameters
----------
element
The queried SpatialElement


Returns
-------
name and type of the element

Raises
------
ValueError
the element is not found or found multiple times in the SpatialData object
A list of Zarr paths of the element relative to the root (multiple copies of the same element are allowed), or
None if the element is not found.
"""
found: list[SpatialElement] = []
found_element_type: list[str] = []
Expand All@@ -341,39 +336,8 @@ def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
found_element_type.append(element_type)
found_element_name.append(element_name)
if len(found) == 0:
raise ValueError("Element not found in the SpatialData object.")
if len(found) > 1:
raise ValueError(
f"Element found multiple times in the SpatialData object."
f"Found {len(found)} elements with names: {found_element_name},"
f" and types: {found_element_type}"
)
assert len(found_element_name) == 1
assert len(found_element_type) == 1
return found_element_name[0], found_element_type[0]

def contains_element(self, element: SpatialElement, raise_exception: bool = False) -> bool:
"""
Check if the SpatialElement is contained in the SpatialData object.

Parameters
----------
element
The SpatialElement to check
raise_exception
If True, raise an exception if the element is not found. If False, return False if the element is not found.

Returns
-------
True if the element is found; False otherwise (if raise_exception is False).
"""
try:
self._locate_spatial_element(element)
return True
except ValueError as e:
if raise_exception:
raise e
return False
return None
Comment thread
LucaMarconato marked this conversation as resolved.
return [f"{found_element_type[i]}/{found_element_name[i]}" for i in range(len(found))]

def _write_transformations_to_disk(self, element: SpatialElement) -> None:
"""
Expand All@@ -388,25 +352,32 @@ def _write_transformations_to_disk(self, element: SpatialElement) -> None:

transformations = get_transformation(element, get_all=True)
assert isinstance(transformations, dict)
found_element_name, found_element_type = self._locate_spatial_element(element)

located = self.locate_element(element)
if located is None:
raise ValueError(
"Cannot save the transformation to the element as it has not been found in the SpatialData object"
)
if self.path is not None:
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)
for path in located:
found_element_type, found_element_name = path.split("/")
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)

overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)
overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)

overwrite_coordinate_transformations_non_raster(group=group, axes=axes, transformations=transformations)
else:
raise ValueError("Unknown element type")
overwrite_coordinate_transformations_non_raster(
group=group, axes=axes, transformations=transformations
)
else:
raise ValueError("Unknown element type")

def filter_by_coordinate_system(self, coordinate_system: str | list[str], filter_table: bool = True) -> SpatialData:
"""
Expand Down
8 changes: 4 additions & 4 deletions src/spatialdata/transformations/operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,8 +68,8 @@ def set_transformation(
assert to_coordinate_system is None
_set_transformations(element, transformation)
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either set a transformation to an element "
Expand DownExpand Up@@ -164,8 +164,8 @@ def remove_transformation(
assert to_coordinate_system is None
_set_transformations(element, {})
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either remove a transformation from an "
Expand Down
9 changes: 4 additions & 5 deletions tests/core/operations/test_spatialdata_operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -310,15 +310,14 @@ def test_concatenate_sdatas(full_sdata: SpatialData) -> None:


def test_locate_spatial_element(full_sdata: SpatialData) -> None:
assert full_sdata._locate_spatial_element(full_sdata.images["image2d"]) == ("image2d", "images")
assert full_sdata.locate_element(full_sdata.images["image2d"])[0] == "images/image2d"
im = full_sdata.images["image2d"]
del full_sdata.images["image2d"]
with pytest.raises(ValueError, match="Element not found in the SpatialData object."):
full_sdata._locate_spatial_element(im)
assert full_sdata.locate_element(im) is None
full_sdata.images["image2d"] = im
full_sdata.images["image2d_again"] = im
with pytest.raises(ValueError):
full_sdata._locate_spatial_element(im)
paths = full_sdata.locate_element(im)
assert len(paths) == 2


def test_get_item(points: SpatialData) -> None:
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Made _locate_spatial_element public, renamed to locate_element() by LucaMarconato · Pull Request #427 · scverse/spatialdata · GitHub
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 numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning][].
### Added

- added SpatialData.subset() API
- added SpatialData.locate_element() API

### Fixed

Expand Down
87 changes: 29 additions & 58 deletions src/spatialdata/_core/spatialdata.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -312,24 +312,19 @@ def _init_add_element(self, name: str, element_type: str, overwrite: bool) -> za
return elem_group
return root

def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
def locate_element(self, element: SpatialElement) -> list[str] | None:
"""
Find the SpatialElement within the SpatialData object.
Locate a SpatialElement within the SpatialData object and, if found, returns its Zarr path relative to the root.

Parameters
----------
element
The queried SpatialElement


Returns
-------
name and type of the element

Raises
------
ValueError
the element is not found or found multiple times in the SpatialData object
A list of Zarr paths of the element relative to the root (multiple copies of the same element are allowed), or
None if the element is not found.
"""
found: list[SpatialElement] = []
found_element_type: list[str] = []
Expand All@@ -341,39 +336,8 @@ def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
found_element_type.append(element_type)
found_element_name.append(element_name)
if len(found) == 0:
raise ValueError("Element not found in the SpatialData object.")
if len(found) > 1:
raise ValueError(
f"Element found multiple times in the SpatialData object."
f"Found {len(found)} elements with names: {found_element_name},"
f" and types: {found_element_type}"
)
assert len(found_element_name) == 1
assert len(found_element_type) == 1
return found_element_name[0], found_element_type[0]

def contains_element(self, element: SpatialElement, raise_exception: bool = False) -> bool:
"""
Check if the SpatialElement is contained in the SpatialData object.

Parameters
----------
element
The SpatialElement to check
raise_exception
If True, raise an exception if the element is not found. If False, return False if the element is not found.

Returns
-------
True if the element is found; False otherwise (if raise_exception is False).
"""
try:
self._locate_spatial_element(element)
return True
except ValueError as e:
if raise_exception:
raise e
return False
return None
Comment thread
LucaMarconato marked this conversation as resolved.
return [f"{found_element_type[i]}/{found_element_name[i]}" for i in range(len(found))]

def _write_transformations_to_disk(self, element: SpatialElement) -> None:
"""
Expand All@@ -388,25 +352,32 @@ def _write_transformations_to_disk(self, element: SpatialElement) -> None:

transformations = get_transformation(element, get_all=True)
assert isinstance(transformations, dict)
found_element_name, found_element_type = self._locate_spatial_element(element)

located = self.locate_element(element)
if located is None:
raise ValueError(
"Cannot save the transformation to the element as it has not been found in the SpatialData object"
)
if self.path is not None:
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)
for path in located:
found_element_type, found_element_name = path.split("/")
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)

overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)
overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)

overwrite_coordinate_transformations_non_raster(group=group, axes=axes, transformations=transformations)
else:
raise ValueError("Unknown element type")
overwrite_coordinate_transformations_non_raster(
group=group, axes=axes, transformations=transformations
)
else:
raise ValueError("Unknown element type")

def filter_by_coordinate_system(self, coordinate_system: str | list[str], filter_table: bool = True) -> SpatialData:
"""
Expand Down
8 changes: 4 additions & 4 deletions src/spatialdata/transformations/operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,8 +68,8 @@ def set_transformation(
assert to_coordinate_system is None
_set_transformations(element, transformation)
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either set a transformation to an element "
Expand DownExpand Up@@ -164,8 +164,8 @@ def remove_transformation(
assert to_coordinate_system is None
_set_transformations(element, {})
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either remove a transformation from an "
Expand Down
9 changes: 4 additions & 5 deletions tests/core/operations/test_spatialdata_operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -310,15 +310,14 @@ def test_concatenate_sdatas(full_sdata: SpatialData) -> None:


def test_locate_spatial_element(full_sdata: SpatialData) -> None:
assert full_sdata._locate_spatial_element(full_sdata.images["image2d"]) == ("image2d", "images")
assert full_sdata.locate_element(full_sdata.images["image2d"])[0] == "images/image2d"
im = full_sdata.images["image2d"]
del full_sdata.images["image2d"]
with pytest.raises(ValueError, match="Element not found in the SpatialData object."):
full_sdata._locate_spatial_element(im)
assert full_sdata.locate_element(im) is None
full_sdata.images["image2d"] = im
full_sdata.images["image2d_again"] = im
with pytest.raises(ValueError):
full_sdata._locate_spatial_element(im)
paths = full_sdata.locate_element(im)
assert len(paths) == 2


def test_get_item(points: SpatialData) -> None:
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Made _locate_spatial_element public, renamed to locate_element() by LucaMarconato · Pull Request #427 · scverse/spatialdata · GitHub
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 numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning][].
### Added

- added SpatialData.subset() API
- added SpatialData.locate_element() API

### Fixed

Expand Down
87 changes: 29 additions & 58 deletions src/spatialdata/_core/spatialdata.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -312,24 +312,19 @@ def _init_add_element(self, name: str, element_type: str, overwrite: bool) -> za
return elem_group
return root

def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
def locate_element(self, element: SpatialElement) -> list[str] | None:
"""
Find the SpatialElement within the SpatialData object.
Locate a SpatialElement within the SpatialData object and, if found, returns its Zarr path relative to the root.

Parameters
----------
element
The queried SpatialElement


Returns
-------
name and type of the element

Raises
------
ValueError
the element is not found or found multiple times in the SpatialData object
A list of Zarr paths of the element relative to the root (multiple copies of the same element are allowed), or
None if the element is not found.
"""
found: list[SpatialElement] = []
found_element_type: list[str] = []
Expand All@@ -341,39 +336,8 @@ def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
found_element_type.append(element_type)
found_element_name.append(element_name)
if len(found) == 0:
raise ValueError("Element not found in the SpatialData object.")
if len(found) > 1:
raise ValueError(
f"Element found multiple times in the SpatialData object."
f"Found {len(found)} elements with names: {found_element_name},"
f" and types: {found_element_type}"
)
assert len(found_element_name) == 1
assert len(found_element_type) == 1
return found_element_name[0], found_element_type[0]

def contains_element(self, element: SpatialElement, raise_exception: bool = False) -> bool:
"""
Check if the SpatialElement is contained in the SpatialData object.

Parameters
----------
element
The SpatialElement to check
raise_exception
If True, raise an exception if the element is not found. If False, return False if the element is not found.

Returns
-------
True if the element is found; False otherwise (if raise_exception is False).
"""
try:
self._locate_spatial_element(element)
return True
except ValueError as e:
if raise_exception:
raise e
return False
return None
Comment thread
LucaMarconato marked this conversation as resolved.
return [f"{found_element_type[i]}/{found_element_name[i]}" for i in range(len(found))]

def _write_transformations_to_disk(self, element: SpatialElement) -> None:
"""
Expand All@@ -388,25 +352,32 @@ def _write_transformations_to_disk(self, element: SpatialElement) -> None:

transformations = get_transformation(element, get_all=True)
assert isinstance(transformations, dict)
found_element_name, found_element_type = self._locate_spatial_element(element)

located = self.locate_element(element)
if located is None:
raise ValueError(
"Cannot save the transformation to the element as it has not been found in the SpatialData object"
)
if self.path is not None:
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)
for path in located:
found_element_type, found_element_name = path.split("/")
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)

overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)
overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)

overwrite_coordinate_transformations_non_raster(group=group, axes=axes, transformations=transformations)
else:
raise ValueError("Unknown element type")
overwrite_coordinate_transformations_non_raster(
group=group, axes=axes, transformations=transformations
)
else:
raise ValueError("Unknown element type")

def filter_by_coordinate_system(self, coordinate_system: str | list[str], filter_table: bool = True) -> SpatialData:
"""
Expand Down
8 changes: 4 additions & 4 deletions src/spatialdata/transformations/operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,8 +68,8 @@ def set_transformation(
assert to_coordinate_system is None
_set_transformations(element, transformation)
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either set a transformation to an element "
Expand DownExpand Up@@ -164,8 +164,8 @@ def remove_transformation(
assert to_coordinate_system is None
_set_transformations(element, {})
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either remove a transformation from an "
Expand Down
9 changes: 4 additions & 5 deletions tests/core/operations/test_spatialdata_operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -310,15 +310,14 @@ def test_concatenate_sdatas(full_sdata: SpatialData) -> None:


def test_locate_spatial_element(full_sdata: SpatialData) -> None:
assert full_sdata._locate_spatial_element(full_sdata.images["image2d"]) == ("image2d", "images")
assert full_sdata.locate_element(full_sdata.images["image2d"])[0] == "images/image2d"
im = full_sdata.images["image2d"]
del full_sdata.images["image2d"]
with pytest.raises(ValueError, match="Element not found in the SpatialData object."):
full_sdata._locate_spatial_element(im)
assert full_sdata.locate_element(im) is None
full_sdata.images["image2d"] = im
full_sdata.images["image2d_again"] = im
with pytest.raises(ValueError):
full_sdata._locate_spatial_element(im)
paths = full_sdata.locate_element(im)
assert len(paths) == 2


def test_get_item(points: SpatialData) -> None:
Expand Down
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Made _locate_spatial_element public, renamed to locate_element() by LucaMarconato · Pull Request #427 · scverse/spatialdata · GitHub
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 numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning][].
### Added

- added SpatialData.subset() API
- added SpatialData.locate_element() API

### Fixed

Expand Down
87 changes: 29 additions & 58 deletions src/spatialdata/_core/spatialdata.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -312,24 +312,19 @@ def _init_add_element(self, name: str, element_type: str, overwrite: bool) -> za
return elem_group
return root

def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
def locate_element(self, element: SpatialElement) -> list[str] | None:
"""
Find the SpatialElement within the SpatialData object.
Locate a SpatialElement within the SpatialData object and, if found, returns its Zarr path relative to the root.

Parameters
----------
element
The queried SpatialElement


Returns
-------
name and type of the element

Raises
------
ValueError
the element is not found or found multiple times in the SpatialData object
A list of Zarr paths of the element relative to the root (multiple copies of the same element are allowed), or
None if the element is not found.
"""
found: list[SpatialElement] = []
found_element_type: list[str] = []
Expand All@@ -341,39 +336,8 @@ def _locate_spatial_element(self, element: SpatialElement) -> tuple[str, str]:
found_element_type.append(element_type)
found_element_name.append(element_name)
if len(found) == 0:
raise ValueError("Element not found in the SpatialData object.")
if len(found) > 1:
raise ValueError(
f"Element found multiple times in the SpatialData object."
f"Found {len(found)} elements with names: {found_element_name},"
f" and types: {found_element_type}"
)
assert len(found_element_name) == 1
assert len(found_element_type) == 1
return found_element_name[0], found_element_type[0]

def contains_element(self, element: SpatialElement, raise_exception: bool = False) -> bool:
"""
Check if the SpatialElement is contained in the SpatialData object.

Parameters
----------
element
The SpatialElement to check
raise_exception
If True, raise an exception if the element is not found. If False, return False if the element is not found.

Returns
-------
True if the element is found; False otherwise (if raise_exception is False).
"""
try:
self._locate_spatial_element(element)
return True
except ValueError as e:
if raise_exception:
raise e
return False
return None
Comment thread
LucaMarconato marked this conversation as resolved.
return [f"{found_element_type[i]}/{found_element_name[i]}" for i in range(len(found))]

def _write_transformations_to_disk(self, element: SpatialElement) -> None:
"""
Expand All@@ -388,25 +352,32 @@ def _write_transformations_to_disk(self, element: SpatialElement) -> None:

transformations = get_transformation(element, get_all=True)
assert isinstance(transformations, dict)
found_element_name, found_element_type = self._locate_spatial_element(element)

located = self.locate_element(element)
if located is None:
raise ValueError(
"Cannot save the transformation to the element as it has not been found in the SpatialData object"
)
if self.path is not None:
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)
for path in located:
found_element_type, found_element_name = path.split("/")
group = self._get_group_for_element(name=found_element_name, element_type=found_element_type)
axes = get_axes_names(element)
if isinstance(element, (SpatialImage, MultiscaleSpatialImage)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_raster,
)

overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)
overwrite_coordinate_transformations_raster(group=group, axes=axes, transformations=transformations)
elif isinstance(element, (DaskDataFrame, GeoDataFrame, AnnData)):
from spatialdata._io._utils import (
overwrite_coordinate_transformations_non_raster,
)

overwrite_coordinate_transformations_non_raster(group=group, axes=axes, transformations=transformations)
else:
raise ValueError("Unknown element type")
overwrite_coordinate_transformations_non_raster(
group=group, axes=axes, transformations=transformations
)
else:
raise ValueError("Unknown element type")

def filter_by_coordinate_system(self, coordinate_system: str | list[str], filter_table: bool = True) -> SpatialData:
"""
Expand Down
8 changes: 4 additions & 4 deletions src/spatialdata/transformations/operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -68,8 +68,8 @@ def set_transformation(
assert to_coordinate_system is None
_set_transformations(element, transformation)
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either set a transformation to an element "
Expand DownExpand Up@@ -164,8 +164,8 @@ def remove_transformation(
assert to_coordinate_system is None
_set_transformations(element, {})
else:
if not write_to_sdata.contains_element(element, raise_exception=True):
raise RuntimeError("contains_element() failed without raising an exception.")
if write_to_sdata.locate_element(element) is None:
raise RuntimeError("The element is not found in the SpatialData object.")
if not write_to_sdata.is_backed():
raise ValueError(
"The SpatialData object is not backed. You can either remove a transformation from an "
Expand Down
9 changes: 4 additions & 5 deletions tests/core/operations/test_spatialdata_operations.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -310,15 +310,14 @@ def test_concatenate_sdatas(full_sdata: SpatialData) -> None:


def test_locate_spatial_element(full_sdata: SpatialData) -> None:
assert full_sdata._locate_spatial_element(full_sdata.images["image2d"]) == ("image2d", "images")
assert full_sdata.locate_element(full_sdata.images["image2d"])[0] == "images/image2d"
im = full_sdata.images["image2d"]
del full_sdata.images["image2d"]
with pytest.raises(ValueError, match="Element not found in the SpatialData object."):
full_sdata._locate_spatial_element(im)
assert full_sdata.locate_element(im) is None
full_sdata.images["image2d"] = im
full_sdata.images["image2d_again"] = im
with pytest.raises(ValueError):
full_sdata._locate_spatial_element(im)
paths = full_sdata.locate_element(im)
assert len(paths) == 2


def test_get_item(points: SpatialData) -> None:
Expand Down