use of com.facebook.presto.spi.function.Description in project presto by prestodb.
the class SphericalGeoFunctions method stSphericalCentroid.
@SqlNullable
@Description("Returns the Point value that is the mathematical centroid of a Spherical Geography")
@ScalarFunction("ST_Centroid")
@SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME)
public static Slice stSphericalCentroid(@SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME) Slice input) {
OGCGeometry geometry = EsriGeometrySerde.deserialize(input);
if (geometry.isEmpty()) {
return null;
}
// TODO: add support for other types e.g. POLYGON
validateSphericalType("ST_Centroid", geometry, EnumSet.of(POINT, MULTI_POINT));
if (geometry instanceof OGCPoint) {
return input;
}
OGCGeometryCollection geometryCollection = (OGCGeometryCollection) geometry;
for (int i = 0; i < geometryCollection.numGeometries(); i++) {
OGCGeometry g = geometryCollection.geometryN(i);
validateSphericalType("ST_Centroid", g, EnumSet.of(POINT));
Point p = (Point) g.getEsriGeometry();
checkLongitude(p.getX());
checkLatitude(p.getY());
}
Point centroid;
if (geometryCollection.numGeometries() == 1) {
centroid = (Point) geometryCollection.geometryN(0).getEsriGeometry();
} else {
double x3DTotal = 0;
double y3DTotal = 0;
double z3DTotal = 0;
for (int i = 0; i < geometryCollection.numGeometries(); i++) {
CartesianPoint cp = new CartesianPoint((Point) geometryCollection.geometryN(i).getEsriGeometry());
x3DTotal += cp.getX();
y3DTotal += cp.getY();
z3DTotal += cp.getZ();
}
double centroidVectorLength = Math.sqrt(x3DTotal * x3DTotal + y3DTotal * y3DTotal + z3DTotal * z3DTotal);
if (centroidVectorLength == 0.0) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Unexpected error. Average vector length adds to zero (%f, %f, %f)", x3DTotal, y3DTotal, z3DTotal));
}
centroid = new CartesianPoint(x3DTotal / centroidVectorLength, y3DTotal / centroidVectorLength, z3DTotal / centroidVectorLength).asSphericalPoint();
}
return EsriGeometrySerde.serialize(new OGCPoint(centroid, geometryCollection.getEsriSpatialReference()));
}
use of com.facebook.presto.spi.function.Description in project presto by prestodb.
the class SphericalGeoFunctions method stSphericalArea.
@SqlNullable
@Description("Returns the area of a geometry on the Earth's surface using spherical model")
@ScalarFunction("ST_Area")
@SqlType(DOUBLE)
public static Double stSphericalArea(@SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME) Slice input) {
OGCGeometry geometry = EsriGeometrySerde.deserialize(input);
if (geometry.isEmpty()) {
return null;
}
validateSphericalType("ST_Area", geometry, EnumSet.of(POLYGON, MULTI_POLYGON));
Polygon polygon = (Polygon) geometry.getEsriGeometry();
// See https://www.movable-type.co.uk/scripts/latlong.html
// and http://osgeo-org.1560.x6.nabble.com/Area-of-a-spherical-polygon-td3841625.html
// and https://www.element84.com/blog/determining-if-a-spherical-polygon-contains-a-pole
// for the underlying Maths
double sphericalExcess = 0.0;
int numPaths = polygon.getPathCount();
for (int i = 0; i < numPaths; i++) {
double sign = polygon.isExteriorRing(i) ? 1.0 : -1.0;
sphericalExcess += sign * Math.abs(computeSphericalExcess(polygon, polygon.getPathStart(i), polygon.getPathEnd(i)));
}
// isExteriorRing returns false for the exterior ring
return Math.abs(sphericalExcess * EARTH_RADIUS_M * EARTH_RADIUS_M);
}
use of com.facebook.presto.spi.function.Description in project presto by prestodb.
the class SphericalGeoFunctions method spatialPartitions.
@ScalarFunction
@SqlNullable
@Description("Returns an array of spatial partition IDs for a geometry representing a set of points within specified distance from the input geometry")
@SqlType("array(int)")
public static Block spatialPartitions(@SqlType(KdbTreeType.NAME) Object kdbTree, @SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME) Slice geometry, @SqlType(DOUBLE) double distance) {
if (isNaN(distance)) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is NaN");
}
if (isInfinite(distance)) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is infinite");
}
if (distance < 0) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is negative");
}
Envelope envelope = deserializeEnvelope(geometry);
if (envelope.isEmpty()) {
return null;
}
Rectangle expandedEnvelope2D = new Rectangle(envelope.getXMin() - distance, envelope.getYMin() - distance, envelope.getXMax() + distance, envelope.getYMax() + distance);
return GeoFunctions.spatialPartitions((KdbTree) kdbTree, expandedEnvelope2D);
}
use of com.facebook.presto.spi.function.Description in project presto by prestodb.
the class SphericalGeoFunctions method toSphericalGeography.
@Description("Converts a Geometry object to a SphericalGeography object")
@ScalarFunction("to_spherical_geography")
@SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME)
public static Slice toSphericalGeography(@SqlType(GEOMETRY_TYPE_NAME) Slice input) {
// "every point in input is in range" <=> "the envelope of input is in range"
Envelope envelope = deserializeEnvelope(input);
if (!envelope.isEmpty()) {
checkLatitude(envelope.getYMin());
checkLatitude(envelope.getYMax());
checkLongitude(envelope.getXMin());
checkLongitude(envelope.getXMax());
}
OGCGeometry geometry = EsriGeometrySerde.deserialize(input);
if (geometry.is3D()) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Cannot convert 3D geometry to a spherical geography");
}
GeometryCursor cursor = geometry.getEsriGeometryCursor();
while (true) {
com.esri.core.geometry.Geometry subGeometry = cursor.next();
if (subGeometry == null) {
break;
}
if (!GEOMETRY_TYPES_FOR_SPHERICAL_GEOGRAPHY.contains(subGeometry.getType())) {
throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Cannot convert geometry of this type to spherical geography: " + subGeometry.getType());
}
}
return input;
}
use of com.facebook.presto.spi.function.Description in project presto by prestodb.
the class SphericalGeoFunctions method stSphericalLength.
@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 = EsriGeometrySerde.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;
}
Aggregations