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 docs/src/main/sphinx/functions/geospatial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,15 @@ Relationship tests
Operations
----------

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

Returns the points on each geometry nearest the other. If either geometry
is empty, return ``NULL``. Otherwise, return a row 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:: geometry_union(array(Geometry)) -> Geometry

Returns a geometry that represents the point set union of the input geometries. Performance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import io.trino.geospatial.serde.GeometrySerde;
import io.trino.geospatial.serde.GeometrySerializationType;
import io.trino.geospatial.serde.JtsGeometrySerde;
import io.trino.spi.PageBuilder;
import io.trino.spi.TrinoException;
import io.trino.spi.block.Block;
import io.trino.spi.block.BlockBuilder;
Expand All @@ -56,12 +57,14 @@
import io.trino.spi.function.SqlNullable;
import io.trino.spi.function.SqlType;
import io.trino.spi.type.IntegerType;
import io.trino.spi.type.RowType;
import io.trino.spi.type.StandardTypes;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.linearref.LengthIndexedLine;
import org.locationtech.jts.operation.distance.DistanceOp;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
Expand Down Expand Up @@ -1188,6 +1191,33 @@ 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("row(" + GEOMETRY_TYPE_NAME + "," + GEOMETRY_TYPE_NAME + ")")
public static Block geometryNearestPoints(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right)
{
Geometry leftGeometry = JtsGeometrySerde.deserialize(left);
Geometry rightGeometry = JtsGeometrySerde.deserialize(right);
if (leftGeometry.isEmpty() || rightGeometry.isEmpty()) {
return null;
}

RowType rowType = RowType.anonymous(ImmutableList.of(GEOMETRY, GEOMETRY));
PageBuilder pageBuilder = new PageBuilder(ImmutableList.of(rowType));
GeometryFactory geometryFactory = leftGeometry.getFactory();
Coordinate[] nearestCoordinates = DistanceOp.nearestPoints(leftGeometry, rightGeometry);

BlockBuilder blockBuilder = pageBuilder.getBlockBuilder(0);
BlockBuilder entryBlockBuilder = blockBuilder.beginBlockEntry();
GEOMETRY.writeSlice(entryBlockBuilder, JtsGeometrySerde.serialize(geometryFactory.createPoint(nearestCoordinates[0])));
GEOMETRY.writeSlice(entryBlockBuilder, JtsGeometrySerde.serialize(geometryFactory.createPoint(nearestCoordinates[1])));
blockBuilder.closeEntry();
pageBuilder.declarePosition();

return rowType.getObject(blockBuilder, blockBuilder.getPositionCount() - 1);
}

@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 @@ -22,6 +22,7 @@
import io.trino.spi.block.Block;
import io.trino.spi.block.BlockBuilder;
import io.trino.spi.type.ArrayType;
import io.trino.spi.type.RowType;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

Expand Down Expand Up @@ -769,6 +770,43 @@ public void testSTDistance()
assertFunction("ST_Distance(ST_GeometryFromText('MULTIPOLYGON EMPTY'), ST_GeometryFromText('POLYGON ((10 100, 30 10))'))", 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("geometry_nearest_points(ST_GeometryFromText('%s'), ST_GeometryFromText('%s'))", leftInputWkt, rightInputWkt),
RowType.anonymous(ImmutableList.of(GEOMETRY, GEOMETRY)),
ImmutableList.of(leftPointWkt, rightPointWkt));
}

private void assertNoNearestPoints(String leftInputWkt, String rightInputWkt)
{
assertFunction(
format("geometry_nearest_points(ST_GeometryFromText('%s'), ST_GeometryFromText('%s'))", leftInputWkt, rightInputWkt),
RowType.anonymous(ImmutableList.of(GEOMETRY, GEOMETRY)),
null);
}

@Test
public void testSTExteriorRing()
{
Expand Down