This is an automated email from the ASF dual-hosted git repository.
jiayu 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 56f196988f [GH-2377] Implement `is_closed` (#2378)
56f196988f is described below
commit 56f196988fffb0f3fed0bf1c0f4153d41b976a6c
Author: Yunchi Pang <[email protected]>
AuthorDate: Thu Oct 9 20:32:10 2025 -0700
[GH-2377] Implement `is_closed` (#2378)
Co-authored-by: Peter Nguyen <[email protected]>
---
python/sedona/spark/geopandas/base.py | 37 ++++++++++++++++++++--
python/sedona/spark/geopandas/geoseries.py | 11 +++----
python/tests/geopandas/test_geoseries.py | 15 ++++++++-
.../tests/geopandas/test_match_geopandas_series.py | 8 ++++-
4 files changed, 60 insertions(+), 11 deletions(-)
diff --git a/python/sedona/spark/geopandas/base.py
b/python/sedona/spark/geopandas/base.py
index 471a245eac..912b3c35f4 100644
--- a/python/sedona/spark/geopandas/base.py
+++ b/python/sedona/spark/geopandas/base.py
@@ -390,9 +390,40 @@ class GeoFrame(metaclass=ABCMeta):
# def is_ccw(self):
# raise NotImplementedError("This method is not implemented yet.")
- # @property
- # def is_closed(self):
- # raise NotImplementedError("This method is not implemented yet.")
+ @property
+ def is_closed(self):
+ """Return a ``Series`` of ``dtype('bool')`` with value ``True`` if a
+ LineString's or LinearRing's first and last points are equal.
+
+ Returns False for any other geometry type.
+
+ Examples
+ --------
+ >>> from sedona.spark.geopandas import GeoSeries
+ >>> from shapely.geometry import LineString, Point, Polygon
+ >>> s = GeoSeries(
+ ... [
+ ... LineString([(0, 0), (1, 1), (0, 1), (0, 0)]),
+ ... LineString([(0, 0), (1, 1), (0, 1)]),
+ ... Polygon([(0, 0), (0, 1), (1, 1), (0, 0)]),
+ Point(3, 3)
+ ... ]
+ ... )
+ >>> s
+ 0 LINESTRING (0 0, 1 1, 0 1, 0 0)
+ 1 LINESTRING (0 0, 1 1, 0 1)
+ 2 POLYGON ((0 0, 0 1, 1 1, 0 0))
+ 3 POINT (3 3)
+ dtype: geometry
+
+ >>> s.is_closed
+ 0 True
+ 1 False
+ 2 False
+ 3 False
+ dtype: bool
+ """
+ return _delegate_to_geometry_column("is_closed", self)
@property
def has_z(self):
diff --git a/python/sedona/spark/geopandas/geoseries.py
b/python/sedona/spark/geopandas/geoseries.py
index 21bb6e4b1d..55af19673f 100644
--- a/python/sedona/spark/geopandas/geoseries.py
+++ b/python/sedona/spark/geopandas/geoseries.py
@@ -976,13 +976,12 @@ class GeoSeries(GeoFrame, pspd.Series):
@property
def is_closed(self):
- # Implementation of the abstract method.
- raise NotImplementedError(
- _not_implemented_error(
- "is_closed",
- "Tests if LineString geometries are closed (start equals end
point).",
- )
+ spark_expr = stf.ST_IsClosed(self.spark.column)
+ result = self._query_geometry_column(
+ spark_expr,
+ returns_geom=False,
)
+ return _to_bool(result)
@property
def has_z(self) -> pspd.Series:
diff --git a/python/tests/geopandas/test_geoseries.py
b/python/tests/geopandas/test_geoseries.py
index 614e4dc112..d9c5fd7db3 100644
--- a/python/tests/geopandas/test_geoseries.py
+++ b/python/tests/geopandas/test_geoseries.py
@@ -885,7 +885,20 @@ e": "Feature", "properties": {}, "geometry": {"type":
"Point", "coordinates": [3
pass
def test_is_closed(self):
- pass
+ s = GeoSeries(
+ [
+ LineString([(0, 0), (1, 1), (1, -1)]),
+ LineString([(0, 0), (1, 1), (1, -1), (0, 0)]),
+ LinearRing([(0, 0), (1, 1), (1, -1)]),
+ ]
+ )
+ result = s.is_closed
+ expected = pd.Series([False, True, True])
+ self.check_pd_series_equal(result, expected)
+
+ # Check that GeoDataFrame works too
+ result = s.to_geoframe().is_closed
+ self.check_pd_series_equal(result, expected)
def test_has_z(self):
s = sgpd.GeoSeries(
diff --git a/python/tests/geopandas/test_match_geopandas_series.py
b/python/tests/geopandas/test_match_geopandas_series.py
index 17cae5b95e..250c133eef 100644
--- a/python/tests/geopandas/test_match_geopandas_series.py
+++ b/python/tests/geopandas/test_match_geopandas_series.py
@@ -588,7 +588,13 @@ class TestMatchGeopandasSeries(TestGeopandasBase):
pass
def test_is_closed(self):
- pass
+ if parse_version(gpd.__version__) < parse_version("1.0.0"):
+ pytest.skip("geopandas is_closed requires version 1.0.0 or higher")
+ # is_closed is only meaningful for linestrings so we use
self.linestrings instead of self.geoms
+ for geom in self.linestrings:
+ sgpd_result = GeoSeries(geom).is_closed
+ gpd_result = gpd.GeoSeries(geom).is_closed
+ self.check_pd_series_equal(sgpd_result, gpd_result)
def test_has_z(self):
for geom in self.geoms: