This is an automated email from the ASF dual-hosted git repository.

jiayu pushed a commit to branch geopandas-tier2-batch-e
in repository https://gitbox.apache.org/repos/asf/sedona.git


The following commit(s) were added to refs/heads/geopandas-tier2-batch-e by 
this push:
     new c081ba7d89 Handle zero-length lines and empty points in 
ST_LineLocatePoint
c081ba7d89 is described below

commit c081ba7d8908b358e45db213778d117eb1ecb4c9
Author: Jia Yu <[email protected]>
AuthorDate: Fri Mar 13 01:50:00 2026 -0700

    Handle zero-length lines and empty points in ST_LineLocatePoint
    
    - Return null for empty point input (was NPE from point.getCoordinate())
    - Guard project non-normalized mode: return 0.0 for zero-length lines,
      propagate null for empty lines (matches geopandas behavior)
    - Add Java tests for empty point and zero-length line edge cases
    - Add Scala Spark test for ST_LineLocatePoint with POINT EMPTY
---
 .../main/java/org/apache/sedona/common/Functions.java |  2 +-
 .../java/org/apache/sedona/common/FunctionsTest.java  | 19 +++++++++++++++++++
 python/sedona/spark/geopandas/geoseries.py            |  6 ++++--
 .../org/apache/sedona/sql/functionTestScala.scala     |  6 ++++++
 4 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/common/src/main/java/org/apache/sedona/common/Functions.java 
b/common/src/main/java/org/apache/sedona/common/Functions.java
index 0359b8efe0..0c7f487b65 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -1487,7 +1487,7 @@ public class Functions {
   }
 
   public static Double lineLocatePoint(Geometry geom, Geometry point) {
-    if (geom.isEmpty()) return null;
+    if (geom.isEmpty() || point.isEmpty()) return null;
     double length = geom.getLength();
     LengthIndexedLine indexedLine = new LengthIndexedLine(geom);
     return indexedLine.indexOf(point.getCoordinate()) / length;
diff --git a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java 
b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
index f2c0561d5f..882a18de82 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -4414,6 +4414,25 @@ public class FunctionsTest extends TestBase {
     assertNull(Functions.lineLocatePoint(emptyLine, point));
   }
 
+  @Test
+  public void lineLocatePointEmptyPoint() {
+    LineString line =
+        GEOMETRY_FACTORY.createLineString(
+            new Coordinate[] {new Coordinate(0, 0), new Coordinate(1, 1)});
+    Geometry emptyPoint = GEOMETRY_FACTORY.createPoint();
+    assertNull(Functions.lineLocatePoint(line, emptyPoint));
+  }
+
+  @Test
+  public void lineLocatePointZeroLength() {
+    LineString zeroLen =
+        GEOMETRY_FACTORY.createLineString(
+            new Coordinate[] {new Coordinate(1, 1), new Coordinate(1, 1)});
+    Geometry point = GEOMETRY_FACTORY.createPoint(new Coordinate(2, 2));
+    Double result = Functions.lineLocatePoint(zeroLen, point);
+    assertTrue(Double.isNaN(result));
+  }
+
   @Test
   public void lineInterpolatePointEmpty() {
     LineString emptyLine = GEOMETRY_FACTORY.createLineString();
diff --git a/python/sedona/spark/geopandas/geoseries.py 
b/python/sedona/spark/geopandas/geoseries.py
index 2f162f362a..f3d96dba30 100644
--- a/python/sedona/spark/geopandas/geoseries.py
+++ b/python/sedona/spark/geopandas/geoseries.py
@@ -1402,8 +1402,10 @@ class GeoSeries(GeoFrame, pspd.Series):
         if normalized:
             spark_expr = stf.ST_LineLocatePoint(F.col("L"), F.col("R"))
         else:
-            spark_expr = stf.ST_LineLocatePoint(F.col("L"), F.col("R")) * 
stf.ST_Length(
-                F.col("L")
+            locate = stf.ST_LineLocatePoint(F.col("L"), F.col("R"))
+            length = stf.ST_Length(F.col("L"))
+            spark_expr = F.when(locate.isNull(), F.lit(None)).otherwise(
+                F.when(length == 0, F.lit(0.0)).otherwise(locate * length)
             )
         result = self._row_wise_operation(
             spark_expr,
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
index b1317da171..377e598b48 100644
--- a/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
+++ b/spark/common/src/test/scala/org/apache/sedona/sql/functionTestScala.scala
@@ -2503,6 +2503,12 @@ class functionTestScala
     assert(df.take(1)(0).isNullAt(0))
   }
 
+  it("Should return null for ST_LineLocatePoint with empty point") {
+    val df = sparkSession.sql(
+      "SELECT ST_LineLocatePoint(ST_GeomFromWKT('LINESTRING(0 0, 1 1)'), 
ST_GeomFromWKT('POINT EMPTY')) AS loc")
+    assert(df.take(1)(0).isNullAt(0))
+  }
+
   it("Should return POINT EMPTY for ST_LineInterpolatePoint with empty 
geometry") {
     val df = sparkSession.sql(
       "SELECT ST_LineInterpolatePoint(ST_GeomFromWKT('LINESTRING EMPTY'), 0.5) 
AS pt")

Reply via email to