This is an automated email from the ASF dual-hosted git repository.
petern pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sedona.git
The following commit(s) were added to refs/heads/master by this push:
new fc61ed2f03 [GH-2482] Geopandas: Implement intersection_all (#2486)
fc61ed2f03 is described below
commit fc61ed2f038f541e6740817a131e294eaf012fba
Author: Subham <[email protected]>
AuthorDate: Wed Nov 12 12:03:05 2025 +0530
[GH-2482] Geopandas: Implement intersection_all (#2486)
Co-authored-by: Peter Nguyen <[email protected]>
Co-authored-by: Gourav <[email protected]>
---
python/sedona/spark/geopandas/base.py | 25 +++++++++++++++++++---
python/sedona/spark/geopandas/geoseries.py | 16 ++++++++++----
python/tests/geopandas/test_geoseries.py | 15 ++++++++++++-
.../tests/geopandas/test_match_geopandas_series.py | 14 ++++++++++++
4 files changed, 62 insertions(+), 8 deletions(-)
diff --git a/python/sedona/spark/geopandas/base.py
b/python/sedona/spark/geopandas/base.py
index 53553f54ad..82dd7dd53b 100644
--- a/python/sedona/spark/geopandas/base.py
+++ b/python/sedona/spark/geopandas/base.py
@@ -905,6 +905,28 @@ class GeoFrame(metaclass=ABCMeta):
"""
return _delegate_to_geometry_column("union_all", self, method,
grid_size)
+ def intersection_all(self) -> BaseGeometry:
+ """Returns a geometry containing the intersection of all geometries in
+ the ``GeoSeries``.
+
+ Returns
+ -------
+ shapely.geometry.base.BaseGeometry
+
+ Examples
+ --------
+ >>> from sedona.spark.geopandas import GeoSeries
+ >>> from shapely.geometry import box
+ >>> s = GeoSeries([box(0, 0, 2, 2), box(1, 1, 3, 3)])
+ >>> s
+ 0 POLYGON ((2 0, 2 2, 0 2, 0 0, 2 0))
+ 1 POLYGON ((3 1, 3 3, 1 3, 1 1, 3 1))
+ dtype: geometry
+ >>> s.intersection_all()
+ <POLYGON ((1 1, 1 2, 2 2, 2 1, 1 1))>
+ """
+ return _delegate_to_geometry_column("intersection_all", self)
+
def crosses(self, other, align=None) -> ps.Series:
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that cross `other`.
@@ -2393,9 +2415,6 @@ class GeoFrame(metaclass=ABCMeta):
"""
return _delegate_to_geometry_column("union", self, other, align)
- def intersection_all(self):
- raise NotImplementedError("This method is not implemented yet.")
-
def contains(self, other, align=None):
"""Returns a ``Series`` of ``dtype('bool')`` with value ``True`` for
each aligned geometry that contains `other`.
diff --git a/python/sedona/spark/geopandas/geoseries.py
b/python/sedona/spark/geopandas/geoseries.py
index 0091c8614b..0530b62e3d 100644
--- a/python/sedona/spark/geopandas/geoseries.py
+++ b/python/sedona/spark/geopandas/geoseries.py
@@ -1126,6 +1126,18 @@ class GeoSeries(GeoFrame, pspd.Series):
geom = ps_series.iloc[0]
return geom
+ def intersection_all(self) -> BaseGeometry:
+ if len(self) == 0:
+ from shapely.geometry import GeometryCollection
+
+ return GeometryCollection()
+ spark_expr = sta.ST_Intersection_Aggr(self.spark.column)
+ tmp = self._query_geometry_column(spark_expr, returns_geom=False,
is_aggr=True)
+
+ ps_series = tmp.take([0])
+ geom = ps_series.iloc[0]
+ return geom
+
def crosses(self, other, align=None) -> pspd.Series:
# Sedona does not support GeometryCollection (errors), so we return
NULL for now to avoid error.
other_series, extended = self._make_series_of_val(other)
@@ -1363,10 +1375,6 @@ class GeoSeries(GeoFrame, pspd.Series):
keep_name=keep_name,
)
- def intersection_all(self):
- # Implementation of the abstract method.
- raise NotImplementedError("This method is not implemented yet.")
-
#
============================================================================
# Binary Predicates
#
============================================================================
diff --git a/python/tests/geopandas/test_geoseries.py
b/python/tests/geopandas/test_geoseries.py
index 28a0b36d6e..c1c373a2ff 100644
--- a/python/tests/geopandas/test_geoseries.py
+++ b/python/tests/geopandas/test_geoseries.py
@@ -1955,7 +1955,20 @@ e": "Feature", "properties": {}, "geometry": {"type":
"Point", "coordinates": [3
self.check_sgpd_equals_gpd(df_result, expected)
def test_intersection_all(self):
- pass
+ s = GeoSeries([box(0, 0, 2, 2), box(1, 1, 3, 3)])
+ result = s.intersection_all()
+ expected = Polygon([(1, 1), (1, 2), (2, 2), (2, 1), (1, 1)])
+ self.check_geom_equals(result, expected)
+
+ # Check that GeoDataFrame works too
+ df_result = s.to_geoframe().intersection_all()
+ self.check_geom_equals(df_result, expected)
+
+ # Empty GeoSeries
+ s = sgpd.GeoSeries([])
+ result = s.intersection_all()
+ expected = GeometryCollection()
+ self.check_geom_equals(result, expected)
def test_contains(self):
s = GeoSeries(
diff --git a/python/tests/geopandas/test_match_geopandas_series.py
b/python/tests/geopandas/test_match_geopandas_series.py
index 7ba09205b7..1a9f7ad4ef 100644
--- a/python/tests/geopandas/test_match_geopandas_series.py
+++ b/python/tests/geopandas/test_match_geopandas_series.py
@@ -860,6 +860,20 @@ class TestMatchGeopandasSeries(TestGeopandasBase):
gpd_result = gpd.GeoSeries([]).union_all()
self.check_geom_equals(sgpd_result, gpd_result)
+ def test_intersection_all(self):
+ if parse_version(gpd.__version__) < parse_version("1.0.0"):
+ pytest.skip("geopandas intersection_all requires version 1.0.0 or
higher")
+
+ lst = self.geoms
+ sgpd_result = GeoSeries(lst).intersection_all()
+ gpd_result = gpd.GeoSeries(lst).intersection_all()
+ self.check_geom_equals(sgpd_result, gpd_result)
+
+ # Ensure we have the same result for empty GeoSeries
+ sgpd_result = GeoSeries([]).intersection_all()
+ gpd_result = gpd.GeoSeries([]).intersection_all()
+ self.check_geom_equals(sgpd_result, gpd_result)
+
def test_crosses(self):
for geom, geom2 in self.pairs:
if self.contains_any_geom_collection(geom, geom2):