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]
