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

jiayu pushed a commit to branch SEDONA-595
in repository https://gitbox.apache.org/repos/asf/sedona.git

commit 18627337985ef1fa64b6ae6c1e26bf6ea6f5ddb9
Author: Furqaan Khan <[email protected]>
AuthorDate: Thu May 30 18:17:26 2024 -0400

    [TASK-115] Add ST_MakePointM (#205)
---
 .../org/apache/sedona/common/Constructors.java     |  5 +++++
 .../org/apache/sedona/common/ConstructorsTest.java | 10 +++++++++
 docs/api/flink/Constructor.md                      | 22 +++++++++++++++++-
 docs/api/sql/Constructor.md                        | 22 +++++++++++++++++-
 .../main/java/org/apache/sedona/flink/Catalog.java |  1 +
 .../sedona/flink/expressions/Constructors.java     |  7 ++++++
 .../org/apache/sedona/flink/ConstructorTest.java   | 26 ++++++++++++++++++++++
 python/sedona/sql/st_constructors.py               | 15 +++++++++++++
 python/tests/sql/test_constructor_test.py          | 14 ++++++++++++
 python/tests/sql/test_dataframe_api.py             |  3 +++
 .../scala/org/apache/sedona/sql/UDF/Catalog.scala  |  1 +
 .../sql/sedona_sql/expressions/Constructors.scala  |  8 +++++++
 .../sedona_sql/expressions/st_constructors.scala   |  4 ++++
 .../apache/sedona/sql/constructorTestScala.scala   |  8 +++++++
 .../apache/sedona/sql/dataFrameAPITestScala.scala  |  7 ++++++
 15 files changed, 151 insertions(+), 2 deletions(-)

diff --git a/common/src/main/java/org/apache/sedona/common/Constructors.java 
b/common/src/main/java/org/apache/sedona/common/Constructors.java
index 910d650e5..31a339c38 100644
--- a/common/src/main/java/org/apache/sedona/common/Constructors.java
+++ b/common/src/main/java/org/apache/sedona/common/Constructors.java
@@ -143,6 +143,11 @@ public class Constructors {
         return geometryFactory.createPoint(new Coordinate(x, y));
     }
 
+    public static Geometry makePointM(double x, double y, double m) {
+        GeometryFactory geometryFactory = new GeometryFactory();
+        return geometryFactory.createPoint(new CoordinateXYM(x, y, m));
+    }
+
     public static Geometry makePoint(Double x, Double y, Double z, Double m){
         GeometryFactory geometryFactory = new GeometryFactory();
         if (x == null || y == null) {
diff --git 
a/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java 
b/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
index 2aaa51d20..fcf8f8554 100644
--- a/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/ConstructorsTest.java
@@ -150,6 +150,16 @@ public class ConstructorsTest {
         assertEquals("POINT (-112.5 22.5)", point);
     }
 
+    @Test
+    public void makePointM() {
+        Geometry point = Constructors.makePointM(1, 2, 3);
+
+        assertTrue(point instanceof Point);
+        String actual = Functions.asWKT(point);
+        String expected = "POINT M(1 2 3)";
+        assertEquals(expected, actual);
+    }
+
     @Test
     public void point2d() {
         Geometry point = Constructors.makePoint(1.0d, 2.0d, null, null);
diff --git a/docs/api/flink/Constructor.md b/docs/api/flink/Constructor.md
index f6cff5b80..c01578220 100644
--- a/docs/api/flink/Constructor.md
+++ b/docs/api/flink/Constructor.md
@@ -543,7 +543,7 @@ MULTIPOLYGON (((0 0, 20 0, 20 20, 0 20, 0 0), (5 5, 5 7, 7 
7, 7 5, 5 5)))
 
 ## ST_MakePoint
 
-Introduction: Creates a 2D, 3D Z or 4D ZM Point geometry. Use ST_MakePointM to 
make points with XYM coordinates. Z and M values are optional.
+Introduction: Creates a 2D, 3D Z or 4D ZM Point geometry. Use 
[ST_MakePointM](#st_makepointm) to make points with XYM coordinates. Z and M 
values are optional.
 
 Format: `ST_MakePoint (X: Double, Y: Double, Z: Double, M: Double)`
 
@@ -585,6 +585,26 @@ Output:
 POINT ZM (1.2345 2.3456 3.4567 4)
 ```
 
+## ST_MakePointM
+
+Introduction: Creates a point with X, Y, and M coordinate. Use 
[ST_MakePoint](#st_makepoint) to make points with XY, XYZ, or XYZM coordinates.
+
+Format: `ST_MakePointM(x: Double, y: Double, m: Double)`
+
+Since: `vTBD`
+
+Example:
+
+```sql
+SELECT ST_MakePointM(1, 2, 3)
+```
+
+Output:
+
+```
+Point M(1 2 3)
+```
+
 ## ST_Point
 
 Introduction: Construct a Point from X and Y
diff --git a/docs/api/sql/Constructor.md b/docs/api/sql/Constructor.md
index 022a77a71..d92c16c9f 100644
--- a/docs/api/sql/Constructor.md
+++ b/docs/api/sql/Constructor.md
@@ -602,7 +602,7 @@ MULTIPOLYGON (((0 0, 20 0, 20 20, 0 20, 0 0), (5 5, 5 7, 7 
7, 7 5, 5 5)))
 
 ## ST_MakePoint
 
-Introduction: Creates a 2D, 3D Z or 4D ZM Point geometry. Use ST_MakePointM to 
make points with XYM coordinates. Z and M values are optional.
+Introduction: Creates a 2D, 3D Z or 4D ZM Point geometry. Use 
[ST_MakePointM](#st_makepointm) to make points with XYM coordinates. Z and M 
values are optional.
 
 Format: `ST_MakePoint (X: Double, Y: Double, Z: Double, M: Double)`
 
@@ -644,6 +644,26 @@ Output:
 POINT ZM (1.2345 2.3456 3.4567 4)
 ```
 
+## ST_MakePointM
+
+Introduction: Creates a point with X, Y, and M coordinate. Use 
[ST_MakePoint](#st_makepoint) to make points with XY, XYZ, or XYZM coordinates.
+
+Format: `ST_MakePointM(x: Double, y: Double, m: Double)`
+
+Since: `vTBD`
+
+Example:
+
+```sql
+SELECT ST_MakePointM(1, 2, 3)
+```
+
+Output:
+
+```
+Point M(1 2 3)
+```
+
 ## ST_Point
 
 Introduction: Construct a Point from X and Y
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 cd6483d90..051a0ebe5 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -32,6 +32,7 @@ public class Catalog {
                 new Constructors.ST_LineFromWKB(),
                 new Constructors.ST_LinestringFromWKB(),
                 new Constructors.ST_MakePoint(),
+                new Constructors.ST_MakePointM(),
                 new Constructors.ST_LineStringFromText(),
                 new Constructors.ST_LineFromText(),
                 new Constructors.ST_PolygonFromText(),
diff --git 
a/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java 
b/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
index 538ee5ce7..1dd062e48 100644
--- a/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
+++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Constructors.java
@@ -88,6 +88,13 @@ public class Constructors {
         }
     }
 
+    public static class ST_MakePointM extends ScalarFunction {
+        @DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class)
+        public Geometry eval(@DataTypeHint("Double") Double x, 
@DataTypeHint("Double") Double y, @DataTypeHint("Double") Double m) throws 
ParseException {
+            return org.apache.sedona.common.Constructors.makePointM(x, y, m);
+        }
+    }
+
     public static class ST_MakePoint extends ScalarFunction {
         @DataTypeHint(value = "RAW", bridgedTo = 
org.locationtech.jts.geom.Geometry.class)
         public Geometry eval(@DataTypeHint("Double") Double x, 
@DataTypeHint("Double") Double y) throws ParseException {
diff --git a/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java 
b/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
index e093a7c04..287bced0c 100644
--- a/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/ConstructorTest.java
@@ -153,6 +153,32 @@ public class ConstructorTest extends TestBase{
         assertEquals(100.0, result.getCoordinate().getM(), 1e-6);
     }
 
+    @Test
+    public void testMakePointM() {
+        List<Row> data = new ArrayList<>();
+        data.add(Row.of(1.0, 2.0, 3.0, "pointM"));
+        String[] colNames = new String[]{"x", "y", "m", "name_point"};
+
+        TypeInformation<?>[] colTypes = {
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.DOUBLE_TYPE_INFO,
+                BasicTypeInfo.STRING_TYPE_INFO};
+        RowTypeInfo typeInfo = new RowTypeInfo(colTypes, colNames);
+        DataStream<Row> ds = env.fromCollection(data).returns(typeInfo);
+        Table pointTable = tableEnv.fromDataStream(ds);
+
+        Table geomTable = pointTable
+                .select(call(Constructors.ST_MakePointM.class.getSimpleName(), 
$(colNames[0]), $(colNames[1]), $(colNames[2]))
+                        ).as(colNames[3]);
+
+        String result = (String) 
first(geomTable.select(call(Functions.ST_AsText.class.getSimpleName(), 
$(colNames[3]))))
+                .getField(0);
+
+        String expected = "POINT M(1 2 3)";
+        assertEquals(expected, result);
+    }
+
     @Test
     public void testMakePoint() {
         List<Row> data = new ArrayList<>();
diff --git a/python/sedona/sql/st_constructors.py 
b/python/sedona/sql/st_constructors.py
index 733ef42b5..617722c2d 100644
--- a/python/sedona/sql/st_constructors.py
+++ b/python/sedona/sql/st_constructors.py
@@ -328,6 +328,21 @@ def ST_LinestringFromWKB(wkb: ColumnOrName, srid: 
Optional[ColumnOrNameOrNumber]
     args = (wkb) if srid is None else (wkb, srid)
     return _call_constructor_function("ST_LinestringFromWKB", args)
 
+@validate_argument_types
+def ST_MakePointM(x: ColumnOrNameOrNumber, y: ColumnOrNameOrNumber, m: 
ColumnOrNameOrNumber) -> Column:
+    """Generate 3D M Point geometry.
+
+        :param x: Either a number or numeric column representing the X 
coordinate of a point.
+        :type x: ColumnOrNameOrNumber
+        :param y: Either a number or numeric column representing the Y 
coordinate of a point.
+        :type y: ColumnOrNameOrNumber
+        :param m: Either a number or numeric column representing the M 
coordinate of a point
+        :type m: ColumnOrNameOrNumber
+        :return: Point geometry column generated from the coordinate values.
+        :rtype: Column
+        """
+    return _call_constructor_function("ST_MakePointM", (x, y, m))
+
 @validate_argument_types
 def ST_MakePoint(x: ColumnOrNameOrNumber, y: ColumnOrNameOrNumber, z: 
Optional[ColumnOrNameOrNumber] = None, m: Optional[ColumnOrNameOrNumber] = 
None) -> Column:
     """Generate a 2D, 3D Z or 4D ZM Point geometry. If z is None then a 2D 
point is generated.
diff --git a/python/tests/sql/test_constructor_test.py 
b/python/tests/sql/test_constructor_test.py
index 678c2eae8..9e994595d 100644
--- a/python/tests/sql/test_constructor_test.py
+++ b/python/tests/sql/test_constructor_test.py
@@ -41,6 +41,20 @@ class TestConstructors(TestBase):
         point_df = self.spark.sql("SELECT ST_PointM(1.2345, 2.3456, 3.4567)")
         assert point_df.count() == 1
 
+    def test_st_makepointm(self):
+        point_csv_df = self.spark.read.format("csv").\
+            option("delimiter", ",").\
+            option("header", "false").\
+            load(csv_point_input_location)
+
+        point_csv_df.createOrReplaceTempView("pointtable")
+
+        point_df = self.spark.sql("select ST_MakePointM(cast(pointtable._c0 as 
Decimal(24,20)), cast(pointtable._c1 as Decimal(24,20)), 2.0) as arealandmark 
from pointtable")
+        assert point_df.count() == 1000
+
+        point_df = self.spark.sql("SELECT ST_AsText(ST_MakePointM(1.2345, 
2.3456, 3.4567))")
+        assert point_df.take(1)[0][0] == "POINT M(1.2345 2.3456 3.4567)"
+
     def test_st_makepoint(self):
         point_csv_df = self.spark.read.format("csv").\
             option("delimiter", ",").\
diff --git a/python/tests/sql/test_dataframe_api.py 
b/python/tests/sql/test_dataframe_api.py
index f4a07f04c..060d675eb 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -66,6 +66,7 @@ test_configurations = [
     (stc.ST_PointFromText, ("single_point", lambda: f.lit(',')), 
"constructor", "", "POINT (0 1)"),
     (stc.ST_PointFromWKB, ("wkbPoint",), "constructor", "", "POINT (10 15)"),
     (stc.ST_MakePoint, ("x", "y", "z"), "constructor", "", "POINT Z (0 1 2)"),
+    (stc.ST_MakePointM, ("x", "y", "z"), "constructor", "ST_AsText(geom)", 
"POINT M(0 1 2)"),
     (stc.ST_PolygonFromEnvelope, ("minx", "miny", "maxx", "maxy"), 
"min_max_x_y", "", "POLYGON ((0 1, 0 3, 2 3, 2 1, 0 1))"),
     (stc.ST_PolygonFromEnvelope, (0.0, 1.0, 2.0, 3.0), "null", "", "POLYGON 
((0 1, 0 3, 2 3, 2 1, 0 1))"),
     (stc.ST_PolygonFromText, ("multiple_point", lambda: f.lit(',')), 
"constructor", "", "POLYGON ((0 0, 1 0, 1 1, 0 0))"),
@@ -277,6 +278,8 @@ wrong_type_configurations = [
     (stc.ST_PolygonFromText, ("", None)),
     (stc.ST_PolygonFromText, (None, None)),
     (stc.ST_PolygonFromText, ("", None)),
+    (stc.ST_MakePointM, (None, None, None)),
+    (stc.ST_MakePointM, (None, "", "")),
 
     # functions
     (stf.ST_3DDistance, (None, "")),
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 e0fb8a1db..846059e68 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
@@ -57,6 +57,7 @@ object Catalog {
     function[ST_Point](),
     function[ST_Points](),
     function[ST_MakePoint](null, null),
+    function[ST_MakePointM](),
     function[ST_PointZ](0),
     function[ST_PointM](0),
     function[ST_PointZM](0),
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
index aeb8073a1..5d1381d60 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Constructors.scala
@@ -413,6 +413,14 @@ case class ST_PointZM(inputExpressions: Seq[Expression])
   }
 }
 
+case class ST_MakePointM(inputExpressions: Seq[Expression])
+  extends InferredExpression(Constructors.makePointM _) {
+
+  protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = 
{
+    copy(inputExpressions = newChildren)
+  }
+}
+
 case class ST_MakePoint(inputExpressions: Seq[Expression])
   extends 
InferredExpression(nullTolerantInferrableFunction4(Constructors.makePoint)) {
 
diff --git 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
index dd8b2577a..e5f3659df 100644
--- 
a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
+++ 
b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_constructors.scala
@@ -132,6 +132,10 @@ object st_constructors extends DataFrameAPI {
   def ST_LinestringFromWKB(wkb: Column, srid: Column): Column = 
wrapExpression[ST_LinestringFromWKB](wkb, srid)
   def ST_LinestringFromWKB(wkb: String, srid: Int): Column = 
wrapExpression[ST_LinestringFromWKB](wkb, srid)
 
+  def ST_MakePointM(x: Column, y: Column, m: Column): Column = 
wrapExpression[ST_MakePointM](x, y, m)
+  def ST_MakePointM(x: String, y: String, m: String): Column = 
wrapExpression[ST_MakePointM](x, y, m)
+  def ST_MakePointM(x: Double, y: Double, m: Double): Column = 
wrapExpression[ST_MakePointM](x, y, m)
+
   def ST_MakePoint(x: Column, y: Column): Column = 
wrapExpression[ST_MakePoint](x, y, null, null)
   def ST_MakePoint(x: String, y: String): Column = 
wrapExpression[ST_MakePoint](x, y, null, null)
   def ST_MakePoint(x: Double, y: Double): Column = 
wrapExpression[ST_MakePoint](x, y, null, null)
diff --git 
a/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala 
b/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
index 8d39b6b4b..d8f97abdd 100644
--- 
a/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
+++ 
b/spark/common/src/test/scala/org/apache/sedona/sql/constructorTestScala.scala
@@ -49,6 +49,14 @@ class constructorTestScala extends TestBaseScala {
       assert(pointDf.count() == 1)
     }
 
+    it("Passed ST_MakePointM") {
+      val pointCsvDF = sparkSession.read.format("csv").option("delimiter", 
",").option("header", "false").load(csvPointInputLocation)
+      pointCsvDF.createOrReplaceTempView("pointtable")
+
+      val pointDf = sparkSession.sql("select ST_MakePointM(cast(pointtable._c0 
as Decimal(24,20)), cast(pointtable._c1 as Decimal(24,20)), 2.0) as 
arealandmark from pointtable")
+      assert(pointDf.count() == 1000)
+    }
+
     it("Passed ST_MakePoint") {
 
       var pointCsvDF = sparkSession.read.format("csv").option("delimiter", 
",").option("header", "false").load(csvPointInputLocation)
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 8494fa092..cf663474b 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
@@ -98,6 +98,13 @@ class dataFrameAPITestScala extends TestBaseScala {
       assertEquals("SRID=4326;POINT ZM(1 2 0 100)", point2)
     }
 
+    it("passed ST_MakePointM") {
+      val df = sparkSession.sql("SELECT 0.0 AS x, 1.0 AS y, 2.0 AS 
m").select(ST_AsText(ST_MakePointM("x", "y", "m")))
+      val actualResult = df.take(1)(0).get(0).asInstanceOf[String]
+      val expectedResult = "POINT M(0 1 2)"
+      assert(actualResult == expectedResult)
+    }
+
     it("passed st_makepoint") {
       val df = sparkSession.sql("SELECT 0.0 AS x, 1.0 AS y, 2.0 AS 
z").select(ST_AsText(ST_MakePoint("x", "y", "z")))
       val actualResult = df.take(1)(0).get(0).asInstanceOf[String]

Reply via email to