Skip to content

Commit 1bd8d55

Browse files
authored
add union fallback to voronoi_frames (#777)
* add union fallback to voronoi_frames * add test * fix the test
1 parent 9479b34 commit 1bd8d55

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

libpysal/cg/tests/test_voronoi.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,75 @@ def test_deprecated_clip(self, clip):
220220
match=f"The '{clip}' option for the 'clip' parameter is deprecated",
221221
):
222222
voronoi_frames(self.points2, clip=clip)
223+
224+
# GH776
225+
def test_union_fallback(self):
226+
data = {
227+
"type": "FeatureCollection",
228+
"features": [
229+
{
230+
"id": "0",
231+
"type": "Feature",
232+
"properties": {},
233+
"geometry": {
234+
"type": "Polygon",
235+
"coordinates": (
236+
(
237+
(575361.23, 115796.88),
238+
(575364.42, 115767.29),
239+
(575343.54, 115765.04),
240+
(575341.61, 115782.98),
241+
(575347.84, 115783.65),
242+
(575348.94, 115773.43),
243+
(575356.99, 115774.3),
244+
(575354.63, 115796.16),
245+
(575361.23, 115796.88),
246+
),
247+
),
248+
},
249+
"bbox": (575341.61, 115765.04, 575364.42, 115796.88),
250+
},
251+
{
252+
"id": "1",
253+
"type": "Feature",
254+
"properties": {},
255+
"geometry": {
256+
"type": "Polygon",
257+
"coordinates": (
258+
(
259+
(575365.47, 115821.98),
260+
(575375.28, 115823.68),
261+
(575379.67, 115798.26),
262+
(575369.86, 115796.56),
263+
(575365.47, 115821.98),
264+
),
265+
),
266+
},
267+
"bbox": (575365.47, 115796.56, 575379.67, 115823.68),
268+
},
269+
{
270+
"id": "2",
271+
"type": "Feature",
272+
"properties": {},
273+
"geometry": {
274+
"type": "Polygon",
275+
"coordinates": (
276+
(
277+
(575344.39, 115825.85),
278+
(575343.47, 115831.29),
279+
(575348.91, 115832.21),
280+
(575349.83, 115826.77),
281+
(575344.39, 115825.85),
282+
),
283+
),
284+
},
285+
"bbox": (575343.47, 115825.85, 575349.83, 115832.21),
286+
},
287+
],
288+
"bbox": (575341.61, 115765.04, 575379.67, 115832.21),
289+
}
290+
geometry = gpd.GeoDataFrame.from_features(data)
291+
vf = voronoi_frames(
292+
geometry, shrink=0.4, segment=0.5, return_input=False, as_gdf=False
293+
)
294+
assert vf.shape == (3,)

libpysal/cg/voronoi.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import shapely
1414
from packaging.version import Version
1515
from scipy.spatial import Voronoi
16+
from shapely.errors import GEOSException
1617

1718
__author__ = "Serge Rey <[email protected]>"
1819

@@ -455,7 +456,7 @@ def voronoi_frames(
455456
polygons = (
456457
polygons.iloc[ids_polygons]
457458
.groupby(objects.index.take(ids_objects))
458-
.agg(shapely.coverage_union_all)
459+
.agg(_union_with_fallback)
459460
)
460461
if geometry.crs is not None:
461462
polygons = polygons.set_crs(geometry.crs)
@@ -497,6 +498,20 @@ def voronoi_frames(
497498
return polygons
498499

499500

501+
def _union_with_fallback(arr):
502+
"""
503+
Coverage union is finnicky with floating point precision and tends to occasionally
504+
raise an error from within GEOS we have no control over. It is not a data issue
505+
typically. Falling back to unary union if that happens.
506+
"""
507+
try:
508+
r = shapely.coverage_union_all(arr)
509+
except GEOSException:
510+
r = shapely.union_all(arr)
511+
512+
return r
513+
514+
500515
def _get_limit(points, clip):
501516
if isinstance(clip, shapely.Geometry):
502517
return clip

0 commit comments

Comments
 (0)