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 974350c2c [SEDONA-604] Add ST_AddMeasure (#1472)
974350c2c is described below

commit 974350c2c31a90c3fc2814ad1c15791f0aa69a21
Author: Jia Yu <[email protected]>
AuthorDate: Fri Jun 7 15:14:18 2024 -0700

    [SEDONA-604] Add ST_AddMeasure (#1472)
    
    * [TASK-92] Add ST_AddMeasure (#207)
    
    * feat: add implementation
    
    * feat: port to flink and spark
    
    * feat: add another declaration to spark DataframeAPI
    
    * Update versions
    
    ---------
    
    Co-authored-by: Furqaan Khan <[email protected]>
---
 .../java/org/apache/sedona/common/Functions.java   |  4 ++
 .../org/apache/sedona/common/utils/GeomUtils.java  | 70 ++++++++++++++++++++++
 .../org/apache/sedona/common/FunctionsTest.java    | 18 ++++++
 docs/api/flink/Function.md                         | 22 +++++++
 docs/api/sql/Function.md                           | 22 +++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |  1 +
 .../apache/sedona/flink/expressions/Functions.java |  9 +++
 .../java/org/apache/sedona/flink/FunctionTest.java | 15 +++++
 python/sedona/sql/st_functions.py                  | 14 +++++
 python/tests/sql/test_dataframe_api.py             |  3 +
 python/tests/sql/test_function.py                  | 10 ++++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |  1 +
 .../sql/sedona_sql/expressions/Functions.scala     |  8 +++
 .../sql/sedona_sql/expressions/st_functions.scala  |  4 ++
 .../apache/sedona/sql/dataFrameAPITestScala.scala  | 16 +++++
 .../org/apache/sedona/sql/functionTestScala.scala  | 11 ++++
 16 files changed, 228 insertions(+)

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 33dd033c1..e9a37958e 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -1092,6 +1092,10 @@ public class Functions {
         return isExteriorRingCCW && isInteriorRingCCW;
     }
 
+    public static Geometry addMeasure(Geometry geom, double measure_start, 
double measure_end) {
+        return GeomUtils.addMeasure(geom, measure_start, measure_end);
+    }
+
     public static double maxDistance(Geometry geom1, Geometry geom2) {
         return longestLine(geom1, geom2).getLength();
     }
diff --git a/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java 
b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
index fc36e79e5..a51f6ccc6 100644
--- a/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
+++ b/common/src/main/java/org/apache/sedona/common/utils/GeomUtils.java
@@ -530,6 +530,76 @@ public class GeomUtils {
         return hausdorffDistanceObj.distance();
     }
 
+    public static Geometry addMeasure(Geometry geom, double measure_start, 
double measure_end) {
+        if (!(geom instanceof LineString) && !(geom instanceof 
MultiLineString)) {
+            throw new IllegalArgumentException("Geometry must be a LineString 
or MultiLineString.");
+        }
+
+        if (geom instanceof LineString) {
+            return addMeasure((LineString) geom, measure_start, measure_end);
+        } else { // MultiLineString
+            return addMeasure((MultiLineString) geom, measure_start, 
measure_end);
+        }
+    }
+
+    private static Geometry addMeasure(LineString geom, double measureStart, 
double measureEnd) {
+        Coordinate[] coordinates = geom.getCoordinates();
+        double totalLength = geom.getLength();
+        CoordinateList newCoordinates = new CoordinateList();
+
+        Coordinate c1 = coordinates[0];
+        double measure,
+                measureRange = measureEnd - measureStart;
+        int numCoordinates = coordinates.length;
+
+        for (int i = 0; i < numCoordinates; i++) {
+            Coordinate c2 = coordinates[i];
+            double length = c1.distance(c2);
+
+            if (totalLength > 0.0) {
+                measure = measureStart + measureRange * (length / totalLength);
+            } else if (totalLength == 0.0 && numCoordinates > 1) { // valid 
zero length LineStrings
+                measure = measureStart + measureRange * i / (numCoordinates - 
1);
+            } else {
+                measure = 0.0;
+            }
+
+            CoordinateXYZM newCoordinate = new CoordinateXYZM(c2.getX(), 
c2.getY(), c2.getZ(), measure);
+            newCoordinates.add(newCoordinate);
+        }
+
+        return 
geom.getFactory().createLineString(newCoordinates.toCoordinateArray());
+    }
+
+    private static Geometry addMeasure(MultiLineString geom, double 
measureStart, double measureEnd) {
+        double totalLength = 0,
+                measureRange = measureEnd - measureStart;
+        for (int i = 0; i < geom.getNumGeometries(); i++) {
+            LineString linestring = (LineString) geom.getGeometryN(i);
+            if (linestring.getCoordinates().length > 1)
+                totalLength += linestring.getLength();
+        }
+        LineString[] newLineStrings = new LineString[geom.getNumGeometries()];
+        double length= 0;
+        for (int i = 0; i < geom.getNumGeometries(); i++) {
+            double subMeasureStart,
+                    subMeasureEnd,
+                    subLength = 0;
+            LineString lineString = (LineString) geom.getGeometryN(i);
+            if (lineString.getCoordinates().length > 1)
+                subLength = lineString.getCoordinates().length;
+
+            subMeasureStart = measureStart + measureRange * length / 
totalLength;
+            subMeasureEnd = measureStart + measureRange * (length + subLength) 
/ totalLength;
+
+            newLineStrings[i] = (LineString) addMeasure(lineString, 
subMeasureStart, subMeasureEnd);
+
+            length += subLength;
+        }
+
+        return geom.getFactory().createMultiLineString(newLineStrings);
+    }
+
     public static Boolean isMeasuredGeometry(Geometry geom) {
         Coordinate coordinate = geom.getCoordinate();
         return !Double.isNaN(coordinate.getM());
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 7c90ae320..8d2553350 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -2760,6 +2760,24 @@ public class FunctionsTest extends TestBase {
 
     }
 
+    @Test
+    public void testAddMeasure() throws ParseException {
+        Geometry geom = Constructors.geomFromEWKT("LINESTRING (1 1, 2 2, 2 2, 
3 3)");
+        String actual = Functions.asWKT(Functions.addMeasure(geom, 1, 70));
+        String expected = "LINESTRING M(1 1 1, 2 2 35.5, 2 2 35.5, 3 3 70)";
+        assertEquals(expected, actual);
+
+        actual = Functions.asWKT(Functions.addMeasure(geom, 1, 2));
+        expected = "LINESTRING M(1 1 1, 2 2 1.5, 2 2 1.5, 3 3 2)";
+        assertEquals(expected, actual);
+
+        geom = Constructors.geomFromEWKT("MULTILINESTRING M((1 0 4, 2 0 4, 4 0 
4),(1 0 4, 2 0 4, 4 0 4))");
+        actual = Functions.asWKT(Functions.addMeasure(geom, 1, 70));
+        expected = "MULTILINESTRING M((1 0 1, 2 0 12.5, 4 0 35.5), (1 0 35.5, 
2 0 47, 4 0 70))";
+        assertEquals(expected, actual);
+
+    }
+
     @Test
     public void voronoiPolygons() {
         MultiPoint multiPoint = 
GEOMETRY_FACTORY.createMultiPointFromCoords(coordArray(0, 0, 2, 2));
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 3cc6d415c..44acffffc 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -53,6 +53,28 @@ Output:
 1.7320508075688772
 ```
 
+## ST_AddMeasure
+
+Introduction: Computes a new geometry with measure (M) values linearly 
interpolated between start and end points. For geometries lacking M dimensions, 
M values are added. Existing M values are overwritten by the new values. 
Applies only to LineString and MultiLineString inputs.
+
+Format: `ST_AddMeasure(geom: Geometry, measureStart: Double, measureEnd: 
Double)`
+
+Since: `v1.6.1`
+
+SQL Example:
+
+```sql
+SELECT ST_AsText(ST_AddMeasure(
+        ST_GeomFromWKT('LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5 0)')
+))
+```
+
+Output:
+
+```
+LINESTRING M(0 0 10, 1 0 16, 2 0 22, 3 0 28, 4 0 34, 5 0 40)
+```
+
 ## ST_AddPoint
 
 Introduction: Return Linestring with additional point at the given index, if 
position is not available the point will be added at the end of line.
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index c9beecc14..eb2485278 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -53,6 +53,28 @@ Output:
 1.7320508075688772
 ```
 
+## ST_AddMeasure
+
+Introduction: Computes a new geometry with measure (M) values linearly 
interpolated between start and end points. For geometries lacking M dimensions, 
M values are added. Existing M values are overwritten by the new values. 
Applies only to LineString and MultiLineString inputs.
+
+Format: `ST_AddMeasure(geom: Geometry, measureStart: Double, measureEnd: 
Double)`
+
+Since: `v1.6.1`
+
+SQL Example:
+
+```sql
+SELECT ST_AsText(ST_AddMeasure(
+        ST_GeomFromWKT('LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5 0)')
+))
+```
+
+Output:
+
+```
+LINESTRING M(0 0 10, 1 0 16, 2 0 22, 3 0 28, 4 0 34, 5 0 40)
+```
+
 ## ST_AddPoint
 
 Introduction: RETURN Linestring with additional point at the given index, if 
position is not available the point will be added at the end of line.
diff --git a/flink/src/main/java/org/apache/sedona/flink/Catalog.java 
b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
index c744a1655..e421669d6 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -132,6 +132,7 @@ public class Catalog {
                 new Functions.ST_IsSimple(),
                 new Functions.ST_IsValid(),
                 new Functions.ST_Normalize(),
+                new Functions.ST_AddMeasure(),
                 new Functions.ST_AddPoint(),
                 new Functions.ST_RemovePoint(),
                 new Functions.ST_SetPoint(),
diff --git 
a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java 
b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
index 5082c4c90..4c254fcb1 100644
--- a/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
+++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Functions.java
@@ -722,6 +722,15 @@ public class Functions {
         }
     }
 
+    public static class ST_AddMeasure extends ScalarFunction {
+        @DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class)
+        public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o1,
+                             @DataTypeHint(value = "Double") Double 
measureStart, @DataTypeHint(value = "Double") Double measureEnd) {
+            Geometry geom = (Geometry) o1;
+            return org.apache.sedona.common.Functions.addMeasure(geom, 
measureStart, measureEnd);
+        }
+    }
+
     public static class ST_AddPoint extends ScalarFunction {
         @DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class)
         public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o1,
diff --git a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java 
b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
index 045a96c99..e237584d9 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -962,6 +962,21 @@ public class FunctionTest extends TestBase{
         assertEquals("POLYGON ((0 0, 0 1, 1 1, 1 0, 0 0))", result.toString());
     }
 
+    @Test
+    public void testAddMeasure() {
+        Table baseTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING 
(1 1, 2 2, 2 2, 3 3)') as line, " +
+                "ST_GeomFromWKT('MULTILINESTRING M((1 0 4, 2 0 4, 4 0 4),(1 0 
4, 2 0 4, 4 0 4))') as mline");
+        String actual = (String) 
first(baseTable.select(call(Functions.ST_AddMeasure.class.getSimpleName(), 
$("line"),
+                1.0, 
70.0)).as("geom").select(call(Functions.ST_AsText.class.getSimpleName(), 
$("geom")))).getField(0);
+        String expected = "LINESTRING M(1 1 1, 2 2 35.5, 2 2 35.5, 3 3 70)";
+        assertEquals(expected, actual);
+
+        actual = (String) 
first(baseTable.select(call(Functions.ST_AddMeasure.class.getSimpleName(), 
$("mline"),
+                10.0, 
70.0)).as("geom").select(call(Functions.ST_AsText.class.getSimpleName(), 
$("geom")))).getField(0);
+        expected = "MULTILINESTRING M((1 0 10, 2 0 20, 4 0 40), (1 0 40, 2 0 
50, 4 0 70))";
+        assertEquals(expected, actual);
+    }
+
     @Test
     public void testAddPoint() {
         Table pointTable = tableEnv.sqlQuery("SELECT 
ST_AddPoint(ST_GeomFromWKT('LINESTRING (0 0, 1 1)'), ST_GeomFromWKT('POINT (2 
2)'))");
diff --git a/python/sedona/sql/st_functions.py 
b/python/sedona/sql/st_functions.py
index 4b1829c72..b1120a2ba 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -57,6 +57,20 @@ def ST_3DDistance(a: ColumnOrName, b: ColumnOrName) -> 
Column:
     """
     return _call_st_function("ST_3DDistance", (a, b))
 
+@validate_argument_types
+def ST_AddMeasure(geom: ColumnOrName, measureStart: Union[ColumnOrName, 
float], measureEnd: Union[ColumnOrName, float]) -> Column:
+    """Interpolate measure values with the provided start and end points and 
return the result geometry.
+
+    :param geom: Geometry column to use in the calculation.
+    :type geom: ColumnOrName
+    :param measureStart: Start point for the measure.
+    :type measureStart: ColumnOrName
+    :param measureEnd: End point for the measure.
+    :type measureEnd: ColumnOrName
+    :return: Result geometry column.
+    :rtype: Column
+    """
+    return _call_st_function("ST_AddMeasure", (geom, measureStart, measureEnd))
 
 @validate_argument_types
 def ST_AddPoint(line_string: ColumnOrName, point: ColumnOrName, index: 
Optional[Union[ColumnOrName, int]] = None) -> Column:
diff --git a/python/tests/sql/test_dataframe_api.py 
b/python/tests/sql/test_dataframe_api.py
index d57c3b466..ac8f8f7fa 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -78,6 +78,7 @@ test_configurations = [
     (stf.ST_3DDistance, ("a", "b"), "two_points", "", 5.0),
     (stf.ST_Affine, ("geom", 1.0, 2.0, 1.0, 2.0, 1.0, 2.0, 1.0, 1.0, 1.0, 1.0, 
1.0, 1.0), "square_geom", "", "POLYGON ((2 3, 4 5, 5 6, 3 4, 2 3))"),
     (stf.ST_Affine, ("geom", 1.0, 2.0, 1.0, 2.0, 1.0, 2.0,), "square_geom", 
"", "POLYGON ((2 3, 4 5, 5 6, 3 4, 2 3))"),
+    (stf.ST_AddMeasure, ("line", 10.0, 40.0), "linestring_geom", 
"ST_AsText(geom)", "LINESTRING M(0 0 10, 1 0 16, 2 0 22, 3 0 28, 4 0 34, 5 0 
40)"),
     (stf.ST_AddPoint, ("line", lambda: f.expr("ST_Point(1.0, 1.0)")), 
"linestring_geom", "", "LINESTRING (0 0, 1 0, 2 0, 3 0, 4 0, 5 0, 1 1)"),
     (stf.ST_AddPoint, ("line", lambda: f.expr("ST_Point(1.0, 1.0)"), 1), 
"linestring_geom", "", "LINESTRING (0 0, 1 1, 1 0, 2 0, 3 0, 4 0, 5 0)"),
     (stf.ST_Angle, ("p1", "p2", "p3", "p4", ), "four_points", "", 
0.4048917862850834),
@@ -291,6 +292,8 @@ wrong_type_configurations = [
     # functions
     (stf.ST_3DDistance, (None, "")),
     (stf.ST_3DDistance, ("", None)),
+    (stf.ST_AddMeasure, (None, None, None)),
+    (stf.ST_AddMeasure, ("", None, "")),
     (stf.ST_AddPoint, (None, "")),
     (stf.ST_AddPoint, ("", None)),
     (stf.ST_Area, (None,)),
diff --git a/python/tests/sql/test_function.py 
b/python/tests/sql/test_function.py
index 3a8b00f58..de1b2e715 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -871,6 +871,16 @@ class TestPredicateJoin(TestBase):
         collected_interior_rings = [[*row] for row in 
number_of_interior_rings.filter("num is not null").collect()]
         assert (collected_interior_rings == [[2, 0], [11, 1]])
 
+    def test_st_add_measure(self):
+        baseDf = self.spark.sql("SELECT ST_GeomFromWKT('LINESTRING (1 1, 2 2, 
2 2, 3 3)') as line, ST_GeomFromWKT('MULTILINESTRING M((1 0 4, 2 0 4, 4 0 4),(1 
0 4, 2 0 4, 4 0 4))') as mline")
+        actual = baseDf.selectExpr("ST_AsText(ST_AddMeasure(line, 1, 
70))").first()[0]
+        expected = "LINESTRING M(1 1 1, 2 2 35.5, 2 2 35.5, 3 3 70)"
+        assert expected == actual
+
+        actual = baseDf.selectExpr("ST_AsText(ST_AddMeasure(mline, 10, 
70))").first()[0]
+        expected = "MULTILINESTRING M((1 0 10, 2 0 20, 4 0 40), (1 0 40, 2 0 
50, 4 0 70))"
+        assert expected == actual
+
     def test_st_add_point(self):
         geometry = [
             ("Point(21 52)", "Point(21 52)"),
diff --git 
a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala 
b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
index 364919521..7b0b4ebc9 100644
--- a/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
+++ b/spark/common/src/main/scala/org/apache/sedona/sql/UDF/Catalog.scala
@@ -148,6 +148,7 @@ object Catalog {
     function[ST_IsCollection](),
     function[ST_NumInteriorRings](),
     function[ST_NumInteriorRing](),
+    function[ST_AddMeasure](),
     function[ST_AddPoint](-1),
     function[ST_RemovePoint](-1),
     function[ST_SetPoint](),
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
index 697bf6128..4af8325a3 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Functions.scala
@@ -728,6 +728,14 @@ case class ST_NumInteriorRing(inputExpressions: 
Seq[Expression])
   }
 }
 
+case class ST_AddMeasure(inputExpressions: Seq[Expression])
+  extends InferredExpression(inferrableFunction3(Functions.addMeasure)) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
 case class ST_AddPoint(inputExpressions: Seq[Expression])
   extends InferredExpression(inferrableFunction3(Functions.addPoint)) {
 
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
index 77fd8fa4e..a6449d521 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_functions.scala
@@ -29,6 +29,10 @@ object st_functions extends DataFrameAPI {
   def ST_3DDistance(a: Column, b: Column): Column = 
wrapExpression[ST_3DDistance](a, b)
   def ST_3DDistance(a: String, b: String): Column = 
wrapExpression[ST_3DDistance](a, b)
 
+  def ST_AddMeasure(geom: Column, measureStart: Column, measureEnd: Column): 
Column = wrapExpression[ST_AddMeasure](geom, measureStart, measureEnd)
+  def ST_AddMeasure(geom: String, measureStart: Double, measureEnd: Double): 
Column = wrapExpression[ST_AddMeasure](geom, measureStart, measureEnd)
+  def ST_AddMeasure(geom: String, measureStart: String, measureEnd: String): 
Column = wrapExpression[ST_AddMeasure](geom, measureStart, measureEnd)
+
   def ST_AddPoint(lineString: Column, point: Column): Column = 
wrapExpression[ST_AddPoint](lineString, point, -1)
   def ST_AddPoint(lineString: String, point: String): Column = 
wrapExpression[ST_AddPoint](lineString, point, -1)
   def ST_AddPoint(lineString: Column, point: Column, index: Column): Column = 
wrapExpression[ST_AddPoint](lineString, point, index)
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
index e22f3727b..a11901447 100644
--- 
a/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
+++ 
b/spark/common/src/test/scala/org/apache/sedona/sql/dataFrameAPITestScala.scala
@@ -926,6 +926,22 @@ class dataFrameAPITestScala extends TestBaseScala {
       assert(actualResult == expectedResult)
     }
 
+    it("Passed ST_AddMeasure") {
+      var baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING (1 1, 2 
2, 2 2, 3 3)') as line, ST_GeomFromWKT('MULTILINESTRING M((1 0 4, 2 0 4, 4 0 
4),(1 0 4, 2 0 4, 4 0 4))') as mline")
+      var actual = baseDf.select(ST_AsText(ST_AddMeasure("line", 1, 
70))).first().get(0)
+      var expected = "LINESTRING M(1 1 1, 2 2 35.5, 2 2 35.5, 3 3 70)"
+      assertEquals(expected, actual)
+
+      actual = baseDf.select(ST_AsText(ST_AddMeasure("mline", 10, 
70))).first().get(0)
+      expected = "MULTILINESTRING M((1 0 10, 2 0 20, 4 0 40), (1 0 40, 2 0 50, 
4 0 70))"
+      assertEquals(expected, actual)
+
+      baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('MULTILINESTRING M((1 0 
4, 2 0 4, 4 0 4),(1 0 4, 2 0 4, 4 0 4))') as mline, 10.0 as measureRangeStart, 
70.0 as measureRangeEnd")
+      actual = baseDf.select(ST_AsText(ST_AddMeasure("mline", 
"measureRangeEnd", "measureRangeStart"))).first().get(0)
+      expected = "MULTILINESTRING M((1 0 70, 2 0 60, 4 0 40), (1 0 40, 2 0 30, 
4 0 10))"
+      assertEquals(expected, actual)
+    }
+
     it("Passed ST_AddPoint without index") {
       val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING (0 0, 1 
0)') AS line, ST_Point(1.0, 1.0) AS point")
       val df = baseDf.select(ST_AddPoint("line", "point"))
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 ccec742fd..2ff20d06f 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
@@ -1345,6 +1345,17 @@ class functionTestScala extends TestBaseScala with 
Matchers with GeometrySample
       .collect().toList should contain theSameElementsAs List((2, 0), (11, 1))
   }
 
+  it("Should pass ST_AddMeasure") {
+    val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING (1 1, 2 
2, 2 2, 3 3)') as line, ST_GeomFromWKT('MULTILINESTRING M((1 0 4, 2 0 4, 4 0 
4),(1 0 4, 2 0 4, 4 0 4))') as mline")
+    var actual = baseDf.selectExpr("ST_AsText(ST_AddMeasure(line, 1, 
70))").first().get(0)
+    var expected = "LINESTRING M(1 1 1, 2 2 35.5, 2 2 35.5, 3 3 70)"
+    assertEquals(expected, actual)
+
+    actual = baseDf.selectExpr("ST_AsText(ST_AddMeasure(mline, 10, 
70))").first().get(0)
+    expected = "MULTILINESTRING M((1 0 10, 2 0 20, 4 0 40), (1 0 40, 2 0 50, 4 
0 70))"
+    assertEquals(expected, actual)
+  }
+
   it("Should pass ST_AddPoint") {
     Given("Geometry df")
     val geometryDf = Seq(

Reply via email to