Skip to content

Commit

Permalink
Add ST_InteriorRings and ST_InteriorRingN functions
Browse files Browse the repository at this point in the history
  • Loading branch information
yamatias authored and mbasmanova committed Jun 29, 2018
1 parent 17e8f3d commit ab0c640
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 2 deletions.
14 changes: 14 additions & 0 deletions presto-docs/src/main/sphinx/functions/geospatial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,14 @@ Accessors
Singular geometries (e.g., POINT, LINESTRING, POLYGON), are treated as collections of one element.
Empty geometries are treated as empty collections.

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

Returns the interior ring element at the specified index (indices start at 1). If
the given index is less than 1 or greater than the total number of interior rings
in the input geometry, returns ``NULL``. Throws an error if the input geometry is
not a polygon.
Use :func:``ST_NumInteriorRing`` to find out the total number of elements.

.. function:: ST_GeometryType(Geometry) -> varchar

Returns the type of the geometry.
Expand Down Expand Up @@ -254,6 +262,12 @@ Accessors

Return the Y coordinate of the point.

.. function:: ST_InteriorRings(Geometry) -> Geometry

Returns an array of all interior rings found in the input geometry, or an empty
array if the polygon has no interior rings. Returns null if the input geometry
is empty. Throws an error if the input geometry is not a polygon.

.. function:: ST_NumGeometries(Geometry) -> bigint

Returns the number of geometries in the collection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,26 @@ public static Long stNumInteriorRings(@SqlType(GEOMETRY_TYPE_NAME) Slice input)
return Long.valueOf(((OGCPolygon) geometry).numInteriorRing());
}

@SqlNullable
@Description("Returns an array of interior rings of a polygon")
@ScalarFunction("ST_InteriorRings")
@SqlType("array(" + GEOMETRY_TYPE_NAME + ")")
public static Block stInteriorRings(@SqlType(GEOMETRY_TYPE_NAME) Slice input)
{
OGCGeometry geometry = deserialize(input);
validateType("ST_InteriorRings", geometry, EnumSet.of(POLYGON));
if (geometry.isEmpty()) {
return null;
}

OGCPolygon polygon = (OGCPolygon) geometry;
BlockBuilder blockBuilder = GEOMETRY.createBlockBuilder(null, polygon.numInteriorRing());
for (int i = 0; i < polygon.numInteriorRing(); i++) {
GEOMETRY.writeSlice(blockBuilder, serialize(polygon.interiorRingN(i)));
}
return blockBuilder.build();
}

@Description("Returns the cardinality of the geometry collection")
@ScalarFunction("ST_NumGeometries")
@SqlType(StandardTypes.INTEGER)
Expand Down Expand Up @@ -568,7 +588,6 @@ public static Slice stPointN(@SqlType(GEOMETRY_TYPE_NAME) Slice input, @SqlType(
public static Block stGeometries(@SqlType(GEOMETRY_TYPE_NAME) Slice input)
{
OGCGeometry geometry = deserialize(input);

if (geometry.isEmpty()) {
return null;
}
Expand All @@ -585,10 +604,25 @@ public static Block stGeometries(@SqlType(GEOMETRY_TYPE_NAME) Slice input)
for (int i = 0; i < collection.numGeometries(); i++) {
GEOMETRY.writeSlice(blockBuilder, serialize(collection.geometryN(i)));
}

return blockBuilder.build();
}

@SqlNullable
@Description("Returns the interior ring element at the specified index (indices start at 1)")
@ScalarFunction("ST_InteriorRingN")
@SqlType(GEOMETRY_TYPE_NAME)
public static Slice stInteriorRingN(@SqlType(GEOMETRY_TYPE_NAME) Slice input, @SqlType(INTEGER) long index)
{
OGCGeometry geometry = deserialize(input);
validateType("ST_InteriorRingN", geometry, EnumSet.of(POLYGON));
OGCPolygon polygon = (OGCPolygon) geometry;
if (index < 1 || index > polygon.numInteriorRing()) {
return null;
}
OGCGeometry interiorRing = polygon.interiorRingN(toIntExact(index) - 1);
return serialize(interiorRing);
}

@Description("Returns the number of points in a Geometry")
@ScalarFunction("ST_NumPoints")
@SqlType(StandardTypes.BIGINT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,33 @@ public void testGreatCircleDistance()
assertInvalidFunction("great_circle_distance(36.12, -86.67, 33.94, infinity())", "Longitude must be between -180 and 180");
}

@Test
public void testSTInteriorRings()
{
assertInvalidInteriorRings("POINT (2 3)", "POINT");
assertInvalidInteriorRings("LINESTRING EMPTY", "LINE_STRING");
assertInvalidInteriorRings("MULTIPOINT (30 20, 60 70)", "MULTI_POINT");
assertInvalidInteriorRings("MULTILINESTRING ((1 10, 100 1000), (2 2, 1 0, 5 6))", "MULTI_LINE_STRING");
assertInvalidInteriorRings("MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((0 0, 0 2, 2 2, 2 0)))", "MULTI_POLYGON");
assertInvalidInteriorRings("GEOMETRYCOLLECTION (POINT (1 1), POINT (2 3), LINESTRING (5 8, 13 21))", "GEOMETRY_COLLECTION");

assertFunction("ST_InteriorRings(ST_GeometryFromText('POLYGON EMPTY'))", new ArrayType(GEOMETRY), null);
assertInteriorRings("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))");
assertInteriorRings("POLYGON ((0 0, 0 3, 3 3, 3 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))", "LINESTRING (1 1, 1 2, 2 2, 2 1, 1 1)");
assertInteriorRings("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1), (3 3, 3 4, 4 4, 4 3, 3 3))",
"LINESTRING (1 1, 1 2, 2 2, 2 1, 1 1)", "LINESTRING (3 3, 3 4, 4 4, 4 3, 3 3)");
}

private void assertInteriorRings(String wkt, String... expected)
{
assertFunction(format("transform(ST_InteriorRings(ST_GeometryFromText('%s')), x -> ST_ASText(x))", wkt), new ArrayType(VARCHAR), ImmutableList.copyOf(expected));
}

private void assertInvalidInteriorRings(String wkt, String geometryType)
{
assertInvalidFunction(format("ST_InteriorRings(ST_GeometryFromText('%s'))", wkt), format("ST_InteriorRings only applies to POLYGON. Input type is: %s", geometryType));
}

@Test
public void testSTNumGeometries()
{
Expand Down Expand Up @@ -943,6 +970,33 @@ private void assertSTGeometries(String wkt, String... expected)
assertFunction(String.format("transform(ST_Geometries(ST_GeometryFromText('%s')), x -> ST_ASText(x))", wkt), new ArrayType(VARCHAR), ImmutableList.copyOf(expected));
}

public void testSTInteriorRingN()
{
assertInvalidInteriorRingN("POINT EMPTY", 0, "POINT");
assertInvalidInteriorRingN("LINESTRING (1 2, 2 3, 3 4)", 1, "LINE_STRING");
assertInvalidInteriorRingN("MULTIPOINT (1 1, 2 3, 5 8)", -1, "MULTI_POINT");
assertInvalidInteriorRingN("MULTILINESTRING ((2 4, 4 2), (3 5, 5 3))", 0, "MULTI_LINE_STRING");
assertInvalidInteriorRingN("MULTIPOLYGON (((1 1, 1 3, 3 3, 3 1)), ((2 4, 2 6, 6 6, 6 4)))", 2, "MULTI_POLYGON");
assertInvalidInteriorRingN("GEOMETRYCOLLECTION (POINT (2 2), POINT (10 20))", 1, "GEOMETRY_COLLECTION");

assertInteriorRingN("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", 1, null);
assertInteriorRingN("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", 2, null);
assertInteriorRingN("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", -1, null);
assertInteriorRingN("POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", 0, null);
assertInteriorRingN("POLYGON ((0 0, 0 3, 3 3, 3 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1))", 1, "LINESTRING (1 1, 1 2, 2 2, 2 1, 1 1)");
assertInteriorRingN("POLYGON ((0 0, 0 5, 5 5, 5 0, 0 0), (1 1, 1 2, 2 2, 2 1, 1 1), (3 3, 3 4, 4 4, 4 3, 3 3))", 2, "LINESTRING (3 3, 3 4, 4 4, 4 3, 3 3)");
}

private void assertInteriorRingN(String wkt, int index, String expected)
{
assertFunction(format("ST_ASText(ST_InteriorRingN(ST_GeometryFromText('%s'), %d))", wkt, index), VARCHAR, expected);
}

private void assertInvalidInteriorRingN(String wkt, int index, String geometryType)
{
assertInvalidFunction(format("ST_InteriorRingN(ST_GeometryFromText('%s'), %d)", wkt, index), format("ST_InteriorRingN only applies to POLYGON. Input type is: %s", geometryType));
}

public void testSTGeometryType()
{
assertFunction("ST_GeometryType(ST_Point(1, 4))", VARCHAR, "ST_Point");
Expand Down

0 comments on commit ab0c640

Please sign in to comment.