This is an automated email from the ASF dual-hosted git repository. jiayu pushed a commit to branch SEDONA-593 in repository https://gitbox.apache.org/repos/asf/sedona.git
commit b4c064380952605ed3211f65a9e9e3ce9abd3c65 Author: Furqaan Khan <[email protected]> AuthorDate: Fri May 3 23:12:18 2024 -0400 feat: Add ST_RelateMatch (#181) --- .../java/org/apache/sedona/common/Predicates.java | 4 ++++ .../org/apache/sedona/common/PredicatesTest.java | 17 ++++++++++++++++ docs/api/flink/Predicate.md | 23 ++++++++++++++++++++++ docs/api/snowflake/vector-data/Predicate.md | 21 ++++++++++++++++++++ docs/api/sql/Predicate.md | 23 ++++++++++++++++++++++ .../main/java/org/apache/sedona/flink/Catalog.java | 1 + .../sedona/flink/expressions/Predicates.java | 14 +++++++++++++ .../org/apache/sedona/flink/PredicateTest.java | 7 +++++++ python/sedona/sql/st_predicates.py | 13 ++++++++++++ python/tests/sql/test_dataframe_api.py | 3 +++ python/tests/sql/test_predicate.py | 4 ++++ .../sedona/snowflake/snowsql/TestPredicates.java | 9 +++++++++ .../sedona/snowflake/snowsql/TestPredicatesV2.java | 9 +++++++++ .../org/apache/sedona/snowflake/snowsql/UDFs.java | 5 +++++ .../apache/sedona/snowflake/snowsql/UDFsV2.java | 5 +++++ .../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 + .../sql/sedona_sql/expressions/Predicates.scala | 8 ++++++++ .../sql/sedona_sql/expressions/st_predicates.scala | 3 +++ .../apache/sedona/sql/dataFrameAPITestScala.scala | 6 ++++++ .../org/apache/sedona/sql/predicateTestScala.scala | 5 +++++ 20 files changed, 181 insertions(+) diff --git a/common/src/main/java/org/apache/sedona/common/Predicates.java b/common/src/main/java/org/apache/sedona/common/Predicates.java index 2994731d6..0eb0fa904 100644 --- a/common/src/main/java/org/apache/sedona/common/Predicates.java +++ b/common/src/main/java/org/apache/sedona/common/Predicates.java @@ -72,4 +72,8 @@ public class Predicates { String matrixFromGeom = relate(leftGeometry, rightGeometry); return IntersectionMatrix.matches(matrixFromGeom, intersectionMatrix); } + + public static boolean relateMatch(String matrix1, String matrix2) { + return IntersectionMatrix.matches(matrix1, matrix2); + } } diff --git a/common/src/test/java/org/apache/sedona/common/PredicatesTest.java b/common/src/test/java/org/apache/sedona/common/PredicatesTest.java index 87e16bd81..e333671c6 100644 --- a/common/src/test/java/org/apache/sedona/common/PredicatesTest.java +++ b/common/src/test/java/org/apache/sedona/common/PredicatesTest.java @@ -118,6 +118,23 @@ public class PredicatesTest extends TestBase { assertTrue(actual); } + @Test + public void testRelateMatch() { + String matrix1 = "101202FFF"; + String matrix2 = "TTTTTTFFF"; + boolean actual = Predicates.relateMatch(matrix1, matrix2); + assertTrue(actual); + + matrix2 = "TTFTTTFFF"; + actual = Predicates.relateMatch(matrix1, matrix2); + assertFalse(actual); + + matrix1 = "FF1FF0102"; + matrix2 = "FF1F***02"; + actual = Predicates.relateMatch(matrix1, matrix2); + assertTrue(actual); + } + @Test public void testCrossesDateLine() throws ParseException { Geometry geom1 = geomFromEWKT("LINESTRING(170 30, -170 30)"); diff --git a/docs/api/flink/Predicate.md b/docs/api/flink/Predicate.md index 7359e8094..9ae317196 100644 --- a/docs/api/flink/Predicate.md +++ b/docs/api/flink/Predicate.md @@ -206,6 +206,29 @@ Output: true ``` +## ST_RelateMatch + +Introduction: This function tests the relationship between two [Dimensionally Extended 9-Intersection Model (DE-9IM)](https://en.wikipedia.org/wiki/DE-9IM) matrices representing geometry intersections. It evaluates whether the DE-9IM matrix specified in `matrix1` satisfies the intersection pattern defined by `matrix2`. The `matrix2` parameter can be an exact DE-9IM value or a pattern containing wildcard characters. + +!!!Note + It is important to note that this function is not optimized for use in spatial join operations. Certain DE-9IM relationships can hold true for geometries that do not intersect or are disjoint. As a result, it is recommended to utilize other dedicated spatial functions specifically optimized for spatial join processing. + +Format: `ST_RelateMatch(matrix1: String, matrix2: String)` + +Since: `vTBD` + +SQL Example: + +```sql +SELECT ST_RelateMatch('101202FFF', 'TTTTTTFFF') +``` + +Output: + +``` +true +``` + ## ST_Touches Introduction: Return true if A touches B diff --git a/docs/api/snowflake/vector-data/Predicate.md b/docs/api/snowflake/vector-data/Predicate.md index d6473a350..f08d63132 100644 --- a/docs/api/snowflake/vector-data/Predicate.md +++ b/docs/api/snowflake/vector-data/Predicate.md @@ -171,6 +171,27 @@ Output: true ``` +## ST_RelateMatch + +Introduction: This function tests the relationship between two [Dimensionally Extended 9-Intersection Model (DE-9IM)](https://en.wikipedia.org/wiki/DE-9IM) matrices representing geometry intersections. It evaluates whether the DE-9IM matrix specified in `matrix1` satisfies the intersection pattern defined by `matrix2`. The `matrix2` parameter can be an exact DE-9IM value or a pattern containing wildcard characters. + +!!!Note + It is important to note that this function is not optimized for use in spatial join operations. Certain DE-9IM relationships can hold true for geometries that do not intersect or are disjoint. As a result, it is recommended to utilize other dedicated spatial functions specifically optimized for spatial join processing. + +Format: `ST_RelateMatch(matrix1: String, matrix2: String)` + +SQL Example: + +```sql +SELECT ST_RelateMatch('101202FFF', 'TTTTTTFFF') +``` + +Output: + +``` +true +``` + ## ST_Touches Introduction: Return true if A touches B diff --git a/docs/api/sql/Predicate.md b/docs/api/sql/Predicate.md index a9f29a532..ffdcb0b75 100644 --- a/docs/api/sql/Predicate.md +++ b/docs/api/sql/Predicate.md @@ -238,6 +238,29 @@ Output: true ``` +## ST_RelateMatch + +Introduction: This function tests the relationship between two [Dimensionally Extended 9-Intersection Model (DE-9IM)](https://en.wikipedia.org/wiki/DE-9IM) matrices representing geometry intersections. It evaluates whether the DE-9IM matrix specified in `matrix1` satisfies the intersection pattern defined by `matrix2`. The `matrix2` parameter can be an exact DE-9IM value or a pattern containing wildcard characters. + +!!!Note + It is important to note that this function is not optimized for use in spatial join operations. Certain DE-9IM relationships can hold true for geometries that do not intersect or are disjoint. As a result, it is recommended to utilize other dedicated spatial functions specifically optimized for spatial join processing. + +Format: `ST_RelateMatch(matrix1: String, matrix2: String)` + +Since: `vTBD` + +SQL Example: + +```sql +SELECT ST_RelateMatch('101202FFF', 'TTTTTTFFF') +``` + +Output: + +``` +true +``` + ## ST_Touches Introduction: Return true if A touches B 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 bdea8749e..b40e48080 100644 --- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java +++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java @@ -198,6 +198,7 @@ public class Catalog { new Predicates.ST_Overlaps(), new Predicates.ST_Touches(), new Predicates.ST_Relate(), + new Predicates.ST_RelateMatch(), new Predicates.ST_DWithin() }; } diff --git a/flink/src/main/java/org/apache/sedona/flink/expressions/Predicates.java b/flink/src/main/java/org/apache/sedona/flink/expressions/Predicates.java index d353cdfd1..ad0aea5d0 100644 --- a/flink/src/main/java/org/apache/sedona/flink/expressions/Predicates.java +++ b/flink/src/main/java/org/apache/sedona/flink/expressions/Predicates.java @@ -240,6 +240,20 @@ public class Predicates { } } + public static class ST_RelateMatch extends ScalarFunction { + /** + * Constructor for relation checking without duplicate removal + */ + public ST_RelateMatch() { + } + + @DataTypeHint("Boolean") + public Boolean eval(@DataTypeHint("String") String matrix1, + @DataTypeHint("String") String matrix2) { + return org.apache.sedona.common.Predicates.relateMatch(matrix1, matrix2); + } + } + public static class ST_Touches extends ScalarFunction { diff --git a/flink/src/test/java/org/apache/sedona/flink/PredicateTest.java b/flink/src/test/java/org/apache/sedona/flink/PredicateTest.java index 561a86ec9..82b0998bb 100644 --- a/flink/src/test/java/org/apache/sedona/flink/PredicateTest.java +++ b/flink/src/test/java/org/apache/sedona/flink/PredicateTest.java @@ -141,6 +141,13 @@ public class PredicateTest extends TestBase{ assertEquals(true, actualBoolean); } + @Test + public void testRelateMatch() { + Table table = tableEnv.sqlQuery("SELECT '101202FFF' as matrix1, 'TTTTTTFFF' as matrix2"); + Boolean actual = (Boolean) first(table.select(call(Predicates.ST_RelateMatch.class.getSimpleName(), $("matrix1"), $("matrix2")))).getField(0); + assertEquals(true, actual); + } + @Test public void testDWithin() { Table table = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('POINT (0 0)') as origin, ST_GeomFromWKT('POINT (1 0)') as p1"); diff --git a/python/sedona/sql/st_predicates.py b/python/sedona/sql/st_predicates.py index 4ffd721f6..1969c8b5e 100644 --- a/python/sedona/sql/st_predicates.py +++ b/python/sedona/sql/st_predicates.py @@ -160,6 +160,19 @@ def ST_Relate(a: ColumnOrName, b: ColumnOrName, intersectionMatrix: Optional[Col return _call_predicate_function("ST_Relate", args) +@validate_argument_types +def ST_RelateMatch(matrix1: ColumnOrName, matrix2: ColumnOrName) -> Column: + """Check whether two DE-9IM are related to each other. + + :param matrix1: One geometry column to check. + :type matrix1: ColumnOrName + :param matrix2: Other geometry column to check. + :type matrix2: ColumnOrName + :return: True if a and b touch and False otherwise, as a boolean column. + :rtype: Column + """ + return _call_predicate_function("ST_RelateMatch", (matrix1, matrix2)) + @validate_argument_types def ST_Within(a: ColumnOrName, b: ColumnOrName) -> Column: diff --git a/python/tests/sql/test_dataframe_api.py b/python/tests/sql/test_dataframe_api.py index fe176e654..a39bc4885 100644 --- a/python/tests/sql/test_dataframe_api.py +++ b/python/tests/sql/test_dataframe_api.py @@ -225,6 +225,7 @@ test_configurations = [ (stp.ST_Touches, ("a", "b"), "touching_polys", "", True), (stp.ST_Relate, ("a", "b"), "touching_polys", "", "FF2F11212"), (stp.ST_Relate, ("a", "b", lambda: f.lit("FF2F11212")), "touching_polys", "", True), + (stp.ST_RelateMatch, (lambda: f.lit("101202FFF"), lambda: f.lit("TTTTTTFFF")), "touching_polys", "", True), (stp.ST_Within, (lambda: f.expr("ST_Point(0.5, 0.25)"), "geom"), "triangle_geom", "", True), (stp.ST_Covers, ("geom", lambda: f.expr("ST_Point(0.5, 0.25)")), "triangle_geom", "", True), (stp.ST_CoveredBy, (lambda: f.expr("ST_Point(0.5, 0.25)"), "geom"), "triangle_geom", "", True), @@ -428,6 +429,8 @@ wrong_type_configurations = [ (stp.ST_Touches, ("", None)), (stp.ST_Relate, (None, "")), (stp.ST_Relate, ("", None)), + (stp.ST_RelateMatch, (None, "")), + (stp.ST_RelateMatch, ("", None)), (stp.ST_Within, (None, "")), (stp.ST_Within, ("", None)), diff --git a/python/tests/sql/test_predicate.py b/python/tests/sql/test_predicate.py index 93cc43b76..504ef81f1 100644 --- a/python/tests/sql/test_predicate.py +++ b/python/tests/sql/test_predicate.py @@ -207,6 +207,10 @@ class TestPredicate(TestBase): actual = baseDf.selectExpr("ST_Relate(g1, g2, im)").take(1)[0][0] assert actual + def test_st_relate_match(self): + actual = self.spark.sql("SELECT ST_RelateMatch('101202FFF', 'TTTTTTFFF') ").take(1)[0][0] + assert actual + def test_st_overlaps(self): test_table = self.spark.sql( "select ST_GeomFromWKT('POLYGON((2.5 2.5, 2.5 4.5, 4.5 4.5, 4.5 2.5, 2.5 2.5))') as a,ST_GeomFromWKT('POLYGON((4 4, 4 6, 6 6, 6 4, 4 4))') as b, ST_GeomFromWKT('POLYGON((5 5, 4 6, 6 6, 6 4, 5 5))') as c, ST_GeomFromWKT('POLYGON((5 5, 4 6, 6 6, 6 4, 5 5))') as d") diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestPredicates.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestPredicates.java index 161c6a208..4095b0261 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestPredicates.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestPredicates.java @@ -130,6 +130,15 @@ public class TestPredicates extends TestBase{ true ); } + + @Test + public void test_ST_RelateMatch() { + registerUDF("ST_RelateMatch", String.class, String.class); + verifySqlSingleRes( + "SELECT SEDONA.ST_RelateMatch('101202FFF', 'TTTTTTFFF')", + true + ); + } @Test public void test_ST_Within() { registerUDF("ST_Within", byte[].class, byte[].class); diff --git a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestPredicatesV2.java b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestPredicatesV2.java index 2f3c23490..56917c9f7 100644 --- a/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestPredicatesV2.java +++ b/snowflake-tester/src/test/java/org/apache/sedona/snowflake/snowsql/TestPredicatesV2.java @@ -133,6 +133,15 @@ public class TestPredicatesV2 ); } + @Test + public void test_ST_RelateMatch() { + registerUDFV2("ST_RelateMatch", String.class, String.class); + verifySqlSingleRes( + "SELECT SEDONA.ST_RelateMatch('101202FFF', 'TTTTTTFFF')", + true + ); + } + @Test public void test_ST_Within() { registerUDFV2("ST_Within", String.class, String.class); diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java index e58020f17..19d897d1b 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFs.java @@ -1264,6 +1264,11 @@ public class UDFs { ); } + @UDFAnnotations.ParamMeta(argNames = {"matrix1", "matrix2"}) + public static Boolean ST_RelateMatch(String matrix1, String matrix2) { + return Predicates.relateMatch(matrix1, matrix2); + } + @UDFAnnotations.ParamMeta(argNames = {"geometry", "sourceCRS", "targetCRS"}) public static byte[] ST_Transform(byte[] geometry, String sourceCRS, String targetCRS) { return GeometrySerde.serialize( diff --git a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java index 1345d10c2..b307f509b 100644 --- a/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java +++ b/snowflake/src/main/java/org/apache/sedona/snowflake/snowsql/UDFsV2.java @@ -1051,6 +1051,11 @@ public class UDFsV2 ); } + @UDFAnnotations.ParamMeta(argNames = {"matrix1", "matrix2"}, argTypes = {"String", "String"}) + public static boolean ST_RelateMatch(String matrix1, String matrix2) { + return Predicates.relateMatch(matrix1, matrix2); + } + @UDFAnnotations.ParamMeta(argNames = {"geometry", "sourceCRS", "targetCRS"}, argTypes = {"Geometry", "String", "String"}, returnTypes = "Geometry") public static String ST_Transform(String geometry, String sourceCRS, String targetCRS) { return GeometrySerde.serGeoJson( 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 298bb3cf1..4a4550c4c 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 @@ -92,6 +92,7 @@ object Catalog { function[ST_Equals](), function[ST_Touches](), function[ST_Relate](), + function[ST_RelateMatch](), function[ST_Overlaps](), function[ST_Crosses](), function[ST_CrossesDateLine](), diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Predicates.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Predicates.scala index 9d601a28e..eb41e704f 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Predicates.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/Predicates.scala @@ -208,6 +208,14 @@ case class ST_Relate(inputExpressions: Seq[Expression]) } } +case class ST_RelateMatch(inputExpressions: Seq[Expression]) + extends InferredExpression(inferrableFunction2(Predicates.relateMatch)) { + + protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = { + copy(inputExpressions = newChildren) + } +} + /** * Test if leftGeometry is equal to rightGeometry * diff --git a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_predicates.scala b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_predicates.scala index 61ed641dd..1ac8cb15b 100644 --- a/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_predicates.scala +++ b/spark/common/src/main/scala/org/apache/spark/sql/sedona_sql/expressions/st_predicates.scala @@ -51,6 +51,9 @@ object st_predicates extends DataFrameAPI { def ST_Relate(a: Column, b: Column, intersectionMatrix: Column): Column = wrapExpression[ST_Relate](a, b, intersectionMatrix) def ST_Relate(a: String, b: String, intersectionMatrix: String): Column = wrapExpression[ST_Relate](a, b, intersectionMatrix) + def ST_RelateMatch(a: Column, b: Column): Column = wrapExpression[ST_RelateMatch](a, b) + def ST_RelateMatch(a: String, b: String): Column = wrapExpression[ST_RelateMatch](a, b) + def ST_Within(a: Column, b: Column): Column = wrapExpression[ST_Within](a, b) def ST_Within(a: String, b: String): Column = wrapExpression[ST_Within](a, b) 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 da9bacd3c..cf3263f4b 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 @@ -1181,6 +1181,12 @@ class dataFrameAPITestScala extends TestBaseScala { assert(actualBoolean) } + it("Passed ST_RelateMatch") { + val baseDf = sparkSession.sql("SELECT '101202FFF' as matrix1, 'TTTTTTFFF' as matrix2") + val actual = baseDf.select(ST_RelateMatch("matrix1", "matrix2")).first().getBoolean(0) + assert(actual) + } + it("Passed ST_Touches") { val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON ((0 0, 1 0, 1 1, 0 0))') AS a, ST_GeomFromWKT('POLYGON ((1 1, 1 0, 2 0, 1 1))') AS b") val df = baseDf.select(ST_Touches("a", "b")) diff --git a/spark/common/src/test/scala/org/apache/sedona/sql/predicateTestScala.scala b/spark/common/src/test/scala/org/apache/sedona/sql/predicateTestScala.scala index 006910d84..0dc5be9be 100644 --- a/spark/common/src/test/scala/org/apache/sedona/sql/predicateTestScala.scala +++ b/spark/common/src/test/scala/org/apache/sedona/sql/predicateTestScala.scala @@ -203,6 +203,11 @@ class predicateTestScala extends TestBaseScala { assert(actualBoolean) } + it("Passed ST_RelateMatch") { + val actual = sparkSession.sql("SELECT ST_RelateMatch('101202FFF', 'TTTTTTFFF')").first().getBoolean(0) + assert(actual) + } + it("Passed ST_Touches") { var pointCsvDF = sparkSession.read.format("csv").option("delimiter", ",").option("header", "false").load(csvPointInputLocation) pointCsvDF.createOrReplaceTempView("pointtable")
