Skip to content

Commit 76cf03c

Browse files
committed
Improved robustness to invalid polygons.
1 parent e04dd80 commit 76cf03c

3 files changed

Lines changed: 37 additions & 25 deletions

File tree

buildingregulariser/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.2.3"
1+
__version__ = "0.2.4"

buildingregulariser/coordinator.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import warnings
12
from functools import partial
23
from multiprocessing import Pool, cpu_count
34
from typing import Optional, Union
@@ -148,8 +149,16 @@ def regularize_geodataframe(
148149
""" # noqa: E501, W505
149150
# Make a copy to avoid modifying the original GeoDataFrame
150151
result_geodataframe = geodataframe.copy()
151-
# Fix invalid geometries
152-
result_geodataframe.geometry = result_geodataframe.make_valid()
152+
# Check for invalid geometries and warn user of potential errors
153+
if not result_geodataframe.is_valid.all():
154+
warnings.warn(
155+
"Found invalid geometries in the GeoDataFrame. "
156+
"Regularization may fail for these polygons. "
157+
"Consider cleaning the geometries before regularization.",
158+
stacklevel=2,
159+
)
160+
result_geodataframe.geometry = result_geodataframe.make_valid()
161+
153162
# Explode the geometries to process them individually
154163
result_geodataframe = result_geodataframe.explode(ignore_index=True)
155164

@@ -168,7 +177,6 @@ def regularize_geodataframe(
168177
diagonal_threshold_reduction=diagonal_threshold_reduction,
169178
allow_circles=allow_circles,
170179
circle_threshold=circle_threshold,
171-
include_metadata=include_metadata,
172180
simplify=simplify,
173181
simplify_tolerance=simplify_tolerance,
174182
)

buildingregulariser/regularization.py

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -752,7 +752,6 @@ def regularize_single_polygon(
752752
diagonal_threshold_reduction: float,
753753
allow_circles: bool,
754754
circle_threshold: float,
755-
include_metadata: bool,
756755
simplify: bool,
757756
simplify_tolerance: float,
758757
) -> dict[str, Any]:
@@ -776,9 +775,6 @@ def regularize_single_polygon(
776775
circle_threshold : float
777776
Intersection over Union (IoU) threshold used for circle detection
778777
Value between 0 and 1.
779-
include_metadata : bool
780-
If True, includes metadata about the regularization process
781-
in the output.
782778
783779
Returns:
784780
--------
@@ -792,14 +788,17 @@ def regularize_single_polygon(
792788
stacklevel=2,
793789
)
794790
return {"geometry": polygon, "iou": 0, "main_direction": 0}
795-
polygon = preprocess_polygon(
791+
if polygon.is_empty:
792+
# Return empty polygon if input is empty
793+
return {"geometry": polygon, "iou": 0, "main_direction": 0}
794+
795+
simple_polygon = preprocess_polygon(
796796
polygon,
797797
simplify=simplify,
798798
simplify_tolerance=simplify_tolerance,
799799
).buffer(0)
800800

801-
exterior_coordinates = np.array(polygon.exterior.coords)
802-
# append the first point to the end to close the polygon
801+
exterior_coordinates = np.array(simple_polygon.exterior.coords)
803802

804803
regularized_exterior, main_direction = regularize_coordinate_array(
805804
coordinates=exterior_coordinates,
@@ -811,18 +810,18 @@ def regularize_single_polygon(
811810
if allow_circles:
812811
radius = np.sqrt(polygon.area / np.pi)
813812
perfect_circle = polygon.centroid.buffer(radius, quad_segs=42)
814-
# Check if the polygon is close to a circle using iou
815-
iou = (
813+
# Check if the polygon is close to a circle using circle_iou
814+
circle_iou = (
816815
perfect_circle.intersection(polygon).area
817816
/ perfect_circle.union(polygon).area
818817
)
819-
if iou > circle_threshold:
818+
if circle_iou > circle_threshold:
820819
# If the polygon is close to a circle, return the perfect circle
821820
regularized_exterior = np.array(perfect_circle.exterior.coords, dtype=float)
822821

823822
# Handle interior rings (holes)
824823
regularized_interiors: List[np.ndarray] = []
825-
for interior in polygon.interiors:
824+
for interior in simple_polygon.interiors:
826825
interior_coordinates = np.array(interior.coords)
827826
regularized_interior, _ = regularize_coordinate_array(
828827
coordinates=interior_coordinates,
@@ -840,19 +839,24 @@ def regularize_single_polygon(
840839

841840
# Create regularized polygon
842841
regularized_polygon = Polygon(exterior_ring, interior_rings).buffer(0)
843-
if include_metadata:
844-
final_iou = (
845-
regularized_polygon.intersection(polygon).area
846-
/ regularized_polygon.union(polygon).area
842+
final_iou = (
843+
regularized_polygon.intersection(polygon).area
844+
/ regularized_polygon.union(polygon).area
845+
)
846+
if final_iou < 0.1:
847+
warnings.warn(
848+
"Regularized polygon has low IoU with original polygon. "
849+
"Returning original polygon.",
850+
stacklevel=2,
847851
)
852+
return {"geometry": polygon, "iou": 0, "main_direction": 0}
848853
else:
849-
final_iou = 0
854+
return {
855+
"geometry": regularized_polygon,
856+
"iou": final_iou,
857+
"main_direction": main_direction,
858+
}
850859

851-
return {
852-
"geometry": regularized_polygon,
853-
"iou": final_iou,
854-
"main_direction": main_direction,
855-
}
856860
except Exception as e:
857861
# If there's an error creating the polygon, return the original
858862
warnings.warn(

0 commit comments

Comments
 (0)