@@ -1323,6 +1323,39 @@ def test_render_shapes_color_with_conflicting_index_name():
13231323 sdata .pl .render_shapes ("shapes" , color = "cell_type" , table_name = "table" ).pl .show ()
13241324
13251325
1326+ def test_render_shapes_disjoint_instance_ids_clear_error ():
1327+ # Regression test for #603: when a table annotates the element (region key matches) but no
1328+ # instance_id values overlap, the call used to crash with a bare `KeyError: None` from deep
1329+ # inside spatialdata's join path. Replace with a clear, actionable ValueError.
1330+ from shapely .geometry import Point
1331+
1332+ shapes = ShapesModel .parse (
1333+ gpd .GeoDataFrame ({"geometry" : [Point (5 , 5 ), Point (15 , 5 ), Point (25 , 5 )], "radius" : [2.0 ] * 3 })
1334+ )
1335+ obs = pd .DataFrame (
1336+ {
1337+ "instance_id" : [99 , 100 , 101 ], # element has IDs 0, 1, 2 -- no overlap
1338+ "region" : pd .Categorical (["s" ] * 3 ),
1339+ "cat" : pd .Categorical (["A" , "B" , "C" ]),
1340+ }
1341+ )
1342+ obs .index = obs .index .astype (str )
1343+ table = TableModel .parse (
1344+ AnnData (X = np .zeros ((3 , 1 )), obs = obs ),
1345+ region = ["s" ],
1346+ region_key = "region" ,
1347+ instance_key = "instance_id" ,
1348+ )
1349+ sdata = SpatialData (shapes = {"s" : shapes }, tables = {"t" : table })
1350+
1351+ fig , ax = plt .subplots ()
1352+ try :
1353+ with pytest .raises (ValueError , match = r"No instance IDs overlap.*table 't'.*element 's'" ):
1354+ sdata .pl .render_shapes ("s" , color = "cat" , table_name = "t" ).pl .show (ax = ax )
1355+ finally :
1356+ plt .close (fig )
1357+
1358+
13261359def test_datashader_colorbar_range_matches_data (sdata_blobs : SpatialData ):
13271360 """Datashader colorbar range must not exceed the actual data range for shapes.
13281361
0 commit comments