Skip to content

Commit c9b0f74

Browse files
committed
fix: skip 2D elements in get_fov_sdata bounding_box_query (spatialdata case 2)
1 parent 210c927 commit c9b0f74

1 file changed

Lines changed: 36 additions & 1 deletion

File tree

src/xenium_analysis_tools/alignment/format_for_napari.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,39 @@ def get_fov_sdata(
9292
else:
9393
filtered_points[el] = pts
9494

95+
# spatialdata's bounding_box_query does not support "case 2":
96+
# a 2D element (data_dim=2) with a 3D lifting transformation (rank-2 map to 3D
97+
# czstack space, transform_coordinate_length=3). Such elements come from 2D
98+
# Xenium section images/labels that align_section_to_zstack registered in the
99+
# czstack_microns coordinate system. They don't need spatial filtering here
100+
# (they are already precisely placed in czstack space), so we remove them
101+
# before the bounding-box query and re-attach them afterwards.
102+
def _is_2d_element(el):
103+
"""Return True if the element has no 'z' spatial dimension."""
104+
if hasattr(el, 'dims'):
105+
return 'z' not in el.dims
106+
if hasattr(el, 'keys'): # DataTree / multiscale
107+
try:
108+
lvl = next(iter(el.keys()))
109+
node = el[lvl]
110+
dims = node.dims if hasattr(node, 'dims') else getattr(node, 'image', node).dims
111+
return 'z' not in dims
112+
except Exception:
113+
pass
114+
return False
115+
116+
images_2d = {k: v for k, v in sdata.images.items() if _is_2d_element(v)}
117+
labels_2d = {k: v for k, v in sdata.labels.items() if _is_2d_element(v)}
118+
95119
# Query non-point elements with bounding box, then attach filtered points.
96120
points_backup = {k: sdata.points[k] for k in list(sdata.points.keys())}
97121
try:
98122
for el in list(sdata.points.keys()):
99123
del sdata.points[el]
124+
for el in images_2d:
125+
del sdata.images[el]
126+
for el in labels_2d:
127+
del sdata.labels[el]
100128

101129
fov_data = sdata.query.bounding_box(
102130
axes=['z', 'y', 'x'],
@@ -106,7 +134,14 @@ def get_fov_sdata(
106134
)
107135
finally:
108136
sdata.points = points_backup
109-
137+
sdata.images.update(images_2d)
138+
sdata.labels.update(labels_2d)
139+
140+
# Re-attach 2D section elements and filtered points.
141+
# 2D elements are already precisely registered in czstack_microns space via
142+
# their lifting transform, so no additional spatial filtering is needed.
143+
fov_data.images.update(images_2d)
144+
fov_data.labels.update(labels_2d)
110145
fov_data.points = filtered_points
111146
return fov_data
112147

0 commit comments

Comments
 (0)