Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions presto-docs/src/main/sphinx/functions/geospatial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,15 @@ Accessors

Returns the great-circle distance in meters between two SphericalGeography points.

.. function:: geometry_nearest_points(Geometry, Geometry) -> array(Point)

Returns the points on each geometry nearest the other. If either geometry
is empty, return ``NULL``. Otherwise, return an array of two Points that have
the minimum distance of any two points on the geometries. The first Point
will be from the first Geometry argument, the second from the second Geometry
argument. If there are multiple pairs with the minimum distance, one pair
is chosen arbitrarily.

.. function:: ST_GeometryN(Geometry, index) -> Geometry

Returns the geometry element at a given index (indices start at 1).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import org.locationtech.jts.geom.TopologyException;
import org.locationtech.jts.geom.impl.PackedCoordinateSequenceFactory;
import org.locationtech.jts.linearref.LengthIndexedLine;
import org.locationtech.jts.operation.distance.DistanceOp;

import java.util.ArrayList;
import java.util.EnumSet;
Expand Down Expand Up @@ -942,6 +943,29 @@ public static Double stDistance(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlTyp
return leftGeometry.isEmpty() || rightGeometry.isEmpty() ? null : leftGeometry.distance(rightGeometry);
}

@SqlNullable
@Description("Return the closest points on the two geometries")
@ScalarFunction("geometry_nearest_points")
@SqlType("array(" + GEOMETRY_TYPE_NAME + ")")
public static Block geometryNearestPoints(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right)
{
Geometry leftGeometry = deserialize(left);
Geometry rightGeometry = deserialize(right);
if (leftGeometry.isEmpty() || rightGeometry.isEmpty()) {
return null;
}
try {
Coordinate[] nearestCoordinates = DistanceOp.nearestPoints(leftGeometry, rightGeometry);
BlockBuilder blockBuilder = GEOMETRY.createBlockBuilder(null, 2);
GEOMETRY.writeSlice(blockBuilder, serialize(createJtsPoint(nearestCoordinates[0])));
GEOMETRY.writeSlice(blockBuilder, serialize(createJtsPoint(nearestCoordinates[1])));
return blockBuilder.build();
}
catch (TopologyException e) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, e.getMessage(), e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to add a test that triggers this exception and confirm that try allows to suppress it?

}
}

@SqlNullable
@Description("Returns a line string representing the exterior ring of the POLYGON")
@ScalarFunction("ST_ExteriorRing")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -789,6 +789,43 @@ public void testSTDistance()
assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOLYGON EMPTY'), ST_GeometryFromText('POLYGON ((10 100, 30 10, 30 100, 10 100))'))", DOUBLE, null);
}

@Test
public void testGeometryNearestPoints()
{
assertNearestPoints("POINT (50 100)", "POINT (150 150)", "POINT (50 100)", "POINT (150 150)");
assertNearestPoints("MULTIPOINT (50 100, 50 200)", "POINT (50 100)", "POINT (50 100)", "POINT (50 100)");
assertNearestPoints("LINESTRING (50 100, 50 200)", "LINESTRING (10 10, 20 20)", "POINT (50 100)", "POINT (20 20)");
assertNearestPoints("MULTILINESTRING ((1 1, 5 1), (2 4, 4 4))", "LINESTRING (10 20, 20 50)", "POINT (4 4)", "POINT (10 20)");
assertNearestPoints("POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))", "POLYGON ((4 4, 4 5, 5 5, 5 4, 4 4))", "POINT (3 3)", "POINT (4 4)");
assertNearestPoints("MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1, 1 1)), ((0 0, 0 2, 2 2, 2 0, 0 0)))", "POLYGON ((10 100, 30 10, 30 100, 10 100))", "POINT (3 3)", "POINT (30 10)");
assertNearestPoints("GEOMETRYCOLLECTION (POINT (0 0), LINESTRING (0 20, 20 0))", "POLYGON ((5 5, 5 6, 6 6, 6 5, 5 5))", "POINT (10 10)", "POINT (6 6)");

assertNoNearestPoints("POINT EMPTY", "POINT (150 150)");
assertNoNearestPoints("POINT (50 100)", "POINT EMPTY");
assertNoNearestPoints("POINT EMPTY", "POINT EMPTY");
assertNoNearestPoints("MULTIPOINT EMPTY", "POINT (50 100)");
assertNoNearestPoints("LINESTRING (50 100, 50 200)", "LINESTRING EMPTY");
assertNoNearestPoints("MULTILINESTRING EMPTY", "LINESTRING (10 20, 20 50)");
assertNoNearestPoints("POLYGON ((1 1, 1 3, 3 3, 3 1, 1 1))", "POLYGON EMPTY");
assertNoNearestPoints("MULTIPOLYGON EMPTY", "POLYGON ((10 100, 30 10, 30 100, 10 100))");
}

private void assertNearestPoints(String leftInputWkt, String rightInputWkt, String leftPointWkt, String rightPointWkt)
{
assertFunction(
format("transform(geometry_nearest_points(ST_GeometryFromText('%s'), ST_GeometryFromText('%s')), x -> ST_ASText(x))", leftInputWkt, rightInputWkt),
new ArrayType(VARCHAR),
ImmutableList.of(leftPointWkt, rightPointWkt));
}

private void assertNoNearestPoints(String leftInputWkt, String rightInputWkt)
{
assertFunction(
format("geometry_nearest_points(ST_GeometryFromText('%s'), ST_GeometryFromText('%s'))", leftInputWkt, rightInputWkt),
new ArrayType(GEOMETRY),
null);
}

@Test
public void testSTExteriorRing()
{
Expand Down