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 9f5003e12 [SEDONA-610] Add ST_IsValidTrajectory (#1481)
9f5003e12 is described below
commit 9f5003e126b66374cf0940997af5b75e92532399
Author: Furqaan Khan <[email protected]>
AuthorDate: Thu Jun 20 18:45:08 2024 -0400
[SEDONA-610] Add ST_IsValidTrajectory (#1481)
* feat: add ST_IsValidTrajectory
* remove print statements and add example
---
.../java/org/apache/sedona/common/Functions.java | 20 ++++++++++++
.../org/apache/sedona/common/FunctionsTest.java | 15 +++++++++
docs/api/flink/Function.md | 36 ++++++++++++++++++++++
docs/api/sql/Function.md | 36 ++++++++++++++++++++++
.../main/java/org/apache/sedona/flink/Catalog.java | 1 +
.../apache/sedona/flink/expressions/Functions.java | 10 ++++++
.../java/org/apache/sedona/flink/FunctionTest.java | 22 +++++++++++++
.../scala/org/apache/sedona/sql/UDF/Catalog.scala | 1 +
.../sql/sedona_sql/expressions/Functions.scala | 8 +++++
.../sql/sedona_sql/expressions/st_functions.scala | 5 +++
.../apache/sedona/sql/dataFrameAPITestScala.scala | 11 +++++++
.../org/apache/sedona/sql/functionTestScala.scala | 13 +++++++-
12 files changed, 177 insertions(+), 1 deletion(-)
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 552c37c8e..58fdcdbbb 100644
--- a/common/src/main/java/org/apache/sedona/common/Functions.java
+++ b/common/src/main/java/org/apache/sedona/common/Functions.java
@@ -727,6 +727,26 @@ public class Functions {
return isValidOp.isValid();
}
+ public static boolean isValidTrajectory(Geometry geom) {
+ if (!(geom.getClass().getSimpleName().equals("LineString"))) {
+ return false;
+ }
+
+ if (!Functions.hasM(geom)) {
+ return false;
+ }
+
+ double measure = -1 * Double.MAX_VALUE;
+ Coordinate[] coordinates = geom.getCoordinates();
+ for (int i = 0; i < geom.getNumPoints(); i++) {
+ if (coordinates[i].getM() <= measure) {
+ return false;
+ }
+ measure = coordinates[i].getM();
+ }
+ return true;
+ }
+
public static Geometry addPoint(Geometry linestring, Geometry point) {
return addPoint(linestring, point, -1);
}
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 eac55be8b..a3aa562b6 100644
--- a/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
+++ b/common/src/test/java/org/apache/sedona/common/FunctionsTest.java
@@ -3459,6 +3459,21 @@ public class FunctionsTest extends TestBase {
e.getMessage());
}
+ @Test
+ public void test() throws ParseException {
+ Geometry geom = Constructors.geomFromEWKT("MULTILINESTRING M ((0 0 1,0 1
2), (0 0 1,0 1 2))");
+ boolean actual = Functions.isValidTrajectory(geom);
+ assertFalse(actual);
+
+ geom = Constructors.geomFromEWKT("LINESTRING M (0 0 1, 0 1 2)");
+ actual = Functions.isValidTrajectory(geom);
+ assertTrue(actual);
+
+ geom = Constructors.geomFromEWKT("LINESTRING M (0 0 1, 0 1 1)");
+ actual = Functions.isValidTrajectory(geom);
+ assertFalse(actual);
+ }
+
@Test
public void isValidDetail() throws ParseException {
// Valid geometry
diff --git a/docs/api/flink/Function.md b/docs/api/flink/Function.md
index 02068ca9d..ec1705f93 100644
--- a/docs/api/flink/Function.md
+++ b/docs/api/flink/Function.md
@@ -2156,6 +2156,42 @@ gid | validity_info
```
+## ST_IsValidTrajectory
+
+Introduction: This function checks if a geometry is a valid trajectory
representation. For a trajectory to be considered valid, it must be a
LineString that includes measure (M) values. The key requirement is that the M
values increase from one vertex to the next as you move along the line.
+
+Format: `ST_IsValidTrajectory(geom: Geometry)`
+
+Since: `v1.6.1`
+
+SQL Example:
+
+```sql
+SELECT ST_IsValidTrajectory(
+ ST_GeomFromText('LINESTRING M (0 0 1, 0 1 2)')
+)
+```
+
+Output:
+
+```
+true
+```
+
+SQL Example:
+
+```sql
+SELECT ST_IsValidTrajectory(
+ ST_GeomFromText('LINESTRING M (0 0 1, 0 1 0)')
+)
+```
+
+Output:
+
+```sql
+false
+```
+
## ST_Length
Introduction: Returns the perimeter of A.
diff --git a/docs/api/sql/Function.md b/docs/api/sql/Function.md
index 591312b8e..a7b1891d1 100644
--- a/docs/api/sql/Function.md
+++ b/docs/api/sql/Function.md
@@ -2201,6 +2201,42 @@ gid | validity_info
```
+## ST_IsValidTrajectory
+
+Introduction: This function checks if a geometry is a valid trajectory
representation. For a trajectory to be considered valid, it must be a
LineString that includes measure (M) values. The key requirement is that the M
values increase from one vertex to the next as you move along the line.
+
+Format: `ST_IsValidTrajectory(geom: Geometry)`
+
+Since: `v1.6.1`
+
+SQL Example:
+
+```sql
+SELECT ST_IsValidTrajectory(
+ ST_GeomFromText('LINESTRING M (0 0 1, 0 1 2)')
+)
+```
+
+Output:
+
+```
+true
+```
+
+SQL Example:
+
+```sql
+SELECT ST_IsValidTrajectory(
+ ST_GeomFromText('LINESTRING M (0 0 1, 0 1 0)')
+)
+```
+
+Output:
+
+```sql
+false
+```
+
## ST_Length
Introduction: Returns the perimeter of A.
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 9013c5fde..165205c52 100644
--- a/flink/src/main/java/org/apache/sedona/flink/Catalog.java
+++ b/flink/src/main/java/org/apache/sedona/flink/Catalog.java
@@ -136,6 +136,7 @@ public class Catalog {
new Functions.ST_IsPolygonCW(),
new Functions.ST_IsRing(),
new Functions.ST_IsSimple(),
+ new Functions.ST_IsValidTrajectory(),
new Functions.ST_IsValid(),
new Functions.ST_Normalize(),
new Functions.ST_AddMeasure(),
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 96e120387..20965a634 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
@@ -1842,6 +1842,16 @@ public class Functions {
}
}
+ public static class ST_IsValidTrajectory extends ScalarFunction {
+ @DataTypeHint("Boolean")
+ public Boolean eval(
+ @DataTypeHint(value = "RAW", bridgedTo =
org.locationtech.jts.geom.Geometry.class)
+ Object o) {
+ Geometry geometry = (Geometry) o;
+ return org.apache.sedona.common.Functions.isValidTrajectory(geometry);
+ }
+ }
+
public static class ST_IsValidReason extends ScalarFunction {
@DataTypeHint("String")
public String eval(
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 83f15b7de..db05f81fe 100644
--- a/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
+++ b/flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
@@ -2441,6 +2441,28 @@ public class FunctionTest extends TestBase {
assertEquals(3, result, 0);
}
+ @Test
+ public void testIsValidTrajectory() {
+ Table table =
+ tableEnv.sqlQuery(
+ "SELECT ST_GeomFromText('LINESTRING M (0 0 1, 0 1 2)') as geom1,
ST_GeomFromText('LINESTRING M (0 0 1, 0 1 1)') as geom2");
+ boolean result =
+ (boolean)
+ first(
+ table.select(
+
call(Functions.ST_IsValidTrajectory.class.getSimpleName(), $("geom1"))))
+ .getField(0);
+ assertTrue(result);
+
+ result =
+ (boolean)
+ first(
+ table.select(
+
call(Functions.ST_IsValidTrajectory.class.getSimpleName(), $("geom2"))))
+ .getField(0);
+ assertFalse(result);
+ }
+
@Test
public void testIsValidReason() {
// Test with an invalid geometry (bow-tie polygon)
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 c2bee9c09..6353e16f2 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
@@ -90,6 +90,7 @@ object Catalog {
function[ST_UnaryUnion](),
function[ST_Union](),
function[ST_IsValidDetail](),
+ function[ST_IsValidTrajectory](),
function[ST_IsValid](),
function[ST_IsEmpty](),
function[ST_ReducePrecision](),
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 b8d5bf90c..3f33a66d0 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
@@ -352,6 +352,14 @@ case class ST_IsValidDetail(children: Seq[Expression])
.add("location", GeometryUDT, nullable = true)
}
+case class ST_IsValidTrajectory(inputExpressions: Seq[Expression])
+ extends
InferredExpression(inferrableFunction1(Functions.isValidTrajectory)) {
+
+ protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) =
{
+ copy(inputExpressions = newChildren)
+ }
+}
+
/**
* Test if Geometry is valid.
*
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 d879c4549..b53a79d06 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
@@ -237,6 +237,11 @@ object st_functions extends DataFrameAPI {
def ST_IsSimple(geometry: Column): Column =
wrapExpression[ST_IsSimple](geometry)
def ST_IsSimple(geometry: String): Column =
wrapExpression[ST_IsSimple](geometry)
+ def ST_IsValidTrajectory(geometry: Column): Column =
+ wrapExpression[ST_IsValidTrajectory](geometry)
+ def ST_IsValidTrajectory(geometry: String): Column =
+ wrapExpression[ST_IsValidTrajectory](geometry)
+
def ST_IsValid(geometry: Column): Column =
wrapExpression[ST_IsValid](geometry)
def ST_IsValid(geometry: String): Column =
wrapExpression[ST_IsValid](geometry)
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 0e1aef5c0..189733033 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
@@ -2073,6 +2073,17 @@ class dataFrameAPITestScala extends TestBaseScala {
assertTrue(actual)
}
+ it("Should pass ST_IsValidTrajectory") {
+ val baseDf = sparkSession.sql(
+ "SELECT ST_GeomFromText('LINESTRING M (0 0 1, 0 1 2)') as geom1,
ST_GeomFromText('LINESTRING M (0 0 1, 0 1 1)') as geom2")
+ var actual =
+
baseDf.select(ST_IsValidTrajectory("geom1")).first().get(0).asInstanceOf[Boolean]
+ assertTrue("Valid", actual)
+
+ actual =
baseDf.select(ST_IsValidTrajectory("geom2")).first().get(0).asInstanceOf[Boolean]
+ assertFalse("Not valid", actual)
+ }
+
it("Passed ST_IsValidDetail") {
// Valid Geometry
var baseDf = sparkSession.sql(
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 4b3d18dbc..76743f619 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
@@ -25,7 +25,7 @@ import
org.apache.spark.sql.catalyst.expressions.GenericRowWithSchema
import org.apache.spark.sql.functions._
import org.apache.spark.sql.{DataFrame, Row}
import org.geotools.referencing.CRS
-import org.junit.Assert.assertEquals
+import org.junit.Assert.{assertEquals, assertFalse, assertTrue}
import org.locationtech.jts.algorithm.MinimumBoundingCircle
import org.locationtech.jts.geom.{Coordinate, Geometry, GeometryFactory,
Polygon}
import org.locationtech.jts.io.WKTWriter
@@ -3230,6 +3230,17 @@ class functionTestScala
}
+ it("Should pass ST_IsValidTrajectory") {
+ val baseDf = sparkSession.sql(
+ "SELECT ST_GeomFromText('LINESTRING M (0 0 1, 0 1 2)') as geom1,
ST_GeomFromText('LINESTRING M (0 0 1, 0 1 1)') as geom2")
+ var actual =
+
baseDf.selectExpr("ST_IsValidTrajectory(geom1)").first().get(0).asInstanceOf[Boolean]
+ assertTrue("Valid", actual)
+
+ actual =
baseDf.selectExpr("ST_IsValidTrajectory(geom2)").first().get(0).asInstanceOf[Boolean]
+ assertFalse("Not valid", actual)
+ }
+
it("Should pass ST_IsValidDetail") {
val testData = Seq(
(5330, "POLYGON ((0 0, 3 3, 0 3, 3 0, 0 0))"),