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 7005e6e925 [GH-2231] Implement is_ring (#2232)
7005e6e925 is described below

commit 7005e6e925b81faa10aa144c1426fabdc4520338
Author: Peter Nguyen <[email protected]>
AuthorDate: Tue Aug 5 20:49:56 2025 -0700

    [GH-2231] Implement is_ring (#2232)
    
    * Implement is_ring
    
    * Also add test for geodataframe
    
    * Update python/tests/geopandas/test_match_geopandas_series.py
    
    Co-authored-by: Copilot <[email protected]>
    
    ---------
    
    Co-authored-by: Jia Yu <[email protected]>
    Co-authored-by: Copilot <[email protected]>
---
 python/sedona/spark/geopandas/base.py              | 38 ++++++++++++++++++++--
 python/sedona/spark/geopandas/geoseries.py         | 10 +++---
 python/tests/geopandas/test_geoseries.py           | 15 ++++++++-
 .../tests/geopandas/test_match_geopandas_series.py |  6 +++-
 4 files changed, 59 insertions(+), 10 deletions(-)

diff --git a/python/sedona/spark/geopandas/base.py 
b/python/sedona/spark/geopandas/base.py
index 7bbc048651..e22c5f876e 100644
--- a/python/sedona/spark/geopandas/base.py
+++ b/python/sedona/spark/geopandas/base.py
@@ -350,9 +350,41 @@ class GeoFrame(metaclass=ABCMeta):
         """
         return _delegate_to_geometry_column("is_simple", self)
 
-    # @property
-    # def is_ring(self):
-    #     raise NotImplementedError("This method is not implemented yet.")
+    @property
+    def is_ring(self):
+        """Return a ``Series`` of ``dtype('bool')`` with value ``True`` for
+        features that are closed.
+
+        When constructing a LinearRing, the sequence of coordinates may be
+        explicitly closed by passing identical values in the first and last 
indices.
+        Otherwise, the sequence will be implicitly closed by copying the first 
tuple
+        to the last index.
+
+        Examples
+        --------
+        >>> from sedona.spark.geopandas import GeoSeries
+        >>> from shapely.geometry import LineString, LinearRing
+        >>> 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)]),
+        ...     ]
+        ... )
+        >>> s
+        0         LINESTRING (0 0, 1 1, 1 -1)
+        1    LINESTRING (0 0, 1 1, 1 -1, 0 0)
+        2    LINEARRING (0 0, 1 1, 1 -1, 0 0)
+        dtype: geometry
+
+        >>> s.is_ring
+        0    False
+        1     True
+        2     True
+        dtype: bool
+
+        """
+        return _delegate_to_geometry_column("is_ring", self)
 
     # @property
     # def is_ccw(self):
diff --git a/python/sedona/spark/geopandas/geoseries.py 
b/python/sedona/spark/geopandas/geoseries.py
index c9dc7b7f3b..648f6860a0 100644
--- a/python/sedona/spark/geopandas/geoseries.py
+++ b/python/sedona/spark/geopandas/geoseries.py
@@ -944,12 +944,12 @@ class GeoSeries(GeoFrame, pspd.Series):
 
     @property
     def is_ring(self):
-        # Implementation of the abstract method
-        raise NotImplementedError(
-            _not_implemented_error(
-                "is_ring", "Tests if LineString geometries are closed rings."
-            )
+        spark_expr = stf.ST_IsRing(self.spark.column)
+        result = self._query_geometry_column(
+            spark_expr,
+            returns_geom=False,
         )
+        return _to_bool(result)
 
     @property
     def is_ccw(self):
diff --git a/python/tests/geopandas/test_geoseries.py 
b/python/tests/geopandas/test_geoseries.py
index d12a2afd18..61ca151317 100644
--- a/python/tests/geopandas/test_geoseries.py
+++ b/python/tests/geopandas/test_geoseries.py
@@ -869,7 +869,20 @@ e": "Feature", "properties": {}, "geometry": {"type": 
"Point", "coordinates": [3
         assert_series_equal(df_result, expected)
 
     def test_is_ring(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_ring
+        expected = pd.Series([False, True, True])
+        self.check_pd_series_equal(result, expected)
+
+        # Check that GeoDataFrame works too
+        result = s.to_geoframe().is_ring
+        self.check_pd_series_equal(result, expected)
 
     def test_is_ccw(self):
         pass
diff --git a/python/tests/geopandas/test_match_geopandas_series.py 
b/python/tests/geopandas/test_match_geopandas_series.py
index aa4ae6d238..c845bf99a0 100644
--- a/python/tests/geopandas/test_match_geopandas_series.py
+++ b/python/tests/geopandas/test_match_geopandas_series.py
@@ -578,7 +578,11 @@ class TestMatchGeopandasSeries(TestGeopandasBase):
         self.check_pd_series_equal(sgpd_result, gpd_result)
 
     def test_is_ring(self):
-        pass
+        # is_ring is only meaningful for linestrings so we use 
self.linestrings instead of self.geoms
+        for geom in self.linestrings:
+            sgpd_result = GeoSeries(geom).is_ring
+            gpd_result = gpd.GeoSeries(geom).is_ring
+            self.check_pd_series_equal(sgpd_result, gpd_result)
 
     def test_is_ccw(self):
         pass

Reply via email to