Skip to content

Commit

Permalink
Add ST_Length(SphericalGeography)
Browse files Browse the repository at this point in the history
  • Loading branch information
seanbarker authored and jlabarbera11 committed Sep 18, 2019
1 parent 0637de3 commit 09b05bb
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 0 deletions.
5 changes: 5 additions & 0 deletions presto-docs/src/main/sphinx/functions/geospatial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,11 @@ Accessors
Returns the length of a linestring or multi-linestring using Euclidean measurement on a
two dimensional plane (based on spatial ref) in projected units.

.. function:: ST_Length(SphericalGeography) -> double

Returns the length of a linestring or multi-linestring on a spherical model of the Earth.
This is equivalent to the sum of great-circle distances between adjacent points on the linestring.

.. function:: ST_PointN(LineString, index) -> Point

Returns the vertex of a linestring at a given index (indices start at 1).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,41 @@ public static double stLength(@SqlType(GEOMETRY_TYPE_NAME) Slice input)
return geometry.getEsriGeometry().calculateLength2D();
}

@SqlNullable
@Description("Returns the great-circle length in meters of a linestring or multi-linestring on Earth's surface")
@ScalarFunction("ST_Length")
@SqlType(DOUBLE)
public static Double stSphericalLength(@SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME) Slice input)
{
OGCGeometry geometry = deserialize(input);
if (geometry.isEmpty()) {
return null;
}

validateSphericalType("ST_Length", geometry, EnumSet.of(LINE_STRING, MULTI_LINE_STRING));
MultiPath lineString = (MultiPath) geometry.getEsriGeometry();

double sum = 0;

// sum up paths on (multi)linestring
for (int path = 0; path < lineString.getPathCount(); path++) {
if (lineString.getPathSize(path) < 2) {
continue;
}

// sum up distances between adjacent points on this path
int pathStart = lineString.getPathStart(path);
Point prev = lineString.getPoint(pathStart);
for (int i = pathStart + 1; i < lineString.getPathEnd(path); i++) {
Point next = lineString.getPoint(i);
sum += greatCircleDistance(prev.getY(), prev.getX(), next.getY(), next.getX());
prev = next;
}
}

return sum * 1000;
}

@SqlNullable
@Description("Returns a float between 0 and 1 representing the location of the closest point on the LineString to the given Point, as a fraction of total 2d line length.")
@ScalarFunction("line_locate_point")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,54 @@ public void testSTLength()
assertInvalidFunction("ST_Length(ST_GeometryFromText('POLYGON ((1 1, 1 4, 4 4, 4 1))'))", "ST_Length only applies to LINE_STRING or MULTI_LINE_STRING. Input type is: POLYGON");
}

@Test
public void testSTLengthSphericalGeography()
{
// Empty linestring returns null
assertSTLengthSphericalGeography("LINESTRING EMPTY", null);

// Linestring with one point has length 0
assertSTLengthSphericalGeography("LINESTRING (0 0)", 0.0);

// Linestring with only one distinct point has length 0
assertSTLengthSphericalGeography("LINESTRING (0 0, 0 0, 0 0)", 0.0);

double length = 4350866.6362;

// ST_Length is equivalent to sums of ST_DISTANCE between points in the LineString
assertSTLengthSphericalGeography("LINESTRING (-71.05 42.36, -87.62 41.87, -122.41 37.77)", length);

// Linestring has same length as its reverse
assertSTLengthSphericalGeography("LINESTRING (-122.41 37.77, -87.62 41.87, -71.05 42.36)", length);

// Path north pole -> south pole -> north pole should be roughly the circumference of the Earth
assertSTLengthSphericalGeography("LINESTRING (0.0 90.0, 0.0 -90.0, 0.0 90.0)", 4.003e7);

// Empty multi-linestring returns null
assertSTLengthSphericalGeography("MULTILINESTRING (EMPTY)", null);

// Multi-linestring with one path is equivalent to a single linestring
assertSTLengthSphericalGeography("MULTILINESTRING ((-71.05 42.36, -87.62 41.87, -122.41 37.77))", length);

// Multi-linestring with two disjoint paths has length equal to sum of lengths of lines
assertSTLengthSphericalGeography("MULTILINESTRING ((-71.05 42.36, -87.62 41.87, -122.41 37.77), (-73.05 42.36, -89.62 41.87, -124.41 37.77))", 2 * length);

// Multi-linestring with adjacent paths is equivalent to a single linestring
assertSTLengthSphericalGeography("MULTILINESTRING ((-71.05 42.36, -87.62 41.87), (-87.62 41.87, -122.41 37.77))", length);
}

private void assertSTLengthSphericalGeography(String lineString, Double expectedLength)
{
String function = format("ST_Length(to_spherical_geography(ST_GeometryFromText('%s')))", lineString);

if (expectedLength == null || expectedLength == 0.0) {
assertFunction(function, DOUBLE, expectedLength);
}
else {
assertFunction(format("ROUND(ABS((%s / %f) - 1.0) / %f, 0)", function, expectedLength, 1e-4), DOUBLE, 0.0);
}
}

@Test
public void testLineLocatePoint()
{
Expand Down

0 comments on commit 09b05bb

Please sign in to comment.