Skip to content

Commit

Permalink
Merge branch 'master' into dimension
Browse files Browse the repository at this point in the history
  • Loading branch information
yyy1000 committed Jun 23, 2023
2 parents f910480 + f8800d6 commit 7adac56
Show file tree
Hide file tree
Showing 15 changed files with 223 additions and 3 deletions.
34 changes: 34 additions & 0 deletions common/src/main/java/org/apache/sedona/common/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -939,4 +939,38 @@ public static Geometry geometricMedian(Geometry geometry) throws Exception {
return geometricMedian(geometry, DEFAULT_TOLERANCE, DEFAULT_MAX_ITER, false);
}

public static Geometry boundingDiagonal(Geometry geometry) {
if (geometry.isEmpty()) {
return GEOMETRY_FACTORY.createLineString();
}else {
//Envelope envelope = geometry.getEnvelopeInternal();
// if (envelope.isNull()) return GEOMETRY_FACTORY.createLineString();
Double startX = null, startY = null, startZ = null,
endX = null, endY = null, endZ = null;
boolean is3d = !Double.isNaN(geometry.getCoordinate().z);
for (Coordinate currCoordinate : geometry.getCoordinates()) {
startX = startX == null ? currCoordinate.getX() : Math.min(startX, currCoordinate.getX());
startY = startY == null ? currCoordinate.getY() : Math.min(startY, currCoordinate.getY());

endX = endX == null ? currCoordinate.getX() : Math.max(endX, currCoordinate.getX());
endY = endY == null ? currCoordinate.getY() : Math.max(endY, currCoordinate.getY());
if (is3d) {
Double geomZ = currCoordinate.getZ();
startZ = startZ == null ? currCoordinate.getZ() : Math.min(startZ, currCoordinate.getZ());
endZ = endZ == null ? currCoordinate.getZ() : Math.max(endZ, currCoordinate.getZ());
}
}
Coordinate startCoordinate;
Coordinate endCoordinate;
if (is3d) {
startCoordinate = new Coordinate(startX, startY, startZ);
endCoordinate = new Coordinate(endX, endY, endZ);
}else {
startCoordinate = new Coordinate(startX, startY);
endCoordinate = new Coordinate(endX, endY);
}
return GEOMETRY_FACTORY.createLineString(new Coordinate[] {startCoordinate, endCoordinate});
}
}

}
62 changes: 60 additions & 2 deletions common/src/test/java/org/apache/sedona/common/FunctionsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import org.locationtech.jts.geom.*;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;

import javax.sound.sampled.Line;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
Expand Down Expand Up @@ -973,4 +971,64 @@ public void translateHybridGeomCollectionDeltaZ() {
wktWriter3D.write(actualGeometry.getGeometryN(0).getGeometryN(1)));
assertEquals(emptyLineString.toText(), actualGeometry.getGeometryN(0).getGeometryN(2).toText());
}
@Test
public void boundingDiagonalGeom2D() {
Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray(1, 0, 1, 1, 2, 1, 2, 2, 2, 0, 1, 0));
String expected = "LINESTRING (1 0, 2 2)";
String actual = Functions.boundingDiagonal(polygon).toText();
assertEquals(expected, actual);
}

@Test
public void boundingDiagonalGeom3D() {
Polygon polygon = GEOMETRY_FACTORY.createPolygon(coordArray3d(1, 0, 1, 3, 2, 2, 2, 4, 5, 1, 1, 1, 1, 0, 1));
WKTWriter wktWriter = new WKTWriter(3);
String expected = "LINESTRING Z(1 0 1, 3 4 5)";
String actual = wktWriter.write(Functions.boundingDiagonal(polygon));
assertEquals(expected, actual);
}

@Test
public void boundingDiagonalGeomEmpty() {
LineString emptyLineString = GEOMETRY_FACTORY.createLineString();
String expected = "LINESTRING EMPTY";
String actual = Functions.boundingDiagonal(emptyLineString).toText();
assertEquals(expected, actual);
}

@Test
public void boundingDiagonalGeomCollection2D() {
// ("'GEOMETRYCOLLECTION (MULTIPOLYGON (((1 1, 1 -1, 2 2, 2 9, 9 1, 1 1)), ((5 5, 4 4, 2 2 , 5 5))), POINT (-1 0))'") -> "'LINESTRING (-1 -1, 9 9)'"
Polygon polygon1 = GEOMETRY_FACTORY.createPolygon(coordArray(1, 1, 1, -1, 2, 2, 2, 9, 9, 1, 1, 1));
Polygon polygon2 = GEOMETRY_FACTORY.createPolygon(coordArray(5, 5, 4, 4, 2, 2, 5, 5));
MultiPolygon multiPolygon = GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {polygon1, polygon2});
LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray(2, 2, 3, 3));
Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(-1, 0));
Geometry geometryCollection = GEOMETRY_FACTORY.createGeometryCollection(new Geometry[] {multiPolygon, lineString, point});
String expected = "LINESTRING (-1 -1, 9 9)";
String actual = Functions.boundingDiagonal(geometryCollection).toText();
assertEquals(expected, actual);
}

@Test
public void boundingDiagonalGeomCollection3D() {
Polygon polygon1 = GEOMETRY_FACTORY.createPolygon(coordArray3d(1, 1, 4, 1, -1, 6, 2, 2, 4, 2, 9, 4, 9, 1, 0, 1, 1, 4));
Polygon polygon2 = GEOMETRY_FACTORY.createPolygon(coordArray3d(5, 5, 1, 4, 4, 1, 2, 2, 2, 5, 5, 1));
MultiPolygon multiPolygon = GEOMETRY_FACTORY.createMultiPolygon(new Polygon[] {polygon1, polygon2});
LineString lineString = GEOMETRY_FACTORY.createLineString(coordArray3d(2, 2, 9, 3, 3, -5));
Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(-1, 9, 1));
Geometry geometryCollection = GEOMETRY_FACTORY.createGeometryCollection(new Geometry[] {multiPolygon, lineString, point});
String expected = "LINESTRING Z(-1 -1 -5, 9 9 9)";
WKTWriter wktWriter = new WKTWriter(3);
String actual = wktWriter.write(Functions.boundingDiagonal(geometryCollection));
assertEquals(expected, actual);
}

@Test
public void boundingDiagonalSingleVertex() {
Point point = GEOMETRY_FACTORY.createPoint(new Coordinate(10, 5));
String expected = "LINESTRING (10 5, 10 5)";
String actual = Functions.boundingDiagonal(point).toText();
assertEquals(expected, actual);
}
}
27 changes: 27 additions & 0 deletions docs/api/flink/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,33 @@ SELECT ST_Boundary(ST_GeomFromText('POLYGON ((1 1, 0 0, -1 1, 1 1))'))

Output: `LINEARRING (1 1, 0 0, -1 1, 1 1)`

## ST_BoundingDiagonal

Introduction: Returns a linestring spanning minimum and maximum values of each dimension of the given geometry's coordinates as its start and end point respectively.
If an empty geometry is provided, the returned LineString is also empty.
If a single vertex (POINT) is provided, the returned LineString has both the start and end points same as the points coordinates

Format: `ST_BoundingDiagonal(geom: geometry)`

Since: `v1.5.0`

Example:
```sql
SELECT ST_BoundingDiagonal(ST_GeomFromWKT(geom))
```

Input: `POLYGON ((1 1 1, 3 3 3, 0 1 4, 4 4 0, 1 1 1))`

Output: `LINESTRING Z(0 1 1, 4 4 4)`

Input: `POINT (10 10)`

Output: `LINESTRING (10 10, 10 10)`

Input: `GEOMETRYCOLLECTION(POLYGON ((5 5 5, -1 2 3, -1 -1 0, 5 5 5)), POINT (10 3 3))`

Output: `LINESTRING Z(-1 -1 0, 10 5 5)`

## ST_Buffer

Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance.
Expand Down
27 changes: 27 additions & 0 deletions docs/api/sql/Function.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,33 @@ SELECT ST_Boundary(ST_GeomFromText('POLYGON((1 1,0 0, -1 1, 1 1))'))

Output: `LINESTRING (1 1, 0 0, -1 1, 1 1)`

## ST_BoundingDiagonal

Introduction: Returns a linestring spanning minimum and maximum values of each dimension of the given geometry's coordinates as its start and end point respectively.
If an empty geometry is provided, the returned LineString is also empty.
If a single vertex (POINT) is provided, the returned LineString has both the start and end points same as the points coordinates

Format: `ST_BoundingDiagonal(geom: geometry)`

Since: `v1.5.0`

Spark SQL Example:
```sql
SELECT ST_BoundingDiagonal(ST_GeomFromWKT(geom))
```

Input: `POLYGON ((1 1 1, 3 3 3, 0 1 4, 4 4 0, 1 1 1))`

Output: `LINESTRING Z(0 1 1, 4 4 4)`

Input: `POINT (10 10)`

Output: `LINESTRING (10 10, 10 10)`

Input: `GEOMETRYCOLLECTION(POLYGON ((5 5 5, -1 2 3, -1 -1 0, 5 5 5)), POINT (10 3 3))`

Output: `LINESTRING Z(-1 -1 0, 10 5 5)`

## ST_Buffer

Introduction: Returns a geometry/geography that represents all points whose distance from this Geometry/geography is less than or equal to distance.
Expand Down
1 change: 1 addition & 0 deletions flink/src/main/java/org/apache/sedona/flink/Catalog.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public static UserDefinedFunction[] getFuncs() {
new Functions.ST_Force3D(),
new Functions.ST_NRings(),
new Functions.ST_Translate(),
new Functions.ST_BoundingDiagonal(),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -633,4 +633,13 @@ public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.j
}
}

public static class ST_BoundingDiagonal extends ScalarFunction {

@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class)
public Geometry eval(@DataTypeHint(value = "RAW", bridgedTo = org.locationtech.jts.geom.Geometry.class) Object o) {
Geometry geometry = (Geometry) o;
return org.apache.sedona.common.Functions.boundingDiagonal(geometry);
}

}
}
9 changes: 9 additions & 0 deletions flink/src/test/java/org/apache/sedona/flink/FunctionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -746,4 +746,13 @@ public void testTranslate() {
assertEquals(expected, actual);
}

@Test
public void testBoundingDiagonal() {
Table polyTable = tableEnv.sqlQuery("SELECT ST_BoundingDiagonal(ST_GeomFromWKT('POLYGON ((1 0, 1 1, 2 1, 2 0, 1 0))'))" +" AS " + polygonColNames[0]);
polyTable = polyTable.select(call(Functions.ST_AsText.class.getSimpleName(), $(polygonColNames[0])));
String expected = "LINESTRING (1 0, 2 1)";
String actual = (String) first(polyTable).getField(0);
assertEquals(expected, actual);
}

}
13 changes: 12 additions & 1 deletion python/sedona/sql/st_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@
"ST_NumPoints",
"ST_Force3D",
"ST_NRings",
"ST_Translate"
"ST_Translate",
"ST_BoundingDiagonal"
]


Expand Down Expand Up @@ -1288,3 +1289,13 @@ def ST_Translate(geometry: ColumnOrName, deltaX: Union[ColumnOrName, float], del
args = (geometry, deltaX, deltaY, deltaZ)
return _call_st_function("ST_Translate", args)

@validate_argument_types
def ST_BoundingDiagonal(geometry: ColumnOrName) -> Column:
"""
Returns a LineString with the min/max values of each dimension of the bounding box of the given geometry as its
start/end coordinates.
:param geometry: Geometry to return bounding diagonal of.
:return: LineString spanning min and max values of each dimension of the given geometry
"""

return _call_st_function("ST_BoundingDiagonal", geometry)
1 change: 1 addition & 0 deletions python/tests/sql/test_dataframe_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
(stf.ST_Boundary, ("geom",), "triangle_geom", "", "LINESTRING (0 0, 1 0, 1 1, 0 0)"),
(stf.ST_Buffer, ("point", 1.0), "point_geom", "ST_PrecisionReduce(geom, 2)", "POLYGON ((0.98 0.8, 0.92 0.62, 0.83 0.44, 0.71 0.29, 0.56 0.17, 0.38 0.08, 0.2 0.02, 0 0, -0.2 0.02, -0.38 0.08, -0.56 0.17, -0.71 0.29, -0.83 0.44, -0.92 0.62, -0.98 0.8, -1 1, -0.98 1.2, -0.92 1.38, -0.83 1.56, -0.71 1.71, -0.56 1.83, -0.38 1.92, -0.2 1.98, 0 2, 0.2 1.98, 0.38 1.92, 0.56 1.83, 0.71 1.71, 0.83 1.56, 0.92 1.38, 0.98 1.2, 1 1, 0.98 0.8))"),
(stf.ST_BuildArea, ("geom",), "multiline_geom", "ST_Normalize(geom)", "POLYGON ((0 0, 1 1, 1 0, 0 0))"),
(stf.ST_BoundingDiagonal, ("geom",), "square_geom", "ST_BoundingDiagonal(geom)", "LINESTRING (1 0, 2 1)"),
(stf.ST_Centroid, ("geom",), "triangle_geom", "ST_PrecisionReduce(geom, 2)", "POINT (0.67 0.33)"),
(stf.ST_Collect, (lambda: f.expr("array(a, b)"),), "two_points", "", "MULTIPOINT Z (0 0 0, 3 0 4)"),
(stf.ST_Collect, ("a", "b"), "two_points", "", "MULTIPOINT Z (0 0 0, 3 0 4)"),
Expand Down
6 changes: 6 additions & 0 deletions python/tests/sql/test_function.py
Original file line number Diff line number Diff line change
Expand Up @@ -1098,3 +1098,9 @@ def test_translate(self):
actual = actualDf.selectExpr("ST_AsText(geom)").take(1)[0][0]
assert expected == actual

def test_boundingDiagonal(self):
expected = "LINESTRING (1 0, 2 1)"
actual_df = self.spark.sql("SELECT ST_BoundingDiagonal(ST_GeomFromText('POLYGON ((1 0, 1 1, 2 1, 2 0, "
"1 0))')) AS geom")
actual = actual_df.selectExpr("ST_AsText(geom)").take(1)[0][0]
assert expected == actual
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ object Catalog {
function[ST_Force3D](0.0),
function[ST_NRings](),
function[ST_Translate](0.0),
function[ST_BoundingDiagonal](),
// Expression for rasters
function[RS_NormalizedDifference](),
function[RS_Mean](),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1022,3 +1022,10 @@ case class ST_Dimension(inputExpressions: Seq[Expression])
}
}

case class ST_BoundingDiagonal(inputExpressions: Seq[Expression])
extends InferredUnaryExpression(Functions.boundingDiagonal) with FoldableExpression {
protected def withNewChildrenInternal(newChildren: IndexedSeq[Expression]) = {
copy(inputExpressions = newChildren)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,10 @@ object st_functions extends DataFrameAPI {
def ST_Translate(geometry: String, deltaX: Double, deltaY: Double): Column = wrapExpression[ST_Translate](geometry, deltaX, deltaY, 0.0)


def ST_BoundingDiagonal(geometry: Column) =
wrapExpression[ST_BoundingDiagonal](geometry)

def ST_BoundingDiagonal(geometry: String) =
wrapExpression[ST_BoundingDiagonal](geometry)

}
Original file line number Diff line number Diff line change
Expand Up @@ -1002,5 +1002,14 @@ class dataFrameAPITestScala extends TestBaseScala {
val expectedDefaultValue = "POLYGON Z((3 3 1, 3 4 1, 4 4 1, 4 3 1, 3 3 1))"
assert(expectedDefaultValue == actualDefaultValue)
}

it("Passed ST_BoundingDiagonal") {
val polyDf = sparkSession.sql("SELECT ST_GeomFromWKT('POLYGON ((1 0 1, 2 3 2, 5 0 1, 5 2 9, 1 0 1))') AS geom")
val df = polyDf.select(ST_BoundingDiagonal("geom"))
val wKTWriter = new WKTWriter(3);
val expected = "LINESTRING Z(1 0 1, 5 3 9)"
val actual = wKTWriter.write(df.take(1)(0).get(0).asInstanceOf[Geometry])
assertEquals(expected, actual)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1987,4 +1987,18 @@ class functionTestScala extends TestBaseScala with Matchers with GeometrySample
assertEquals(expectedDefaultValue, actualDefaultValue)
}
}

it ("should pass ST_BoundingDiagonal") {
val geomTestCases = Map (
("'POINT (10 10)'")-> "'LINESTRING (10 10, 10 10)'",
("'POLYGON ((1 1 1, 4 4 4, 0 9 3, 0 9 9, 1 1 1))'") -> "'LINESTRING Z(0 1 1, 4 9 9)'",
("'GEOMETRYCOLLECTION (MULTIPOLYGON (((1 1, 1 -1, 2 2, 2 9, 9 1, 1 1)), ((5 5, 4 4, 2 2 , 5 5))), POINT (-1 0))'") -> "'LINESTRING (-1 -1, 9 9)'"
)
for (((geom), expectedResult) <- geomTestCases) {
val df = sparkSession.sql(s"SELECT ST_AsText(ST_BoundingDiagonal(ST_GeomFromWKT($geom))) AS geom, " + s"$expectedResult")
val actual = df.take(1)(0).get(0).asInstanceOf[String]
val expected = df.take(1)(0).get(1).asInstanceOf[String]
assertEquals(expected, actual)
}
}
}

0 comments on commit 7adac56

Please sign in to comment.