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 8de70083cc [GH-2485] Implement minimum_bounding_circle (#2488)
8de70083cc is described below
commit 8de70083cc405a22836538e52e24bb116086943f
Author: Krishna C Vemulakonda <[email protected]>
AuthorDate: Tue Nov 11 10:26:01 2025 -0700
[GH-2485] Implement minimum_bounding_circle (#2488)
Co-authored-by: Peter Nguyen <[email protected]>
---
python/sedona/spark/geopandas/base.py | 21 ++++++++++++++++--
python/sedona/spark/geopandas/geoseries.py | 9 +++++---
python/tests/geopandas/test_geopandas_base.py | 8 ++-----
python/tests/geopandas/test_geoseries.py | 25 +++++++++++++++++++++-
.../tests/geopandas/test_match_geopandas_series.py | 5 ++++-
5 files changed, 55 insertions(+), 13 deletions(-)
diff --git a/python/sedona/spark/geopandas/base.py
b/python/sedona/spark/geopandas/base.py
index 6e9eb39ee0..53553f54ad 100644
--- a/python/sedona/spark/geopandas/base.py
+++ b/python/sedona/spark/geopandas/base.py
@@ -722,8 +722,25 @@ class GeoFrame(metaclass=ABCMeta):
# def representative_point(self):
# raise NotImplementedError("This method is not implemented yet.")
- # def minimum_bounding_circle(self):
- # raise NotImplementedError("This method is not implemented yet.")
+ def minimum_bounding_circle(self):
+ """
+ Returns a ``GeoSeries`` containing the minimum bounding circle of each
geometry.
+ The minimum bounding circle is the smallest circle that completely
encloses
+ the geometry. The result is returned as a circular polygon
approximation.
+ Returns
+ -------
+ GeoSeries
+ A GeoSeries containing the minimum bounding circle for each
geometry.
+ Examples
+ --------
+ >>> from shapely.geometry import Polygon
+ >>> from sedona.spark.geopandas import GeoSeries
+ >>> gs = GeoSeries([Polygon([(0, 0), (3, 0), (3, 3), (0, 3)])])
+ >>> gs.minimum_bounding_circle
+ 0 POLYGON ((...))
+ dtype: geometry
+ """
+ return _delegate_to_geometry_column("minimum_bounding_circle", self)
# def minimum_bounding_radius(self):
# raise NotImplementedError("This method is not implemented yet.")
diff --git a/python/sedona/spark/geopandas/geoseries.py
b/python/sedona/spark/geopandas/geoseries.py
index 222419afd0..0091c8614b 100644
--- a/python/sedona/spark/geopandas/geoseries.py
+++ b/python/sedona/spark/geopandas/geoseries.py
@@ -1030,9 +1030,12 @@ class GeoSeries(GeoFrame, pspd.Series):
# Implementation of the abstract method.
raise NotImplementedError("This method is not implemented yet.")
- def minimum_bounding_circle(self):
- # Implementation of the abstract method.
- raise NotImplementedError("This method is not implemented yet.")
+ def minimum_bounding_circle(self) -> "GeoSeries":
+ spark_expr = stf.ST_MinimumBoundingCircle(self.spark.column)
+ return self._query_geometry_column(
+ spark_expr,
+ returns_geom=True,
+ )
def minimum_bounding_radius(self):
# Implementation of the abstract method.
diff --git a/python/tests/geopandas/test_geopandas_base.py
b/python/tests/geopandas/test_geopandas_base.py
index d2171bc1e1..de6ab14ad0 100644
--- a/python/tests/geopandas/test_geopandas_base.py
+++ b/python/tests/geopandas/test_geopandas_base.py
@@ -53,9 +53,7 @@ class TestGeopandasBase(TestBase):
# TODO chore: rename to check_sgpd_series_equals_gpd_series and change the
names in the geoseries tests
@classmethod
def check_sgpd_equals_gpd(
- cls,
- actual: GeoSeries,
- expected: gpd.GeoSeries,
+ cls, actual: GeoSeries, expected: gpd.GeoSeries, tolerance: float =
1e-2
):
assert isinstance(actual, GeoSeries)
assert isinstance(expected, gpd.GeoSeries)
@@ -69,9 +67,7 @@ class TestGeopandasBase(TestBase):
# Sometimes sedona and geopandas both return empty geometries but
of different types (e.g Point and Polygon)
elif a.is_empty and e.is_empty:
continue
- cls.assert_geometry_almost_equal(
- a, e, tolerance=1e-2
- ) # increased tolerance from 1e-6
+ cls.assert_geometry_almost_equal(a, e, tolerance)
assert_index_equal(actual.index.to_pandas(), expected.index)
diff --git a/python/tests/geopandas/test_geoseries.py
b/python/tests/geopandas/test_geoseries.py
index 30de7fd356..28a0b36d6e 100644
--- a/python/tests/geopandas/test_geoseries.py
+++ b/python/tests/geopandas/test_geoseries.py
@@ -1283,7 +1283,30 @@ e": "Feature", "properties": {}, "geometry": {"type":
"Point", "coordinates": [3
pass
def test_minimum_bounding_circle(self):
- pass
+ s = GeoSeries(
+ [
+ Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
+ LineString([(0, 0), (2, 0)]),
+ Point(0, 0),
+ None,
+ ]
+ )
+
+ expected = gpd.GeoSeries(
+ [
+ Polygon([(0, 0), (1, 0), (1, 1), (0, 1)]),
+ LineString([(0, 0), (2, 0)]),
+ Point(0, 0),
+ None,
+ ]
+ ).minimum_bounding_circle()
+
+ result = s.minimum_bounding_circle()
+ self.check_sgpd_equals_gpd(result, expected)
+
+ gdf = s.to_geoframe()
+ df_result = gdf.minimum_bounding_circle()
+ self.check_sgpd_equals_gpd(df_result, expected)
def test_minimum_bounding_radius(self):
pass
diff --git a/python/tests/geopandas/test_match_geopandas_series.py
b/python/tests/geopandas/test_match_geopandas_series.py
index 20718baf0c..7ba09205b7 100644
--- a/python/tests/geopandas/test_match_geopandas_series.py
+++ b/python/tests/geopandas/test_match_geopandas_series.py
@@ -775,7 +775,10 @@ class TestMatchGeopandasSeries(TestGeopandasBase):
pass
def test_minimum_bounding_circle(self):
- pass
+ for geom in self.geoms:
+ sgpd_result = GeoSeries(geom).minimum_bounding_circle()
+ gpd_result = gpd.GeoSeries(geom).minimum_bounding_circle()
+ self.check_sgpd_equals_gpd(sgpd_result, gpd_result, tolerance=0.5)
def test_minimum_bounding_radius(self):
pass