private static long getGeometryMemorySize(OGCGeometry geometry) { if (geometry == null) { return 0; } // Due to the following issue: // https://github.com/Esri/geometry-api-java/issues/192 // We must check if the geometry is empty before calculating its size. Once the issue is resolved // and we bring the fix into our codebase, we can remove this check. if (geometry.isEmpty()) { return OGC_GEOMETRY_BASE_INSTANCE_SIZE; } return geometry.estimateMemorySize(); }
@SqlNullable @Description("Returns the cardinality of the collection of interior rings of a polygon") @ScalarFunction("ST_NumInteriorRing") @SqlType(BIGINT) public static Long stNumInteriorRings(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); validateType("ST_NumInteriorRing", geometry, EnumSet.of(POLYGON)); if (geometry.isEmpty()) { return null; } return Long.valueOf(((OGCPolygon) geometry).numInteriorRing()); }
@SqlNullable @Description("Returns the 2-dimensional cartesian minimum distance (based on spatial ref) between two geometries in projected units") @ScalarFunction("ST_Distance") @SqlType(DOUBLE) public static Double stDistance(@SqlType(GEOMETRY_TYPE_NAME) Slice left, @SqlType(GEOMETRY_TYPE_NAME) Slice right) { OGCGeometry leftGeometry = deserialize(left); OGCGeometry rightGeometry = deserialize(right); verifySameSpatialReference(leftGeometry, rightGeometry); return leftGeometry.isEmpty() || rightGeometry.isEmpty() ? null : leftGeometry.distance(rightGeometry); }
@SqlNullable @Description("Return the Y coordinate of the point") @ScalarFunction("ST_Y") @SqlType(DOUBLE) public static Double stY(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); validateType("ST_Y", geometry, EnumSet.of(POINT)); if (geometry.isEmpty()) { return null; } return ((OGCPoint) geometry).Y(); }
@SqlNullable @Description("Return the X coordinate of the point") @ScalarFunction("ST_X") @SqlType(DOUBLE) public static Double stX(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); validateType("ST_X", geometry, EnumSet.of(POINT)); if (geometry.isEmpty()) { return null; } return ((OGCPoint) geometry).X(); }
@Description("Returns TRUE if this Geometry has no anomalous geometric points, such as self intersection or self tangency") @ScalarFunction("ST_IsSimple") @SqlType(BOOLEAN) public static boolean stIsSimple(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); return geometry.isEmpty() || geometry.isSimple(); }
@SqlNullable @Description("Returns a line string representing the exterior ring of the POLYGON") @ScalarFunction("ST_ExteriorRing") @SqlType(GEOMETRY_TYPE_NAME) public static Slice stExteriorRing(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); validateType("ST_ExteriorRing", geometry, EnumSet.of(POLYGON)); if (geometry.isEmpty()) { return null; } return serialize(((OGCPolygon) geometry).exteriorRing()); }
@SqlNullable @Description("Returns the great-circle distance in meters between two SphericalGeography points.") @ScalarFunction("ST_Distance") @SqlType(DOUBLE) public static Double stSphericalDistance(@SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME) Slice left, @SqlType(SPHERICAL_GEOGRAPHY_TYPE_NAME) Slice right) { OGCGeometry leftGeometry = deserialize(left); OGCGeometry rightGeometry = deserialize(right); if (leftGeometry.isEmpty() || rightGeometry.isEmpty()) { return null; } // TODO: support more SphericalGeography types. validateSphericalType("ST_Distance", leftGeometry, EnumSet.of(POINT)); validateSphericalType("ST_Distance", rightGeometry, EnumSet.of(POINT)); Point leftPoint = (Point) leftGeometry.getEsriGeometry(); Point rightPoint = (Point) rightGeometry.getEsriGeometry(); // greatCircleDistance returns distance in KM. return greatCircleDistance(leftPoint.getY(), leftPoint.getX(), rightPoint.getY(), rightPoint.getX()) * 1000; }
@InputFunction public static void input(@AggregationState GeometryState state, @SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = GeometrySerde.deserialize(input); if (state.getGeometry() == null) { state.setGeometry(geometry); } else if (!geometry.isEmpty()) { state.setGeometry(state.getGeometry().union(geometry)); } }
@Description("Returns the closure of the combinatorial boundary of this Geometry") @ScalarFunction("ST_Boundary") @SqlType(GEOMETRY_TYPE_NAME) public static Slice stBoundary(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); if (geometry.isEmpty() && GeometryType.getForEsriGeometryType(geometry.geometryType()) == LINE_STRING) { // OCGGeometry#boundary crashes with NPE for LINESTRING EMPTY return EMPTY_MULTIPOINT; } return serialize(geometry.boundary()); }
@SqlNullable @Description("Returns the first point of a LINESTRING geometry as a Point") @ScalarFunction("ST_StartPoint") @SqlType(GEOMETRY_TYPE_NAME) public static Slice stStartPoint(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); validateType("ST_StartPoint", geometry, EnumSet.of(LINE_STRING)); if (geometry.isEmpty()) { return null; } MultiPath lines = (MultiPath) geometry.getEsriGeometry(); SpatialReference reference = geometry.getEsriSpatialReference(); return serialize(createFromEsriGeometry(lines.getPoint(0), reference)); }
@Description("Returns the minimum convex geometry that encloses all input geometries") @ScalarFunction("ST_ConvexHull") @SqlType(GEOMETRY_TYPE_NAME) public static Slice stConvexHull(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); if (geometry.isEmpty()) { return input; } if (GeometryType.getForEsriGeometryType(geometry.geometryType()) == POINT) { return input; } return serialize(geometry.convexHull()); }
@SqlNullable @Description("Returns the last point of a LINESTRING geometry as a Point") @ScalarFunction("ST_EndPoint") @SqlType(GEOMETRY_TYPE_NAME) public static Slice stEndPoint(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); validateType("ST_EndPoint", geometry, EnumSet.of(LINE_STRING)); if (geometry.isEmpty()) { return null; } MultiPath lines = (MultiPath) geometry.getEsriGeometry(); SpatialReference reference = geometry.getEsriSpatialReference(); return serialize(createFromEsriGeometry(lines.getPoint(lines.getPointCount() - 1), reference)); }
@SqlNullable @Description("Returns the geometry that represents all points whose distance from the specified geometry is less than or equal to the specified distance") @ScalarFunction("ST_Buffer") @SqlType(GEOMETRY_TYPE_NAME) public static Slice stBuffer(@SqlType(GEOMETRY_TYPE_NAME) Slice input, @SqlType(DOUBLE) double distance) { if (isNaN(distance)) { throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is NaN"); } if (distance < 0) { throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "distance is negative"); } if (distance == 0) { return input; } OGCGeometry geometry = deserialize(input); if (geometry.isEmpty()) { return null; } return serialize(geometry.buffer(distance)); }
@Description("Returns the cardinality of the geometry collection") @ScalarFunction("ST_NumGeometries") @SqlType(INTEGER) public static long stNumGeometries(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); if (geometry.isEmpty()) { return 0; } GeometryType type = GeometryType.getForEsriGeometryType(geometry.geometryType()); if (!type.isMultitype()) { return 1; } return ((OGCGeometryCollection) geometry).numGeometries(); }
@InputFunction public static void input(@AggregationState GeometryState state, @SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = GeometrySerde.deserialize(input); if (state.getGeometry() == null) { state.setGeometry(geometry.convexHull()); } else if (!geometry.isEmpty()) { state.setGeometry(state.getGeometry().union(geometry).convexHull()); } }
@CombineFunction public static void combine(@AggregationState GeometryState state, @AggregationState GeometryState otherState) { if (state.getGeometry() == null) { state.setGeometry(otherState.getGeometry()); } else if (otherState.getGeometry() != null && !otherState.getGeometry().isEmpty()) { state.setGeometry(state.getGeometry().union(otherState.getGeometry())); } }
@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(); }
@CombineFunction public static void combine(@AggregationState GeometryState state, @AggregationState GeometryState otherState) { if (state.getGeometry() == null) { state.setGeometry(otherState.getGeometry()); } else if (otherState.getGeometry() != null && !otherState.getGeometry().isEmpty()) { state.setGeometry(state.getGeometry().union(otherState.getGeometry()).convexHull()); } }
@SqlNullable @Description("Returns an array of geometries in the specified collection") @ScalarFunction("ST_Geometries") @SqlType("array(" + GEOMETRY_TYPE_NAME + ")") public static Block stGeometries(@SqlType(GEOMETRY_TYPE_NAME) Slice input) { OGCGeometry geometry = deserialize(input); if (geometry.isEmpty()) { return null; } GeometryType type = GeometryType.getForEsriGeometryType(geometry.geometryType()); if (!type.isMultitype()) { BlockBuilder blockBuilder = GEOMETRY.createBlockBuilder(null, 1); GEOMETRY.writeSlice(blockBuilder, serialize(geometry)); return blockBuilder.build(); } OGCGeometryCollection collection = (OGCGeometryCollection) geometry; BlockBuilder blockBuilder = GEOMETRY.createBlockBuilder(null, collection.numGeometries()); for (int i = 0; i < collection.numGeometries(); i++) { GEOMETRY.writeSlice(blockBuilder, serialize(collection.geometryN(i))); } return blockBuilder.build(); }