Repository: shapely/shapely

Issue:
split ``Polygon`` by ``MultiLineString``
This code can be successfully used also for splitting `Polygon` by `MultiLineString`.

https://github.com/shapely/shapely/blob/f1b98cf1241e5d31eff6deefa1419507221d0c2f/shapely/ops.py#L365-L384


Discussion:

Patch:
diff --git a/CHANGES.txt b/CHANGES.txt
index b6f8dca1d..b9f53d77a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -32,6 +32,7 @@ Improvements:
 - Add option on ``invalid="fix"`` to ``from_wkb`` and ``from_wkt`` (#2094)
 - Add function ``minimum_clearance_line`` (#2106)
 - Add function ``maximum_inscribed_circle`` (#1307)
+- Add support to split polygons by multilinestrings (#2206)
 - Add function ``orient_polygons`` (#2147)
 
 Breaking changes in GEOS 3.12:
diff --git a/shapely/ops.py b/shapely/ops.py
index 5cc0a4c29..5b6a0ef97 100644
--- a/shapely/ops.py
+++ b/shapely/ops.py
@@ -367,8 +367,8 @@ def _split_polygon_with_line(poly, splitter):
         """Split a Polygon with a LineString."""
         if not isinstance(poly, Polygon):
             raise GeometryTypeError("First argument must be a Polygon")
-        if not isinstance(splitter, LineString):
-            raise GeometryTypeError("Second argument must be a LineString")
+        if not isinstance(splitter, (LineString, MultiLineString)):
+            raise GeometryTypeError("Second argument must be a (Multi)LineString")
 
         union = poly.boundary.union(splitter)
 
@@ -539,7 +539,7 @@ def split(geom, splitter):
                 )
 
         elif geom.geom_type == "Polygon":
-            if splitter.geom_type == "LineString":
+            if splitter.geom_type in ("LineString", "MultiLineString"):
                 split_func = SplitOp._split_polygon_with_line
             else:
                 raise GeometryTypeError(


Test patch:
diff --git a/shapely/tests/legacy/test_split.py b/shapely/tests/legacy/test_split.py
index 5ef325b50..0bbf4eaf1 100644
--- a/shapely/tests/legacy/test_split.py
+++ b/shapely/tests/legacy/test_split.py
@@ -51,12 +51,17 @@ class TestSplitPolygon(TestSplitGeometry):
     )
 
     def test_split_poly_with_line(self):
-        # crossing at 2 points --> return 2 segments
+        # crossing at 2 points --> return 2 polygons
         splitter = LineString([(1, 3), (1, -3)])
         self.helper(self.poly_simple, splitter, 2)
         self.helper(self.poly_hole, splitter, 2)
 
-        # touching the boundary--> return equal
+        # crossing twice with one linestring --> return 3 polygons
+        splitter = LineString([(1, 3), (1, -3), (1.7, -3), (1.7, 3)])
+        self.helper(self.poly_simple, splitter, 3)
+        self.helper(self.poly_hole, splitter, 3)
+
+        # touching the boundary --> return equal
         splitter = LineString([(0, 2), (5, 2)])
         self.helper(self.poly_simple, splitter, 1)
         self.helper(self.poly_hole, splitter, 1)
@@ -71,6 +76,34 @@ def test_split_poly_with_line(self):
         self.helper(self.poly_simple, splitter, 1)
         self.helper(self.poly_hole, splitter, 1)
 
+    def test_split_poly_with_multiline(self):
+        # crossing twice with a multilinestring --> return 3 polygons
+        splitter = MultiLineString([[(0.2, 3), (0.2, -3)], [(1.7, -3), (1.7, 3)]])
+        self.helper(self.poly_simple, splitter, 3)
+        self.helper(self.poly_hole, splitter, 3)
+
+        # crossing twice with a cross multilinestring --> return 4 polygons
+        splitter = MultiLineString([[(0.2, 3), (0.2, -3)], [(-3, 1), (3, 1)]])
+        self.helper(self.poly_simple, splitter, 4)
+        self.helper(self.poly_hole, splitter, 4)
+
+        # cross once, touch the boundary once --> return 2 polygons
+        splitter = MultiLineString([[(0.2, 3), (0.2, -3)], [(0, 2), (5, 2)]])
+        self.helper(self.poly_simple, splitter, 2)
+        self.helper(self.poly_hole, splitter, 2)
+
+        # cross once, inside the polygon once --> return 2 polygons
+        splitter = MultiLineString(
+            [[(0.2, 3), (0.2, -3)], [(1.2, 1.2), (1.7, 1.7), (3, 2)]]
+        )
+        self.helper(self.poly_simple, splitter, 2)
+        self.helper(self.poly_hole, splitter, 2)
+
+        # cross once, outside the polygon once --> return 2 polygons
+        splitter = MultiLineString([[(0.2, 3), (0.2, -3)], [(0, 3), (3, 3), (3, 0)]])
+        self.helper(self.poly_simple, splitter, 2)
+        self.helper(self.poly_hole, splitter, 2)
+
     def test_split_poly_with_other(self):
         with pytest.raises(GeometryTypeError):
             split(self.poly_simple, Point(1, 1))