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 ee3a28d71 [SEDONA-571][SEDONA-574] Add ST_MMin, ST_MMax (#1442)
ee3a28d71 is described below

commit ee3a28d7134f4804e881f97bbfded11c2dda9917
Author: Jia Yu <[email protected]>
AuthorDate: Tue May 28 21:29:18 2024 -0700

    [SEDONA-571][SEDONA-574] Add ST_MMin, ST_MMax (#1442)
    
    * Add ST_MMin (#149)
    
    * Add ST_MMax (#152)
    
    ---------
    
    Co-authored-by: Furqaan Khan <[email protected]>
---
 .../java/org/apache/sedona/common/Functions.java   | 22 +++++++++++
 .../org/apache/sedona/common/FunctionsTest.java    | 24 ++++++++++++
 docs/api/flink/Function.md                         | 44 ++++++++++++++++++++++
 docs/api/sql/Function.md                           | 44 ++++++++++++++++++++++
 .../main/java/org/apache/sedona/flink/Catalog.java |  2 +
 .../apache/sedona/flink/expressions/Functions.java | 16 ++++++++
 .../java/org/apache/sedona/flink/FunctionTest.java | 22 +++++++++++
 python/sedona/sql/st_functions.py                  | 22 +++++++++++
 python/tests/sql/test_dataframe_api.py             |  6 +++
 python/tests/sql/test_function.py                  | 18 +++++++++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |  2 +
 .../sql/sedona_sql/expressions/Functions.scala     | 15 ++++++++
 .../sql/sedona_sql/expressions/st_functions.scala  |  6 +++
 .../apache/sedona/sql/dataFrameAPITestScala.scala  | 20 ++++++++++
 .../org/apache/sedona/sql/functionTestScala.scala  | 22 +++++++++++
 15 files changed, 285 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 ba9971c52..774a2bfe3 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -398,6 +398,28 @@ public class Functions {
         return null;
     }
 
+    public static Double mMin(Geometry geometry) {
+        Coordinate[] points = geometry.getCoordinates();
+        double min = Double.MAX_VALUE;
+        for(int i=0; i < points.length; i++){
+            if(java.lang.Double.isNaN(points[i].getM()))
+                continue;
+            min = Math.min(points[i].getM(), min);
+        }
+        return min == Double.MAX_VALUE ? null : min;
+    }
+
+    public static Double mMax(Geometry geometry) {
+        Coordinate[] points = geometry.getCoordinates();
+        double max = - Double.MAX_VALUE;
+        for (int i=0; i < points.length; i++) {
+            if(java.lang.Double.isNaN(points[i].getM()))
+                continue;
+            max = Math.max(points[i].getM(), max);
+        }
+        return max == -Double.MAX_VALUE ? null : max;
+    }
+
     public static double xMin(Geometry geometry) {
         Coordinate[] points = geometry.getCoordinates();
         double min = Double.MAX_VALUE;
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 62e47a462..6d15e20ca 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -424,6 +424,30 @@ public class FunctionsTest extends TestBase {
         assertNull(actualRes);
     }
 
+    @Test
+    public void testMMin() throws ParseException {
+        Geometry geom = Constructors.geomFromWKT("LINESTRING ZM(1 1 1 1, 2 2 2 
2, 3 3 3 3, -1 -1 -1 -1)", 0);
+        Double actual = Functions.mMin(geom);
+        Double expected = -1.0;
+        assertEquals(expected, actual);
+
+        geom = Constructors.geomFromWKT("LINESTRING(1 1 1, 2 2 2, 3 3 3, -1 -1 
-1)", 0);
+        actual = Functions.mMin(geom);
+        assertNull(actual);
+    }
+
+    @Test
+    public void testMMax() throws ParseException {
+        Geometry geom = Constructors.geomFromWKT("LINESTRING ZM(1 1 1 1, 2 2 2 
2, 3 3 3 3, -1 -1 -1 -1)", 0);
+        Double actual = Functions.mMax(geom);
+        Double expected = 3.0;
+        assertEquals(expected, actual);
+
+        geom = Constructors.geomFromWKT("LINESTRING(1 1 1, 2 2 2, 3 3 3, -1 -1 
-1)", 0);
+        actual = Functions.mMax(geom);
+        assertNull(actual);
+    }
+
     @Test
     public void dimensionGeometry3D() {
         Point point3D = GEOMETRY_FACTORY.createPoint(new Coordinate(1, 1, 1));
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 55ac471b8..318c97354 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -2068,6 +2068,50 @@ Output:
 4.0
 ```
 
+## ST_MMax
+
+Introduction: Returns M maxima of the given geometry or null if there is no M 
coordinate.
+
+Format: `ST_MMax(geom: Geometry)`
+
+Since: `vTBD`
+
+SQL Example
+
+```sql
+SELECT ST_MMax(
+        ST_GeomFromWKT('POLYGON ZM ((30 10 5 1, 40 40 10 2, 20 40 15 3, 10 20 
20 4, 30 10 5 1))')
+)
+```
+
+Output:
+
+```
+4.0
+```
+
+## ST_MMin
+
+Introduction: Returns M minima of the given geometry or null if there is no M 
coordinate.
+
+Format: `ST_MMin(geom: Geometry)`
+
+Since: `vTBD`
+
+SQL Example:
+
+```sql
+SELECT ST_MMin(
+        ST_GeomFromWKT('LINESTRING ZM(1 1 1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)')
+)
+```
+
+Output:
+
+```
+-1.0
+```
+
 ## ST_MakeLine
 
 Introduction: Creates a LineString containing the points of Point, MultiPoint, 
or LineString geometries. Other geometry types cause an error.
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index dccb5fad0..928770887 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -2075,6 +2075,50 @@ Output:
 4.0
 ```
 
+## ST_MMax
+
+Introduction: Returns M maxima of the given geometry or null if there is no M 
coordinate.
+
+Format: `ST_MMax(geom: Geometry)`
+
+Since: `vTBD`
+
+SQL Example
+
+```sql
+SELECT ST_MMax(
+        ST_GeomFromWKT('POLYGON ZM ((30 10 5 1, 40 40 10 2, 20 40 15 3, 10 20 
20 4, 30 10 5 1))')
+)
+```
+
+Output:
+
+```
+4.0
+```
+
+## ST_MMin
+
+Introduction: Returns M minima of the given geometry or null if there is no M 
coordinate.
+
+Format: `ST_MMin(geom: Geometry)`
+
+Since: `vTBD`
+
+SQL Example:
+
+```sql
+SELECT ST_MMin(
+        ST_GeomFromWKT('LINESTRING ZM(1 1 1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)')
+)
+```
+
+Output:
+
+```
+-1.0
+```
+
 ## ST_MakeLine
 
 Introduction: Creates a LineString containing the points of Point, MultiPoint, 
or LineString geometries. Other geometry types cause an error.
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 fe47f5f86..6b5639f97 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -128,6 +128,8 @@ public class Catalog {
                 new Functions.ST_LineSubstring(),
                 new Functions.ST_HasM(),
                 new Functions.ST_M(),
+                new Functions.ST_MMin(),
+                new Functions.ST_MMax(),
                 new Functions.ST_MakeLine(),
                 new Functions.ST_Polygon(),
                 new Functions.ST_Polygonize(),
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 150da9e99..7397087c5 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
@@ -765,6 +765,22 @@ public class Functions {
         }
     }
 
+    public static class ST_MMin extends  ScalarFunction {
+        @DataTypeHint("Double")
+        public Double eval(@DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o) {
+            Geometry geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.mMin(geom);
+        }
+    }
+
+    public static class ST_MMax extends  ScalarFunction {
+        @DataTypeHint("Double")
+        public Double eval(@DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class) Object o) {
+            Geometry geom = (Geometry) o;
+            return org.apache.sedona.common.Functions.mMax(geom);
+        }
+    }
+
     public static class ST_MakeLine 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 e9b4053a9..9d4d6bb79 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -713,6 +713,28 @@ public class FunctionTest extends TestBase{
         assertEquals(4, actual, FP_TOLERANCE);
     }
 
+    @Test
+    public void testMMin() {
+        Table lineTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING 
ZM(1 1 1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') AS line");
+        double actual = (double) 
first(lineTable.select(call(Functions.ST_MMin.class.getSimpleName(), 
$("line")))).getField(0);
+        assertEquals(-1.0, actual, FP_TOLERANCE);
+
+        lineTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(1 1, 
2 2, 3 3, -1 -1)') AS line");
+        Double actualNull = (Double) 
first(lineTable.select(call(Functions.ST_MMin.class.getSimpleName(), 
$("line")))).getField(0);
+        assertNull(actualNull);
+    }
+
+    @Test
+    public void testMMax() {
+        Table lineTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING 
ZM(1 1 1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') AS line");
+        double actual = (double) 
first(lineTable.select(call(Functions.ST_MMax.class.getSimpleName(), 
$("line")))).getField(0);
+        assertEquals(3, actual, FP_TOLERANCE);
+
+        lineTable = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING(1 1, 
2 2, 3 3, -1 -1)') AS line");
+        Double actualNull = (Double) 
first(lineTable.select(call(Functions.ST_MMax.class.getSimpleName(), 
$("line")))).getField(0);
+        assertNull(actualNull);
+    }
+
     @Test
     public void testZMax() {
         Table polygonTable = tableEnv.sqlQuery("SELECT 
ST_GeomFromWKT('LINESTRING(1 3 4, 5 6 7)') AS " + polygonColNames[0]);
diff --git a/python/sedona/sql/st_functions.py 
b/python/sedona/sql/st_functions.py
index 2b45d9e03..07814947a 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -880,6 +880,28 @@ def ST_M(geom: ColumnOrName) -> Column:
     """
     return _call_st_function("ST_M", geom)
 
+@validate_argument_types
+def ST_MMin(geom: ColumnOrName) -> Column:
+    """Return the minimum M coordinate of a geometry.
+
+    :param geometry: Geometry column to get the minimum M coordinate from.
+    :type geometry: ColumnOrName
+    :return: Minimum M coordinate for the geometry as a double column.
+    :rtype: Column
+    """
+    return _call_st_function("ST_MMin", geom)
+
+@validate_argument_types
+def ST_MMax(geom: ColumnOrName) -> Column:
+    """Return the maximum M coordinate of a geometry.
+
+    :param geometry: Geometry column to get the maximum M coordinate from.
+    :type geometry: ColumnOrName
+    :return: Minimum M coordinate for the geometry as a double column.
+    :rtype: Column
+    """
+    return _call_st_function("ST_MMax", geom)
+
 
 @validate_argument_types
 def ST_MakeLine(geom1: ColumnOrName, geom2: Optional[ColumnOrName] = None) -> 
Column:
diff --git a/python/tests/sql/test_dataframe_api.py 
b/python/tests/sql/test_dataframe_api.py
index 8df2440fa..628aceefa 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -148,6 +148,8 @@ test_configurations = [
     (stf.ST_LineSubstring, ("line", 0.5, 1.0), "linestring_geom", "", 
"LINESTRING (2.5 0, 3 0, 4 0, 5 0)"),
     (stf.ST_HasM, ("point",), "4D_point", "", True),
     (stf.ST_M, ("point",), "4D_point", "", 4.0),
+    (stf.ST_MMin, ("line",), "4D_line", "", -1.0),
+    (stf.ST_MMax, ("line",), "4D_line", "", 3.0),
     (stf.ST_MakeValid, ("geom",), "invalid_geom", "", "MULTIPOLYGON (((1 5, 3 
3, 1 1, 1 5)), ((5 3, 7 5, 7 1, 5 3)))"),
     (stf.ST_MakeLine, ("line1", "line2"), "two_lines", "", "LINESTRING (0 0, 1 
1, 0 0, 3 2)"),
     (stf.ST_Polygon, ("geom", 4236), "closed_linestring_geom", "", "POLYGON 
((0 0, 1 0, 1 1, 0 0))"),
@@ -319,6 +321,8 @@ wrong_type_configurations = [
     (stf.ST_LineSubstring, ("", 0.5, None)),
     (stf.ST_HasM, (None,)),
     (stf.ST_M, (None,)),
+    (stf.ST_MMin, (None,)),
+    (stf.ST_MMax, (None,)),
     (stf.ST_MakeValid, (None,)),
     (stf.ST_MakePolygon, (None,)),
     (stf.ST_MinimumBoundingCircle, (None,)),
@@ -451,6 +455,8 @@ class TestDataFrameAPI(TestBase):
             return TestDataFrameAPI.spark.sql("SELECT ST_PointZ(0.0, 0.0, 0.0) 
AS a, ST_PointZ(3.0, 0.0, 4.0) AS b")
         elif request.param == "4D_point":
             return TestDataFrameAPI.spark.sql("SELECT ST_GeomFromWKT('POINT 
ZM(1 2 3 4)') AS point")
+        elif request.param == "4D_line":
+            return TestDataFrameAPI.spark.sql("SELECT 
ST_GeomFromWKT('LINESTRING ZM(1 1 1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') AS 
line")
         elif request.param == "invalid_geom":
             return TestDataFrameAPI.spark.sql("SELECT ST_GeomFromWKT('POLYGON 
((1 5, 1 1, 3 3, 5 3, 7 1, 7 5, 5 3, 3 3, 1 5))') AS geom")
         elif request.param == "overlapping_polys":
diff --git a/python/tests/sql/test_function.py 
b/python/tests/sql/test_function.py
index bc0aeb990..1df5e8508 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -941,6 +941,24 @@ class TestPredicateJoin(TestBase):
         actual = baseDf.selectExpr("ST_M(point)").take(1)[0][0]
         assert actual == 4.0
 
+    def test_st_m_min(self):
+        baseDf = self.spark.sql("SELECT ST_GeomFromWKT('LINESTRING ZM(1 1 1 1, 
2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') AS line")
+        actual = baseDf.selectExpr("ST_MMin(line)").take(1)[0][0]
+        assert actual == -1.0
+
+        baseDf = self.spark.sql("SELECT ST_GeomFromWKT('LINESTRING(1 1, 2 2, 3 
3, -1 -1)') AS line")
+        actual = baseDf.selectExpr("ST_MMin(line)").take(1)[0][0]
+        assert actual is None
+
+    def test_st_m_max(self):
+        baseDf = self.spark.sql("SELECT ST_GeomFromWKT('LINESTRING ZM(1 1 1 1, 
2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') AS line")
+        actual = baseDf.selectExpr("ST_MMax(line)").take(1)[0][0]
+        assert actual == 3.0
+
+        baseDf = self.spark.sql("SELECT ST_GeomFromWKT('LINESTRING(1 1, 2 2, 3 
3, -1 -1)') AS line")
+        actual = baseDf.selectExpr("ST_MMax(line)").take(1)[0][0]
+        assert actual is None
+
     def test_st_subdivide_explode_lateral(self):
         # Given
         geometry_df = self.__wkt_list_to_data_frame(
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 334ac8e71..3e7eacf0a 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
@@ -114,6 +114,8 @@ object Catalog {
     function[ST_Boundary](),
     function[ST_HasM](),
     function[ST_M](),
+    function[ST_MMin](),
+    function[ST_MMax](),
     function[ST_MinimumBoundingRadius](),
     
function[ST_MinimumBoundingCircle](BufferParameters.DEFAULT_QUADRANT_SEGMENTS * 
6),
     function[ST_EndPoint](),
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 b395dd0f8..21613b37c 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
@@ -527,6 +527,21 @@ case class ST_M(inputExpressions: Seq[Expression])
   }
 }
 
+case class ST_MMin(inputExpressions: Seq[Expression])
+  extends InferredExpression(Functions.mMin _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
+case class ST_MMax(inputExpressions: Seq[Expression])
+  extends InferredExpression(Functions.mMax _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
 
 /**
   * Return a linestring being a substring of the input one starting and ending 
at the given fractions of total 2d length.
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 dd7395bd1..65cec5a29 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
@@ -218,6 +218,12 @@ object st_functions extends DataFrameAPI {
   def ST_M(geoms: Column): Column = wrapExpression[ST_M](geoms)
   def ST_M(geoms: String): Column = wrapExpression[ST_M](geoms)
 
+  def ST_MMin(geoms: Column): Column = wrapExpression[ST_MMin](geoms)
+  def ST_MMin(geoms: String): Column = wrapExpression[ST_MMin](geoms)
+
+  def ST_MMax(geoms: Column): Column = wrapExpression[ST_MMax](geoms)
+  def ST_MMax(geoms: String): Column = wrapExpression[ST_MMax](geoms)
+
   def ST_MakeLine(geoms: Column): Column = wrapExpression[ST_MakeLine](geoms)
   def ST_MakeLine(geoms: String): Column = wrapExpression[ST_MakeLine](geoms)
   def ST_MakeLine(geom1: Column, geom2: Column): Column = 
wrapExpression[ST_MakeLine](geom1, geom2)
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 6d0946b44..3bf263be4 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
@@ -64,6 +64,26 @@ class dataFrameAPITestScala extends TestBaseScala {
       assert(actual == 40.0)
     }
 
+    it("Passed ST_MMin") {
+      var baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING ZM(1 1 
1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') as line")
+      val actual = baseDf.select(ST_MMin("line")).first().getDouble(0)
+      assert(actual == -1.0)
+
+      baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(1 1, 2 2, 3 
3, -1 -1)') AS line")
+      val actualNull = baseDf.select(ST_MMin("line")).first().get(0)
+      assert(actualNull == null)
+    }
+
+    it("Passed ST_MMax()") {
+      var baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING ZM(1 1 
1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') as line")
+      val actual = baseDf.select(ST_MMax("line")).first().getDouble(0)
+      assert(actual == 3.0)
+
+      baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(1 1, 2 2, 3 
3, -1 -1)') AS line")
+      val actualNull = baseDf.select(ST_MMax("line")).first().get(0)
+      assert(actualNull == null)
+    }
+
     it("Passed st_pointm") {
       val pointDf = sparkSession.sql("SELECT ST_PointM(1,2,100) as point1, 
ST_PointM(1,2,100,4326) as point2")
       val point1 = pointDf.select(ST_AsEWKT("point1")).take(1)(0).getString(0)
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 5dee828db..98f4975ea 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
@@ -397,6 +397,28 @@ class functionTestScala extends TestBaseScala with 
Matchers with GeometrySample
       assert(actual == 4.0)
     }
 
+    it("Passed ST_MMin") {
+      var baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING ZM(1 1 
1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') as line")
+      val actual = baseDf.selectExpr("ST_MMin(line)").first().getDouble(0)
+      assert(actual == -1.0)
+
+      baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(1 1, 2 2, 3 
3, -1 -1)') AS line")
+      val actualNull = baseDf.selectExpr("ST_MMin(line)").first().get(0)
+      assert(actualNull == null)
+    }
+
+    it("Passed ST_MMax") {
+      var baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING ZM(1 1 
1 1, 2 2 2 2, 3 3 3 3, -1 -1 -1 -1)') as line")
+      val actual = baseDf.selectExpr("ST_MMax(line)").first().getDouble(0)
+      assert(actual == 3.0)
+
+      baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING(1 1, 2 2, 3 
3, -1 -1)') AS line")
+      val actualNull = baseDf.selectExpr("ST_MMax(line)").first().get(0)
+      assert(actualNull == null)
+
+      print(sparkSession.sql("SELECT ST_MMax(ST_GeomFromWKT('POLYGON ZM ((30 
10 5 1, 40 40 10 2, 20 40 15 3, 10 20 20 4, 30 10 5 1))'))").first().get(0))
+    }
+
     it("Passed ST_MakeLine") {
       val testtable = sparkSession.sql(
         """SELECT

Reply via email to