9
9
from types import MappingProxyType
10
10
from typing import Any , Literal , Optional , Union
11
11
12
+ import matplotlib .patches as mpatches
13
+ import matplotlib .path as mpath
12
14
import matplotlib .pyplot as plt
13
15
import multiscale_spatial_image as msi
14
16
import numpy as np
15
17
import pandas as pd
18
+ import shapely
16
19
import spatial_image
17
20
import spatialdata as sd
18
21
import xarray as xr
@@ -305,7 +308,9 @@ def get_point_bb(
305
308
sdata .shapes [e_id ]["geometry" ].apply (lambda geom : geom .geom_type == "Point" )
306
309
]
307
310
tmp_polygons = sdata .shapes [e_id ][
308
- sdata .shapes [e_id ]["geometry" ].apply (lambda geom : geom .geom_type == "Polygon" )
311
+ sdata .shapes [e_id ]["geometry" ].apply (
312
+ lambda geom : geom .geom_type in ["Polygon" , "MultiPolygon" ]
313
+ )
309
314
]
310
315
311
316
if not tmp_points .empty :
@@ -321,24 +326,17 @@ def get_point_bb(
321
326
xmin_br , ymin_br , xmax_br , ymax_br = tmp_points ["point_bottomright" ].total_bounds
322
327
y_dims += [min (ymin_tl , ymin_br ), max (ymax_tl , ymax_br )]
323
328
x_dims += [min (xmin_tl , xmin_br ), max (xmax_tl , xmax_br )]
324
- y_dims += [min (ymin_tl , ymin_br ), max (ymax_tl , ymax_br )]
325
- x_dims += [min (xmin_tl , xmin_br ), max (xmax_tl , xmax_br )]
326
329
327
330
if not tmp_polygons .empty :
328
331
xmin , ymin , xmax , ymax = tmp_polygons .total_bounds
329
332
y_dims += [ymin , ymax ]
330
333
x_dims += [xmin , xmax ]
331
- y_dims += [ymin , ymax ]
332
- x_dims += [xmin , xmax ]
333
334
334
335
del tmp_points
335
336
del tmp_polygons
336
337
337
- extent [cs_name ][e_id ] = x_dims + y_dims
338
338
extent [cs_name ][e_id ] = x_dims + y_dims
339
339
340
- transformations = get_transformation (sdata .shapes [e_id ], to_coordinate_system = cs_name )
341
- transformations = _flatten_transformation_sequence (transformations )
342
340
transformations = get_transformation (sdata .shapes [e_id ], to_coordinate_system = cs_name )
343
341
transformations = _flatten_transformation_sequence (transformations )
344
342
@@ -358,16 +356,6 @@ def get_point_bb(
358
356
359
357
elif isinstance (t , sd .transformations .transformations .Affine ):
360
358
pass
361
- if has_points and cs_contents .query (f"cs == '{ cs_name } '" )["has_points" ][0 ]:
362
- for points_key in sdata .points :
363
- for e_id in element_ids :
364
- if points_key == e_id :
365
- tmp = sdata .points [points_key ]
366
- xmin = tmp ["x" ].min ().compute ()
367
- xmax = tmp ["x" ].max ().compute ()
368
- ymin = tmp ["y" ].min ().compute ()
369
- ymax = tmp ["y" ].max ().compute ()
370
- extent [cs_name ][e_id ] = [xmin , xmax , ymin , ymax ]
371
359
372
360
if has_points and cs_contents .query (f"cs == '{ cs_name } '" )["has_points" ][0 ]:
373
361
for points_key in sdata .points :
@@ -1137,3 +1125,40 @@ def _robust_transform(element: Any, cs: str) -> Any:
1137
1125
raise ValueError ("Unable to transform element." ) from e
1138
1126
1139
1127
return element
1128
+
1129
+
1130
+ def _split_multipolygon_into_outer_and_inner (mp : shapely .MultiPolygon ): # type: ignore
1131
+ # https://stackoverflow.com/a/21922058
1132
+ if len (mp .geoms ) > 1 :
1133
+ raise NotImplementedError ("Currently, lists of Polygons are not supported. Only Polygons with holes." )
1134
+
1135
+ geom = mp .geoms [0 ]
1136
+ if geom .type == "Polygon" :
1137
+ exterior_coords = geom .exterior .coords [:]
1138
+ interior_coords = []
1139
+ for interior in geom .interiors :
1140
+ interior_coords += interior .coords [:]
1141
+ elif geom .type == "MultiPolygon" :
1142
+ exterior_coords = []
1143
+ interior_coords = []
1144
+ for part in geom :
1145
+ epc = _split_multipolygon_into_outer_and_inner (part ) # Recursive call
1146
+ exterior_coords += epc ["exterior_coords" ]
1147
+ interior_coords += epc ["interior_coords" ]
1148
+ else :
1149
+ raise ValueError ("Unhandled geometry type: " + repr (geom .type ))
1150
+
1151
+ return interior_coords , exterior_coords
1152
+
1153
+
1154
+ def _make_patch_from_multipolygon (mp : shapely .MultiPolygon ) -> mpatches .PathPatch :
1155
+ # https://matplotlib.org/stable/gallery/shapes_and_collections/donut.html
1156
+
1157
+ inside , outside = _split_multipolygon_into_outer_and_inner (mp )
1158
+ codes = np .ones (len (inside ), dtype = mpath .Path .code_type ) * mpath .Path .LINETO
1159
+ codes [0 ] = mpath .Path .MOVETO
1160
+ vertices = np .concatenate ((outside , inside [::- 1 ]))
1161
+ all_codes = np .concatenate ((codes , codes ))
1162
+ path = mpath .Path (vertices , all_codes )
1163
+
1164
+ return mpatches .PathPatch (path )
0 commit comments