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 mbasmanova committed Apr 3, 2019
1 parent 2e6cafc commit bb51fe3
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 @@ -316,6 +316,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 @@ -1551,6 +1551,41 @@ public static Double stSphericalArea(@SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME) Sli
return Math.abs(sphericalExcess * EARTH_RADIUS_M * EARTH_RADIUS_M);
}

@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;
}

private static double computeSphericalExcess(Polygon polygon, int start, int end)
{
// Our calculations rely on not processing the same point twice
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,4 +203,52 @@ private void assertArea(String wkt, double expectedArea)
{
assertFunction(format("ABS(ROUND((ST_Area(to_spherical_geography(ST_GeometryFromText('%s'))) / %f - 1 ) * %d, 0))", wkt, expectedArea, 10000), DOUBLE, 0.0);
}

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

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

// Linestring with only one distinct point has length 0
assertLength("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
assertLength("LINESTRING (-71.05 42.36, -87.62 41.87, -122.41 37.77)", length);

// Linestring has same length as its reverse
assertLength("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
assertLength("LINESTRING (0.0 90.0, 0.0 -90.0, 0.0 90.0)", 4.003e7);

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

// Multi-linestring with one path is equivalent to a single linestring
assertLength("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
assertLength("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
assertLength("MULTILINESTRING ((-71.05 42.36, -87.62 41.87), (-87.62 41.87, -122.41 37.77))", length);
}

private void assertLength(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);
}
}
}

0 comments on commit bb51fe3

Please sign in to comment.