don't crash
This commit is contained in:
parent
aab85fe32e
commit
d6d20ccd37
13 changed files with 2630 additions and 3924 deletions
|
|
@ -263,6 +263,43 @@ def _postcode_buffers(
|
|||
return circles, shapely.STRtree(circles)
|
||||
|
||||
|
||||
# 0.1 mm in the BNG working CRS (EPSG:27700) — far below survey resolution; the
|
||||
# same grid the postcode_boundaries overlay uses.
|
||||
_OVERLAY_GRID_M = 1e-4
|
||||
|
||||
|
||||
def _robust_intersection_area(a: np.ndarray, b: np.ndarray) -> np.ndarray:
|
||||
"""Vectorized ``area(a[i] ∩ b[i])`` that survives GEOS robustness failures.
|
||||
|
||||
External Forest Research TOW/NFI polygons are occasionally invalid
|
||||
(self-intersections), and a single bad polygon makes the batched
|
||||
``shapely.intersection`` raise ``TopologyException: side location conflict``,
|
||||
aborting the whole run. The fast path is the raw batched overlay — unchanged,
|
||||
full-speed, when the data is clean — and only a failure triggers repair.
|
||||
|
||||
The repair deliberately uses a *plain* overlay rather than the fixed-precision
|
||||
(``grid_size``) one: ``make_valid`` can emit a mixed-dimension
|
||||
``GeometryCollection`` (a polygon plus a dangling line), which OverlayNG
|
||||
rejects with ``Overlay input is mixed-dimension`` — whereas a plain overlay
|
||||
accepts it, and its non-polygonal debris has zero area and is dropped by the
|
||||
``clipped_area > 0`` filter downstream anyway. A final pointwise coordinate
|
||||
snap (which never raises) collapses the near-coincident edges behind any
|
||||
residual full-precision robustness failure.
|
||||
"""
|
||||
try:
|
||||
return shapely.area(shapely.intersection(a, b))
|
||||
except shapely.errors.GEOSException:
|
||||
pass
|
||||
a = shapely.make_valid(a)
|
||||
b = shapely.make_valid(b)
|
||||
try:
|
||||
return shapely.area(shapely.intersection(a, b))
|
||||
except shapely.errors.GEOSException:
|
||||
a = shapely.make_valid(shapely.set_precision(a, _OVERLAY_GRID_M, mode="pointwise"))
|
||||
b = shapely.make_valid(shapely.set_precision(b, _OVERLAY_GRID_M, mode="pointwise"))
|
||||
return shapely.area(shapely.intersection(a, b))
|
||||
|
||||
|
||||
def _accumulate_clipped_area(
|
||||
geoms: np.ndarray,
|
||||
circles: np.ndarray,
|
||||
|
|
@ -294,8 +331,8 @@ def _accumulate_clipped_area(
|
|||
if geom_index.size == 0:
|
||||
return
|
||||
|
||||
clipped_area = shapely.area(
|
||||
shapely.intersection(geoms[geom_index], circles[postcode_index])
|
||||
clipped_area = _robust_intersection_area(
|
||||
geoms[geom_index], circles[postcode_index]
|
||||
)
|
||||
positive = clipped_area > 0
|
||||
geom_index = geom_index[positive]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue