@@ -37,6 +37,7 @@ class Mask(Geometry):
37
37
38
38
@property
39
39
def geometry (self ) -> Dict [str , Tuple [int , int , int ]]:
40
+ # Extract mask contours and build geometry
40
41
mask = self .draw (color = 1 )
41
42
contours , hierarchy = cv2 .findContours (
42
43
image = mask , mode = cv2 .RETR_TREE , method = cv2 .CHAIN_APPROX_NONE
@@ -62,7 +63,38 @@ def geometry(self) -> Dict[str, Tuple[int, int, int]]:
62
63
if not holes .is_valid :
63
64
holes = holes .buffer (0 )
64
65
65
- return external_polygons .difference (holes ).__geo_interface__
66
+ # Get geometry result
67
+ result_geometry = external_polygons .difference (holes )
68
+
69
+ # Ensure consistent MultiPolygon format across shapely versions
70
+ if hasattr (result_geometry , 'geom_type' ) and result_geometry .geom_type == 'Polygon' :
71
+ from shapely .geometry import MultiPolygon
72
+ result_geometry = MultiPolygon ([result_geometry ])
73
+
74
+ geometry_dict = result_geometry .__geo_interface__
75
+ return self ._normalize_coordinates (geometry_dict )
76
+
77
+ def _normalize_coordinates (self , geometry_dict ):
78
+ """Normalize coordinates to ensure consistent tuple format across shapely versions"""
79
+
80
+ def ensure_tuple_coords (obj ):
81
+ """Recursively ensure coordinate pairs are tuples"""
82
+ if isinstance (obj , (list , tuple )):
83
+ # Check if this is a coordinate pair [x, y]
84
+ if len (obj ) == 2 and all (isinstance (x , (int , float )) for x in obj ):
85
+ return (float (obj [0 ]), float (obj [1 ]))
86
+ else :
87
+ # Recursively process nested structures, preserving list/tuple types
88
+ if isinstance (obj , list ):
89
+ return [ensure_tuple_coords (item ) for item in obj ]
90
+ else :
91
+ return tuple (ensure_tuple_coords (item ) for item in obj )
92
+ return obj
93
+
94
+ result = geometry_dict .copy ()
95
+ if 'coordinates' in result :
96
+ result ['coordinates' ] = ensure_tuple_coords (result ['coordinates' ])
97
+ return result
66
98
67
99
def draw (
68
100
self ,
0 commit comments