BUG: Fix handling of empty shapely geometries in clip() (#2595)

This commit is contained in:
Fred Bunt
2022-10-22 15:54:26 +02:00
committed by GitHub
parent cb310dff59
commit 59c1f7f3e3
3 changed files with 32 additions and 1 deletions
+1
View File
@@ -27,6 +27,7 @@ Deprecations and compatibility notes:
- resolve ``matplotlib.cm`` warning in ``.explore()`` (#2596)
Bug fixes:
- Fix cryptic error message in ``geopandas.clip()`` when clipping with an empty geometry (#2589)
- Accessing `gdf.geometry` where the active geometry column is missing, and a column named `"geometry"` is present
will now raise an `AttributeError`, rather than returning `gdf["geometry"]` (#2575)
- Combining GeoSeries/GeoDataFrames with ``pandas.concat`` will no longer silently
+6 -1
View File
@@ -7,6 +7,7 @@ A module to clip vector data using GeoPandas.
"""
import warnings
import numpy as np
import pandas.api.types
from shapely.geometry import Polygon, MultiPolygon, box
@@ -168,7 +169,11 @@ def clip(gdf, mask, keep_geom_type=False):
elif mask_is_list_like:
box_mask = mask
else:
box_mask = mask.bounds
# Avoid empty tuple returned by .bounds when geometry is empty. A tuple of
# all nan values is consistent with the behavior of
# {GeoSeries, GeoDataFrame}.total_bounds for empty geometries.
# TODO(shapely) can simpely use mask.bounds once relying on Shapely 2.0
box_mask = mask.bounds if not mask.is_empty else (np.nan,) * 4
box_gdf = gdf.total_bounds
if not (
((box_mask[0] <= box_gdf[2]) and (box_gdf[0] <= box_mask[2]))
+25
View File
@@ -450,3 +450,28 @@ def test_clip_single_multipoly_no_extra_geoms(
multi = buffered_locations.dissolve(by="type").reset_index()
clipped = clip(multi, masks)
assert clipped.geom_type[0] == "Polygon"
@pytest.mark.filterwarnings("ignore:All-NaN slice encountered")
@pytest.mark.parametrize(
"mask",
[
Polygon(),
(np.nan,) * 4,
(np.nan, 0, np.nan, 1),
GeoSeries([Polygon(), Polygon()], crs="EPSG:3857"),
GeoSeries([Polygon(), Polygon()], crs="EPSG:3857").to_frame(),
GeoSeries([], crs="EPSG:3857"),
GeoSeries([], crs="EPSG:3857").to_frame(),
],
)
def test_clip_empty_mask(buffered_locations, mask):
"""Test that clipping with empty mask returns an empty result."""
clipped = clip(buffered_locations, mask)
assert_geodataframe_equal(
clipped,
GeoDataFrame([], columns=["geometry", "type"], crs="EPSG:3857"),
check_index_type=False,
)
clipped = clip(buffered_locations.geometry, mask)
assert_geoseries_equal(clipped, GeoSeries([], crs="EPSG:3857"))