Skip to content

Commit

Permalink
Add geometry_nearest_points function
Browse files Browse the repository at this point in the history
This function is similar to PostGIS ST_ClosestPoints.  Given two
geometries, find a pair of points on them that have the minimum
distance.  If either geometry is empty, return null.
  • Loading branch information
jagill authored and mbasmanova committed Aug 21, 2020
1 parent f25aa0c commit e7af71b
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 0 deletions.
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 @@ -945,6 +946,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);
}
}

@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

0 comments on commit e7af71b

Please sign in to comment.