You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sedona.apache.org by ji...@apache.org on 2024/05/29 04:07:46 UTC
(sedona) branch SEDONA-571 created (now 7e691c1f3)
This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a change to branch SEDONA-571
in repository https://gitbox.apache.org/repos/asf/sedona.git
at 7e691c1f3 Add ST_MMax (#152)
This branch includes the following new commits:
new 9e2684ce4 Add ST_MMin (#149)
new 7e691c1f3 Add ST_MMax (#152)
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(sedona) 02/02: Add ST_MMax (#152)
Posted by ji...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch SEDONA-571
in repository https://gitbox.apache.org/repos/asf/sedona.git
commit 7e691c1f37d085cdb805b6c6cdb5b874712eaf6c
Author: Furqaan Khan <46...@users.noreply.github.com>
AuthorDate: Fri Apr 5 01:14:08 2024 -0400
Add ST_MMax (#152)
---
.../java/org/apache/sedona/common/Functions.java | 11 +++++++++++
.../org/apache/sedona/common/FunctionsTest.java | 12 ++++++++++++
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 | 8 ++++++++
.../java/org/apache/sedona/flink/FunctionTest.java | 11 +++++++++++
python/sedona/sql/st_functions.py | 11 +++++++++++
python/tests/sql/test_dataframe_api.py | 2 ++
python/tests/sql/test_function.py | 9 +++++++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sql/sedona_sql/expressions/Functions.scala | 8 ++++++++
.../sql/sedona_sql/expressions/st_functions.scala | 3 +++
.../apache/sedona/sql/dataFrameAPITestScala.scala | 10 ++++++++++
.../org/apache/sedona/sql/functionTestScala.scala | 12 ++++++++++++
15 files changed, 143 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 44c67e6ce..774a2bfe3 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -409,6 +409,17 @@ public class Functions {
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 a3f396dab..6d15e20ca 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -436,6 +436,18 @@ public class FunctionsTest extends TestBase {
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 3d3e142e6..318c97354 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -2068,6 +2068,28 @@ 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.
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index ec798b9cb..928770887 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -2075,6 +2075,28 @@ 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.
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 9dc51d998..6b5639f97 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -129,6 +129,7 @@ public class Catalog {
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 31230ae30..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
@@ -773,6 +773,14 @@ public class Functions {
}
}
+ 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 22428410a..9d4d6bb79 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -724,6 +724,17 @@ public class FunctionTest extends TestBase{
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 5a39206a5..07814947a 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -891,6 +891,17 @@ def ST_MMin(geom: ColumnOrName) -> 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 360ee0567..628aceefa 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -149,6 +149,7 @@ test_configurations = [
(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))"),
@@ -321,6 +322,7 @@ wrong_type_configurations = [
(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,)),
diff --git a/python/tests/sql/test_function.py b/python/tests/sql/test_function.py
index ec39cd871..1df5e8508 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -950,6 +950,15 @@ class TestPredicateJoin(TestBase):
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 3e926c5da..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
@@ -115,6 +115,7 @@ object Catalog {
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 cc28974ad..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
@@ -535,6 +535,14 @@ case class ST_MMin(inputExpressions: Seq[Expression])
}
}
+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.
* Second and third arguments are Double values between 0 and 1. This only works with LINESTRINGs.
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 608f9931f..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
@@ -221,6 +221,9 @@ object st_functions extends DataFrameAPI {
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 ad21bf9b9..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
@@ -74,6 +74,16 @@ class dataFrameAPITestScala extends TestBaseScala {
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 7691b444b..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
@@ -407,6 +407,18 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample
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
(sedona) 01/02: Add ST_MMin (#149)
Posted by ji...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
jiayu pushed a commit to branch SEDONA-571
in repository https://gitbox.apache.org/repos/asf/sedona.git
commit 9e2684ce4d3f5a9e004d274ec20204186c000fe5
Author: Furqaan Khan <46...@users.noreply.github.com>
AuthorDate: Mon Apr 1 14:32:12 2024 -0400
Add ST_MMin (#149)
---
.../java/org/apache/sedona/common/Functions.java | 11 +++++++++++
.../org/apache/sedona/common/FunctionsTest.java | 12 ++++++++++++
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 | 8 ++++++++
.../java/org/apache/sedona/flink/FunctionTest.java | 11 +++++++++++
python/sedona/sql/st_functions.py | 11 +++++++++++
python/tests/sql/test_dataframe_api.py | 4 ++++
python/tests/sql/test_function.py | 9 +++++++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sql/sedona_sql/expressions/Functions.scala | 7 +++++++
.../sql/sedona_sql/expressions/st_functions.scala | 3 +++
.../apache/sedona/sql/dataFrameAPITestScala.scala | 10 ++++++++++
.../org/apache/sedona/sql/functionTestScala.scala | 10 ++++++++++
15 files changed, 142 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..44c67e6ce 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,17 @@ 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 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..a3f396dab 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,18 @@ 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 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..3d3e142e6 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -2068,6 +2068,28 @@ 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..ec798b9cb 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -2075,6 +2075,28 @@ 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..9dc51d998 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,7 @@ public class Catalog {
new Functions.ST_LineSubstring(),
new Functions.ST_HasM(),
new Functions.ST_M(),
+ new Functions.ST_MMin(),
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..31230ae30 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,14 @@ 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_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..22428410a 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,17 @@ 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 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..5a39206a5 100644
--- a/python/sedona/sql/st_functions.py
+++ b/python/sedona/sql/st_functions.py
@@ -880,6 +880,17 @@ 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_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..360ee0567 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -148,6 +148,7 @@ 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_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 +320,7 @@ wrong_type_configurations = [
(stf.ST_LineSubstring, ("", 0.5, None)),
(stf.ST_HasM, (None,)),
(stf.ST_M, (None,)),
+ (stf.ST_MMin, (None,)),
(stf.ST_MakeValid, (None,)),
(stf.ST_MakePolygon, (None,)),
(stf.ST_MinimumBoundingCircle, (None,)),
@@ -451,6 +453,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..ec39cd871 100644
--- a/python/tests/sql/test_function.py
+++ b/python/tests/sql/test_function.py
@@ -941,6 +941,15 @@ 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_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..3e926c5da 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,7 @@ object Catalog {
function[ST_Boundary](),
function[ST_HasM](),
function[ST_M](),
+ function[ST_MMin](),
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..cc28974ad 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,13 @@ 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)
+ }
+}
/**
* 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..608f9931f 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,9 @@ 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_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..ad21bf9b9 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,16 @@ 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_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..7691b444b 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,16 @@ 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_MakeLine") {
val testtable = sparkSession.sql(
"""SELECT