I have several blue and red polygons. I would like the red ones subtracted from the blue ones.
After this is done, some remaining polygons may have holes. I'd like those polygons with holes converted to polygons without holes.
What I've tried so far
from typing import TypedDict
from shapely.geometry import MultiPolygon, Polygon
class Coords(TypedDict):
x: list[float]
y: list[float]
def subtract_polygons(group_a: list[Coords], group_b: list[Coords]):
# Convert polygons to Shapely Polygon objects
polygons_a = [Polygon(zip(group["x"], group["y"])) for group in group_a]
polygons_b = [Polygon(zip(group["x"], group["y"])) for group in group_b]
# Create a "negative" polygon for the hole in group B
negative_polygons_b = [polygon.exterior for polygon in polygons_b]
# Subtract each polygon in polygons_b from each polygon in polygons_a
result_polygons = []
for polygon_a in polygons_a:
for negative_polygon_b in negative_polygons_b:
result_polygon = polygon_a.difference(negative_polygon_b)
result_polygons.append(result_polygon)
# Convert the resulting geometry to MultiPolygon
result_multipolygon = MultiPolygon(result_polygons)
print("polygons_a", polygons_a)
print("polygons_b", polygons_b)
print("negative_polygons_b", negative_polygons_b)
print("result_multipolygon", result_multipolygon)
group_a: list[Coords] = [
{"x": [100, 200, 200, 100, 100], "y": [100, 100, 200, 200, 100]},
{"x": [130, 230, 230, 130, 130], "y": [130, 130, 230, 230, 130]},
{"x": [180, 280, 280, 180, 180], "y": [180, 180, 280, 280, 180]},
]
group_b: list[Coords] = [
{"x": [150, 175, 175, 150, 150], "y": [150, 150, 175, 175, 150]},
{"x": [150, 250, 250, 150, 150], "y": [220, 220, 320, 320, 220]},
]
subtract_polygons(group_a, group_b)
Desired result:
Note that the little line is arbitrary. I just wanted to show that it's a single polygon, concave in this case, without a hole
Input and Actual Result:
The console output:
polygons_a [
<POLYGON ((100 100, 200 100, 200 200, 100 200, 100 100))>,
<POLYGON ((130 130, 230 130, 230 230, 130 230, 130 130))>,
<POLYGON ((180 180, 280 180, 280 280, 180 280, 180 180))>
]
polygons_b [
<POLYGON ((150 150, 175 150, 175 175, 150 175, 150 150))>,
<POLYGON ((150 220, 250 220, 250 320, 150 320, 150 220))>
]
negative_polygons_b [
<LINEARRING (150 150, 175 150, 175 175, 150 175, 150 150)>,
<LINEARRING (150 220, 250 220, 250 320, 150 320, 150 220)>
]
result_multipolygon MULTIPOLYGON (
((100 200, 200 200, 200 100, 100 100, 100 200)),
((100 200, 200 200, 200 100, 100 100, 100 200)),
((130 230, 230 230, 230 130, 130 130, 130 230)),
((230 130, 130 130, 130 230, 150 230, 230 230, 230 220, 230 130)),
((180 280, 280 280, 280 180, 180 180, 180 280)),
((280 280, 280 180, 180 180, 180 220, 180 280, 250 280, 280 280))
)


If I understand correctly what you want to accomplish, there were several problems in your script:
Corrected script:
Result: