summaryrefslogtreecommitdiff
path: root/lib/utils
diff options
context:
space:
mode:
authorKaalleen <36401965+kaalleen@users.noreply.github.com>2024-01-25 17:54:08 +0100
committerGitHub <noreply@github.com>2024-01-25 17:54:08 +0100
commit2677c30a0f210d14586c83016f45e5a75fb9d647 (patch)
tree04a91f0ee9a7f5723abe362890006ed383ddf74e /lib/utils
parentf44eff9e74b36507cd512aea7aa6f4d698a374d5 (diff)
Second chance for invalid fill stitch graphs (#2643)
Diffstat (limited to 'lib/utils')
-rw-r--r--lib/utils/clamp_path.py9
-rw-r--r--lib/utils/geometry.py62
2 files changed, 43 insertions, 28 deletions
diff --git a/lib/utils/clamp_path.py b/lib/utils/clamp_path.py
index 432f618a..f6db66a8 100644
--- a/lib/utils/clamp_path.py
+++ b/lib/utils/clamp_path.py
@@ -56,8 +56,10 @@ def adjust_line_end(line, end):
def find_border(polygon, point):
+ """Finds subpath of polygon which intersects with the point.
+ Ignores small border fragments"""
for border in polygon.interiors:
- if border.intersects(point):
+ if border.length > 0.1 and border.intersects(point):
return border
else:
return polygon.exterior
@@ -76,7 +78,10 @@ def clamp_path_to_polygon(path, polygon):
# This splits the path at the points where it intersects with the polygon
# border and returns the pieces in the same order as the original path.
- split_path = ensure_geometry_collection(LineString(path).difference(polygon.boundary))
+ try:
+ split_path = ensure_geometry_collection(LineString(path).difference(polygon.boundary))
+ except FloatingPointError:
+ return path
if len(split_path.geoms) == 1:
# The path never intersects with the polygon, so it's entirely inside.
diff --git a/lib/utils/geometry.py b/lib/utils/geometry.py
index 6ef0d439..47347a02 100644
--- a/lib/utils/geometry.py
+++ b/lib/utils/geometry.py
@@ -7,7 +7,8 @@ import math
import typing
import numpy
-from shapely.geometry import LineString, LinearRing, MultiLineString, MultiPolygon, MultiPoint, GeometryCollection
+from shapely.geometry import (GeometryCollection, LinearRing, LineString,
+ MultiLineString, MultiPolygon)
from shapely.geometry import Point as ShapelyPoint
@@ -102,47 +103,56 @@ def reverse_line_string(line_string):
return LineString(line_string.coords[::-1])
-def ensure_multi_line_string(thing):
- """Given either a MultiLineString, a single LineString or GeometryCollection, return a MultiLineString"""
+def ensure_multi_line_string(thing, min_size=0):
+ """Given a shapely geometry, return a MultiLineString"""
+ multi_line_string = MultiLineString()
if thing.is_empty:
- return thing
- if thing.geom_type == "LineString":
- return MultiLineString([thing])
- if thing.geom_type == "GeometryCollection":
+ return multi_line_string
+ if thing.geom_type == "MultiLineString":
+ multi_line_string = thing
+ elif thing.geom_type == "LineString":
+ multi_line_string = MultiLineString([thing])
+ elif thing.geom_type == "GeometryCollection":
multilinestring = []
for line in thing.geoms:
if line.geom_type == "LineString":
multilinestring.append(line)
- if multilinestring:
- return MultiLineString(multilinestring)
- return thing
+ multi_line_string = MultiLineString(multilinestring)
+ if min_size > 0:
+ multi_line_string = MultiLineString([line for line in multi_line_string.geoms if line.length > min_size])
+ return multi_line_string
def ensure_geometry_collection(thing):
- """Given either some kind of geometry or a GeometryCollection, return a GeometryCollection"""
-
- if isinstance(thing, (MultiLineString, MultiPolygon, MultiPoint)):
- return GeometryCollection(thing.geoms)
- elif isinstance(thing, GeometryCollection):
+ """Given a shapely geometry, return a GeometryCollection"""
+ if thing.is_empty:
+ return GeometryCollection()
+ if thing.geom_type == "GeometryCollection":
return thing
- else:
- return GeometryCollection([thing])
+ if thing.geom_type in ["MultiLineString", "MultiPolygon", "MultiPoint"]:
+ return GeometryCollection(thing.geoms)
+ # LineString, Polygon, Point
+ return GeometryCollection([thing])
-def ensure_multi_polygon(thing):
- """Given either a MultiPolygon or a single Polygon, return a MultiPolygon"""
+def ensure_multi_polygon(thing, min_size=0):
+ """Given a shapely geometry, return a MultiPolygon"""
+ multi_polygon = MultiPolygon()
if thing.is_empty:
- return thing
- if thing.geom_type == "Polygon":
- return MultiPolygon([thing])
- if thing.geom_type == "GeometryCollection":
+ return multi_polygon
+ if thing.geom_type == "MultiPolygon":
+ multi_polygon = thing
+ elif thing.geom_type == "Polygon":
+ multi_polygon = MultiPolygon([thing])
+ elif thing.geom_type == "GeometryCollection":
multipolygon = []
for polygon in thing.geoms:
if polygon.geom_type == "Polygon":
multipolygon.append(polygon)
- if multipolygon:
- return MultiPolygon(multipolygon)
- return thing
+ multi_polygon = MultiPolygon(multipolygon)
+ if min_size > 0:
+ multi_polygon = MultiPolygon([polygon for polygon in multi_polygon.geoms if polygon.area > min_size])
+ return multi_polygon
def cut_path(points, length):