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 86378f030 [SEDONA-593] [SEDONA-594] Add ST_Relate, ST_RelateMatch
(#1465)
86378f030 is described below
commit 86378f0301b75cade8077ce68c3ecc7a1e72bbe6
Author: Jia Yu <[email protected]>
AuthorDate: Fri Jun 7 13:28:12 2024 -0700
[SEDONA-593] [SEDONA-594] Add ST_Relate, ST_RelateMatch (#1465)
* [TASK-131] Add ST_Relate (#180)
* add: ST_Relate
* fix: snowflake udf registration
* Update version
* feat: Add ST_RelateMatch (#181)
* Update versions
---------
Co-authored-by: Furqaan Khan <[email protected]>
---
.../java/org/apache/sedona/common/Predicates.java | 13 ++++
.../org/apache/sedona/common/PredicatesTest.java | 59 ++++++++++++++++++
docs/api/flink/Predicate.md | 71 ++++++++++++++++++++++
docs/api/snowflake/vector-data/Predicate.md | 67 ++++++++++++++++++++
docs/api/sql/Predicate.md | 71 ++++++++++++++++++++++
.../main/java/org/apache/sedona/flink/Catalog.java | 2 +
.../sedona/flink/expressions/Predicates.java | 40 ++++++++++++
.../org/apache/sedona/flink/PredicateTest.java | 17 ++++++
python/sedona/sql/st_predicates.py | 30 +++++++++
python/tests/sql/test_dataframe_api.py | 7 +++
python/tests/sql/test_predicate.py | 12 ++++
.../sedona/snowflake/snowsql/TestPredicates.java | 23 +++++++
.../sedona/snowflake/snowsql/TestPredicatesV2.java | 25 ++++++++
.../org/apache/sedona/snowflake/snowsql/UDFs.java | 22 +++++++
.../apache/sedona/snowflake/snowsql/UDFsV2.java | 22 +++++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 2 +
.../sql/sedona_sql/expressions/Predicates.scala | 16 +++++
.../sql/sedona_sql/expressions/st_predicates.scala | 8 +++
.../apache/sedona/sql/dataFrameAPITestScala.scala | 15 +++++
.../org/apache/sedona/sql/predicateTestScala.scala | 14 +++++
20 files changed, 536 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 1027fe4be..0eb0fa904 100644
--- a/common/src/main/java/org/apache/sedona/common/Predicates.java
+++ b/common/src/main/java/org/apache/sedona/common/Predicates.java
@@ -15,6 +15,7 @@ package org.apache.sedona.common;
import org.locationtech.jts.geom.*;
import org.apache.sedona.common.sphere.Spheroid;
+import org.locationtech.jts.operation.relate.RelateOp;
public class Predicates {
public static boolean contains(Geometry leftGeometry, Geometry
rightGeometry) {
@@ -63,4 +64,16 @@ public class Predicates {
}
}
+ public static String relate(Geometry leftGeometry, Geometry rightGeometry)
{
+ return RelateOp.relate(leftGeometry, rightGeometry).toString();
+ }
+
+ public static boolean relate(Geometry leftGeometry, Geometry
rightGeometry, String intersectionMatrix) {
+ 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 4e6ea7155..e333671c6 100644
--- a/common/src/test/java/org/apache/sedona/common/PredicatesTest.java
+++ b/common/src/test/java/org/apache/sedona/common/PredicatesTest.java
@@ -76,6 +76,65 @@ public class PredicatesTest extends TestBase {
assertTrue(actual);
}
+ @Test
+ public void testRelateString() throws ParseException {
+ Geometry geom1 = geomFromEWKT("POINT(1 2)");
+ Geometry geom2 = Functions.buffer(geomFromEWKT("POINT(1 2)"), 2);
+ String actual = Predicates.relate(geom1, geom2);
+ assertEquals("0FFFFF212", actual);
+
+ geom1 = geomFromEWKT("LINESTRING(1 2, 3 4)");
+ geom2 = geomFromEWKT("LINESTRING(5 6, 7 8)");
+ actual = Predicates.relate(geom1, geom2);
+ assertEquals("FF1FF0102", actual);
+
+ geom1 = geomFromEWKT("LINESTRING (1 1, 5 5)");
+ geom2 = geomFromEWKT("POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))");
+ actual = Predicates.relate(geom1, geom2);
+ assertEquals("1010F0212", actual);
+ }
+
+ @Test
+ public void testRelateBoolean() throws ParseException {
+ Geometry geom1 = geomFromEWKT("POINT(1 2)");
+ Geometry geom2 = Functions.buffer(geomFromEWKT("POINT(1 2)"), 2);
+ boolean actual = Predicates.relate(geom1, geom2, "0FFFFF212");
+ assertTrue(actual);
+
+ actual = Predicates.relate(geom1, geom2, "0F0FFF212");
+ assertFalse(actual);
+
+ geom1 = geomFromEWKT("LINESTRING(1 2, 3 4)");
+ geom2 = geomFromEWKT("LINESTRING(5 6, 7 8)");
+ actual = Predicates.relate(geom1, geom2, "FF1F***02");
+ assertTrue(actual);
+
+ actual = Predicates.relate(geom1, geom2, "FF10***02");
+ assertFalse(actual);
+
+ geom1 = geomFromEWKT("LINESTRING (1 1, 5 5)");
+ geom2 = geomFromEWKT("POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))");
+ actual = Predicates.relate(geom1, geom2, "1010F0212");
+ 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 c5780ba6c..f4c8514d0 100644
--- a/docs/api/flink/Predicate.md
+++ b/docs/api/flink/Predicate.md
@@ -158,6 +158,77 @@ Output:
true
```
+## ST_Relate
+
+Introduction: The first variant of the function computes and returns the
[Dimensionally Extended 9-Intersection Model
(DE-9IM)](https://en.wikipedia.org/wiki/DE-9IM) matrix string representing the
spatial relationship between the two input geometry objects.
+
+The second variant of the function evaluates whether the two input geometries
satisfy a specific spatial relationship defined by the provided
`intersectionMatrix` pattern.
+
+!!!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_Relate(geom1: Geometry, geom2: Geometry)`
+
+`ST_Relate(geom1: Geometry, geom2: Geometry, intersectionMatrix: String)`
+
+Since: `v1.6.1`
+
+SQL Example
+
+```sql
+SELECT ST_Relate(
+ ST_GeomFromWKT('LINESTRING (1 1, 5 5)'),
+ ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))')
+)
+```
+
+Output:
+
+```
+1010F0212
+```
+
+SQL Example
+
+```sql
+SELECT ST_Relate(
+ ST_GeomFromWKT('LINESTRING (1 1, 5 5)'),
+ ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))'),
+ "1010F0212"
+)
+```
+
+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: `v1.6.1`
+
+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 5a7724698..f08d63132 100644
--- a/docs/api/snowflake/vector-data/Predicate.md
+++ b/docs/api/snowflake/vector-data/Predicate.md
@@ -125,6 +125,73 @@ FROM geom
WHERE ST_Overlaps(geom.geom_a, geom.geom_b)
```
+## ST_Relate
+
+Introduction: The first variant of the function computes and returns the
[Dimensionally Extended 9-Intersection Model
(DE-9IM)](https://en.wikipedia.org/wiki/DE-9IM) matrix string representing the
spatial relationship between the two input geometry objects.
+
+The second variant of the function evaluates whether the two input geometries
satisfy a specific spatial relationship defined by the provided
`intersectionMatrix` pattern.
+
+!!!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_Relate(geom1: Geometry, geom2: Geometry)`
+
+`ST_Relate(geom1: Geometry, geom2: Geometry, intersectionMatrix: String)`
+
+SQL Example
+
+```sql
+SELECT ST_Relate(
+ ST_GeomFromWKT('LINESTRING (1 1, 5 5)'),
+ ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))')
+)
+```
+
+Output:
+
+```
+1010F0212
+```
+
+SQL Example
+
+```sql
+SELECT ST_Relate(
+ ST_GeomFromWKT('LINESTRING (1 1, 5 5)'),
+ ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))'),
+ "1010F0212"
+)
+```
+
+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 417f0114d..561b75571 100644
--- a/docs/api/sql/Predicate.md
+++ b/docs/api/sql/Predicate.md
@@ -190,6 +190,77 @@ Output:
true
```
+## ST_Relate
+
+Introduction: The first variant of the function computes and returns the
[Dimensionally Extended 9-Intersection Model
(DE-9IM)](https://en.wikipedia.org/wiki/DE-9IM) matrix string representing the
spatial relationship between the two input geometry objects.
+
+The second variant of the function evaluates whether the two input geometries
satisfy a specific spatial relationship defined by the provided
`intersectionMatrix` pattern.
+
+!!!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_Relate(geom1: Geometry, geom2: Geometry)`
+
+`ST_Relate(geom1: Geometry, geom2: Geometry, intersectionMatrix: String)`
+
+Since: `v1.6.1`
+
+SQL Example
+
+```sql
+SELECT ST_Relate(
+ ST_GeomFromWKT('LINESTRING (1 1, 5 5)'),
+ ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))')
+)
+```
+
+Output:
+
+```
+1010F0212
+```
+
+SQL Example
+
+```sql
+SELECT ST_Relate(
+ ST_GeomFromWKT('LINESTRING (1 1, 5 5)'),
+ ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))'),
+ "1010F0212"
+)
+```
+
+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: `v1.6.1`
+
+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 e40874356..8e60a8483 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -200,6 +200,8 @@ public class Catalog {
new Predicates.ST_OrderingEquals(),
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 0963999bc..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
@@ -214,6 +214,46 @@ public class Predicates {
}
}
+ public static class ST_Relate extends ScalarFunction {
+ /**
+ * Constructor for relation checking without duplicate removal
+ */
+ public ST_Relate() {
+ }
+
+ @DataTypeHint("String")
+ public String eval(@DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class) Object o1, @DataTypeHint(value =
"RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o2)
+ {
+ Geometry geom1 = (Geometry) o1;
+ Geometry geom2 = (Geometry) o2;
+ return org.apache.sedona.common.Predicates.relate(geom1, geom2);
+ }
+
+ @DataTypeHint("Boolean")
+ public Boolean eval(@DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class) Object o1,
+ @DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class) Object o2,
+ @DataTypeHint("String") String IM)
+ {
+ Geometry geom1 = (Geometry) o1;
+ Geometry geom2 = (Geometry) o2;
+ return org.apache.sedona.common.Predicates.relate(geom1, geom2,
IM);
+ }
+ }
+
+ 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 f4acafb56..82b0998bb 100644
--- a/flink/src/test/java/org/apache/sedona/flink/PredicateTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/PredicateTest.java
@@ -131,6 +131,23 @@ public class PredicateTest extends TestBase{
assertEquals(true, actual);
}
+ @Test
+ public void testRelate() {
+ Table table = tableEnv.sqlQuery("SELECT ST_GeomFromWKT('LINESTRING (1
1, 5 5)') AS g1, ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))') as g2,
'1010F0212' as im");
+ String actual = (String)
first(table.select(call(Predicates.ST_Relate.class.getSimpleName(), $("g1"),
$("g2")))).getField(0);
+ assertEquals("1010F0212", actual);
+
+ Boolean actualBoolean = (Boolean)
first(table.select(call(Predicates.ST_Relate.class.getSimpleName(), $("g1"),
$("g2"), $("im")))).getField(0);
+ 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 5106e0473..1969c8b5e 100644
--- a/python/sedona/sql/st_predicates.py
+++ b/python/sedona/sql/st_predicates.py
@@ -143,6 +143,36 @@ def ST_Touches(a: ColumnOrName, b: ColumnOrName) -> Column:
"""
return _call_predicate_function("ST_Touches", (a, b))
+@validate_argument_types
+def ST_Relate(a: ColumnOrName, b: ColumnOrName, intersectionMatrix:
Optional[ColumnOrName] = None) -> Column:
+ """Check whether two geometries are related to each other.
+
+ :param a: One geometry column to check.
+ :type a: ColumnOrName
+ :param b: Other geometry column to check.
+ :type b: ColumnOrName
+ :param intersectionMatrix: intersectionMatrix column to check
+ :type intersectionMatrix: ColumnOrName
+ :return: True if a and b touch and False otherwise, as a boolean column.
+ :rtype: Column
+ """
+ args = (a, b) if intersectionMatrix is None else (a, b, intersectionMatrix)
+
+ 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 d8804856f..6de3a7699 100644
--- a/python/tests/sql/test_dataframe_api.py
+++ b/python/tests/sql/test_dataframe_api.py
@@ -224,6 +224,9 @@ test_configurations = [
(stp.ST_OrderingEquals, ("line", lambda: f.expr("ST_Reverse(line)")),
"linestring_geom", "", False),
(stp.ST_Overlaps, ("a", "b"), "overlapping_polys", "", True),
(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),
@@ -431,6 +434,10 @@ wrong_type_configurations = [
(stp.ST_Overlaps, ("", None)),
(stp.ST_Touches, (None, "")),
(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 3f977caa6..504ef81f1 100644
--- a/python/tests/sql/test_predicate.py
+++ b/python/tests/sql/test_predicate.py
@@ -199,6 +199,18 @@ class TestPredicate(TestBase):
result_df.show()
assert result_df.count() == 1
+ def test_st_relate(self):
+ baseDf = self.spark.sql("SELECT ST_GeomFromWKT('LINESTRING (1 1, 5
5)') AS g1, ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))') as g2,
'1010F0212' as im")
+ actual = baseDf.selectExpr("ST_Relate(g1, g2)").take(1)[0][0]
+ assert actual == "1010F0212"
+
+ 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 5f3b54596..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
@@ -116,6 +116,29 @@ public class TestPredicates extends TestBase{
true
);
}
+
+ @Test
+ public void test_ST_Relate() {
+ registerUDF("ST_Relate", byte[].class, byte[].class);
+ verifySqlSingleRes(
+ "SELECT SEDONA.ST_Relate(SEDONA.ST_GeomFromWKT('LINESTRING (1
1, 5 5)'), SEDONA.ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))'))",
+ "1010F0212"
+ );
+ registerUDF("ST_Relate", byte[].class, byte[].class, String.class);
+ verifySqlSingleRes(
+ "SELECT SEDONA.ST_Relate(SEDONA.ST_GeomFromWKT('LINESTRING (1
1, 5 5)'), SEDONA.ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))'),
'1010F0212')",
+ 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 3a3a4fa8b..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
@@ -117,6 +117,31 @@ public class TestPredicatesV2
true
);
}
+
+ @Test
+ public void test_ST_Relate() {
+ registerUDFV2("ST_Relate", String.class, String.class);
+ verifySqlSingleRes(
+ "SELECT SEDONA.ST_Relate( ST_GeometryFromWKT('LINESTRING (1 1,
5 5)'), ST_GeometryFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))'))",
+ "1010F0212"
+ );
+
+ registerUDFV2("ST_Relate", String.class, String.class, String.class);
+ verifySqlSingleRes(
+ "SELECT SEDONA.ST_Relate( ST_GeometryFromWKT('LINESTRING (1 1,
5 5)'), ST_GeometryFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))'),
'1010F0212')",
+ true
+ );
+ }
+
+ @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 98514cc57..ff044c546 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
@@ -1284,6 +1284,28 @@ public class UDFs {
);
}
+ @UDFAnnotations.ParamMeta(argNames = {"leftGeometry", "rightGeometry"})
+ public static String ST_Relate(byte[] leftGeometry, byte[] rightGeometry) {
+ return Predicates.relate(
+ GeometrySerde.deserialize(leftGeometry),
+ GeometrySerde.deserialize(rightGeometry)
+ );
+ }
+
+ @UDFAnnotations.ParamMeta(argNames = {"geom1", "geom2",
"intersectionMatrix"})
+ public static Boolean ST_Relate(byte[] geom1, byte[] geom2, String
intersectionMatrix) {
+ return Predicates.relate(
+ GeometrySerde.deserialize(geom1),
+ GeometrySerde.deserialize(geom2),
+ intersectionMatrix
+ );
+ }
+
+ @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 155b29b79..fbb4d2879 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
@@ -1042,6 +1042,28 @@ public class UDFsV2
);
}
+ @UDFAnnotations.ParamMeta(argNames = {"leftGeometry", "rightGeometry"},
argTypes = {"Geometry", "Geometry"}, returnTypes = "String")
+ public static String ST_Relate(String leftGeometry, String rightGeometry) {
+ return Predicates.relate(
+ GeometrySerde.deserGeoJson(leftGeometry),
+ GeometrySerde.deserGeoJson(rightGeometry)
+ );
+ }
+
+ @UDFAnnotations.ParamMeta(argNames = {"geom1", "geom2",
"intersectionMatrix"}, argTypes = {"Geometry", "Geometry", "String"})
+ public static boolean ST_Relate(String geom1, String geom2, String
intersectionMatrix) {
+ return Predicates.relate(
+ GeometrySerde.deserGeoJson(geom1),
+ GeometrySerde.deserGeoJson(geom2),
+ intersectionMatrix
+ );
+ }
+
+ @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 7e1fa981a..bbf06b75e 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
@@ -91,6 +91,8 @@ object Catalog {
function[ST_ReducePrecision](),
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 746cb8c1c..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
@@ -200,6 +200,22 @@ case class ST_Touches(inputExpressions: Seq[Expression])
}
}
+case class ST_Relate(inputExpressions: Seq[Expression])
+ extends InferredExpression(inferrableFunction3(Predicates.relate),
inferrableFunction2(Predicates.relate)) {
+
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
+ copy(inputExpressions = newChildren)
+ }
+}
+
+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 32f8b0a29..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
@@ -46,6 +46,14 @@ object st_predicates extends DataFrameAPI {
def ST_Touches(a: Column, b: Column): Column = wrapExpression[ST_Touches](a,
b)
def ST_Touches(a: String, b: String): Column = wrapExpression[ST_Touches](a,
b)
+ def ST_Relate(a: Column, b: Column): Column = wrapExpression[ST_Relate](a, b)
+ def ST_Relate(a: String, b: String): Column = wrapExpression[ST_Relate](a, b)
+ 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 a34f3aeea..08e0a6d0e 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
@@ -1205,6 +1205,21 @@ class dataFrameAPITestScala extends TestBaseScala {
assert(actualResult)
}
+ it("Passed ST_Relate") {
+ val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING (1 1, 5
5)') AS g1, ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))') as g2,
'1010F0212' as im")
+ val actual = baseDf.select(ST_Relate("g1", "g2")).first().get(0)
+ assert(actual.equals("1010F0212"))
+
+ val actualBoolean = baseDf.select(ST_Relate("g1", "g2",
"im")).first().getBoolean(0)
+ 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 6ec688856..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
@@ -194,6 +194,20 @@ class predicateTestScala extends TestBaseScala {
assert(!notCrosses.take(1)(0).get(0).asInstanceOf[Boolean])
}
+ it("Passed ST_Relate") {
+ val baseDf = sparkSession.sql("SELECT ST_GeomFromWKT('LINESTRING (1 1, 5
5)') AS g1, ST_GeomFromWKT('POLYGON ((3 3, 3 7, 7 7, 7 3, 3 3))') as g2,
'1010F0212' as im");
+ val actual = baseDf.selectExpr("ST_Relate(g1, g2)").first().get(0)
+ assert(actual.equals("1010F0212"))
+
+ val actualBoolean = baseDf.selectExpr("ST_Relate(g1, g2,
im)").first().getBoolean(0)
+ 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")