@@ -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