org.apache.calcite.runtime.GeoFunctions Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of calcite-core Show documentation
Show all versions of calcite-core Show documentation
Core Calcite APIs and engine.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.runtime;
import org.apache.calcite.linq4j.function.Deterministic;
import org.apache.calcite.linq4j.function.Experimental;
import org.apache.calcite.linq4j.function.SemiStrict;
import org.apache.calcite.linq4j.function.Strict;
import org.apache.calcite.util.Util;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.Geometry;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.Line;
import com.esri.core.geometry.MapGeometry;
import com.esri.core.geometry.Operator;
import com.esri.core.geometry.OperatorBoundary;
import com.esri.core.geometry.OperatorFactoryLocal;
import com.esri.core.geometry.OperatorIntersects;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.Polygon;
import com.esri.core.geometry.Polyline;
import com.esri.core.geometry.SpatialReference;
import com.esri.core.geometry.WktExportFlags;
import com.esri.core.geometry.WktImportFlags;
import java.math.BigDecimal;
import java.util.Objects;
/**
* Helper methods to implement Geo-spatial functions in generated code.
*
* Remaining tasks:
*
*
* - Determine type code for
* {@link org.apache.calcite.sql.type.ExtraSqlTypes#GEOMETRY}
*
- Should we create aliases for functions in upper-case?
* Without ST_ prefix?
*
- Consider adding spatial literals, e.g. `GEOMETRY 'POINT (30 10)'`
*
- Integer arguments, e.g. SELECT ST_MakePoint(1, 2, 1.5),
* ST_MakePoint(1, 2)
*
- Are GEOMETRY values comparable? If so add ORDER BY test
*
- We have to add 'Z' to create 3D objects. This is inconsistent with
* PostGIS. Who is right? At least document the difference.
*
- Should add GeometryEngine.intersects; similar to disjoint etc.
*
- Make {@link #ST_MakeLine(Geom, Geom)} varargs
*
*/
@SuppressWarnings({"UnnecessaryUnboxing", "WeakerAccess", "unused"})
@Deterministic
@Strict
@Experimental
public class GeoFunctions {
private static final int NO_SRID = 0;
private static final SpatialReference SPATIAL_REFERENCE =
SpatialReference.create(4326);
private GeoFunctions() {}
private static UnsupportedOperationException todo() {
return new UnsupportedOperationException();
}
protected static Geom bind(Geometry geometry, int srid) {
if (geometry == null) {
return null;
}
if (srid == NO_SRID) {
return new SimpleGeom(geometry);
}
return bind(geometry, SpatialReference.create(srid));
}
private static MapGeom bind(Geometry geometry, SpatialReference sr) {
return new MapGeom(new MapGeometry(geometry, sr));
}
// Geometry conversion functions (2D and 3D) ================================
public static String ST_AsText(Geom g) {
return ST_AsWKT(g);
}
public static String ST_AsWKT(Geom g) {
return GeometryEngine.geometryToWkt(g.g(),
WktExportFlags.wktExportDefaults);
}
public static Geom ST_GeomFromText(String s) {
return ST_GeomFromText(s, NO_SRID);
}
public static Geom ST_GeomFromText(String s, int srid) {
final Geometry g = GeometryEngine.geometryFromWkt(s,
WktImportFlags.wktImportDefaults, Geometry.Type.Unknown);
return bind(g, srid);
}
public static Geom ST_LineFromText(String s) {
return ST_GeomFromText(s, NO_SRID);
}
public static Geom ST_LineFromText(String wkt, int srid) {
final Geometry g = GeometryEngine.geometryFromWkt(wkt,
WktImportFlags.wktImportDefaults,
Geometry.Type.Line);
return bind(g, srid);
}
public static Geom ST_MPointFromText(String s) {
return ST_GeomFromText(s, NO_SRID);
}
public static Geom ST_MPointFromText(String wkt, int srid) {
final Geometry g = GeometryEngine.geometryFromWkt(wkt,
WktImportFlags.wktImportDefaults,
Geometry.Type.MultiPoint);
return bind(g, srid);
}
public static Geom ST_PointFromText(String s) {
return ST_GeomFromText(s, NO_SRID);
}
public static Geom ST_PointFromText(String wkt, int srid) {
final Geometry g = GeometryEngine.geometryFromWkt(wkt,
WktImportFlags.wktImportDefaults,
Geometry.Type.Point);
return bind(g, srid);
}
public static Geom ST_PolyFromText(String s) {
return ST_GeomFromText(s, NO_SRID);
}
public static Geom ST_PolyFromText(String wkt, int srid) {
final Geometry g = GeometryEngine.geometryFromWkt(wkt,
WktImportFlags.wktImportDefaults,
Geometry.Type.Polygon);
return bind(g, srid);
}
public static Geom ST_MLineFromText(String s) {
return ST_GeomFromText(s, NO_SRID);
}
public static Geom ST_MLineFromText(String wkt, int srid) {
final Geometry g = GeometryEngine.geometryFromWkt(wkt,
WktImportFlags.wktImportDefaults,
Geometry.Type.Unknown); // NOTE: there is no Geometry.Type.MultiLine
return bind(g, srid);
}
public static Geom ST_MPolyFromText(String s) {
return ST_GeomFromText(s, NO_SRID);
}
public static Geom ST_MPolyFromText(String wkt, int srid) {
final Geometry g = GeometryEngine.geometryFromWkt(wkt,
WktImportFlags.wktImportDefaults,
Geometry.Type.Unknown); // NOTE: there is no Geometry.Type.MultiPolygon
return bind(g, srid);
}
// Geometry creation functions ==============================================
/** Creates a line-string from the given POINTs (or MULTIPOINTs). */
public static Geom ST_MakeLine(Geom geom1, Geom geom2) {
return makeLine(geom1, geom2);
}
public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3) {
return makeLine(geom1, geom2, geom3);
}
public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3,
Geom geom4) {
return makeLine(geom1, geom2, geom3, geom4);
}
public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3,
Geom geom4, Geom geom5) {
return makeLine(geom1, geom2, geom3, geom4, geom5);
}
public static Geom ST_MakeLine(Geom geom1, Geom geom2, Geom geom3,
Geom geom4, Geom geom5, Geom geom6) {
return makeLine(geom1, geom2, geom3, geom4, geom5, geom6);
}
private static Geom makeLine(Geom... geoms) {
final Polyline g = new Polyline();
Point p = null;
for (Geom geom : geoms) {
if (geom.g() instanceof Point) {
final Point prev = p;
p = (Point) geom.g();
if (prev != null) {
final Line line = new Line();
line.setStart(prev);
line.setEnd(p);
g.addSegment(line, false);
}
}
}
return new SimpleGeom(g);
}
/** Alias for {@link #ST_Point(BigDecimal, BigDecimal)}. */
public static Geom ST_MakePoint(BigDecimal x, BigDecimal y) {
return ST_Point(x, y);
}
/** Alias for {@link #ST_Point(BigDecimal, BigDecimal, BigDecimal)}. */
public static Geom ST_MakePoint(BigDecimal x, BigDecimal y, BigDecimal z) {
return ST_Point(x, y, z);
}
/** Constructs a 2D point from coordinates. */
public static Geom ST_Point(BigDecimal x, BigDecimal y) {
// NOTE: Combine the double and BigDecimal variants of this function
return point(x.doubleValue(), y.doubleValue());
}
/** Constructs a 3D point from coordinates. */
public static Geom ST_Point(BigDecimal x, BigDecimal y, BigDecimal z) {
final Geometry g = new Point(x.doubleValue(), y.doubleValue(),
z.doubleValue());
return new SimpleGeom(g);
}
private static Geom point(double x, double y) {
final Geometry g = new Point(x, y);
return new SimpleGeom(g);
}
// Geometry properties (2D and 3D) ==========================================
/** Returns whether {@code geom} has at least one z-coordinate. */
public static boolean ST_Is3D(Geom geom) {
return geom.g().hasZ();
}
/** Returns the x-value of the first coordinate of {@code geom}. */
public static Double ST_X(Geom geom) {
return geom.g() instanceof Point ? ((Point) geom.g()).getX() : null;
}
/** Returns the y-value of the first coordinate of {@code geom}. */
public static Double ST_Y(Geom geom) {
return geom.g() instanceof Point ? ((Point) geom.g()).getY() : null;
}
/** Returns the z-value of the first coordinate of {@code geom}. */
public static Double ST_Z(Geom geom) {
return geom.g().getDescription().hasZ() && geom.g() instanceof Point
? ((Point) geom.g()).getZ() : null;
}
/** Returns the boundary of {@code geom}. */
public static Geom ST_Boundary(Geom geom) {
OperatorBoundary op = OperatorBoundary.local();
Geometry result = op.execute(geom.g(), null);
return geom.wrap(result);
}
/** Returns the distance between {@code geom1} and {@code geom2}. */
public static double ST_Distance(Geom geom1, Geom geom2) {
return GeometryEngine.distance(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns the type of {@code geom}. */
public static String ST_GeometryType(Geom geom) {
return type(geom.g()).name();
}
/** Returns the OGC SFS type code of {@code geom}. */
public static int ST_GeometryTypeCode(Geom geom) {
return type(geom.g()).code;
}
/** Returns the OGC type of a geometry. */
private static Type type(Geometry g) {
switch (g.getType()) {
case Point:
return Type.POINT;
case Polyline:
return Type.LINESTRING;
case Polygon:
return Type.POLYGON;
case MultiPoint:
return Type.MULTIPOINT;
case Envelope:
return Type.POLYGON;
case Line:
return Type.LINESTRING;
case Unknown:
return Type.Geometry;
default:
throw new AssertionError(g);
}
}
/** Returns the minimum bounding box of {@code geom} (which may be a
* GEOMETRYCOLLECTION). */
public static Geom ST_Envelope(Geom geom) {
final Envelope env = envelope(geom.g());
return geom.wrap(env);
}
private static Envelope envelope(Geometry g) {
final Envelope env = new Envelope();
g.queryEnvelope(env);
return env;
}
// Geometry predicates ======================================================
/** Returns whether {@code geom1} contains {@code geom2}. */
public static boolean ST_Contains(Geom geom1, Geom geom2) {
return GeometryEngine.contains(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns whether {@code geom1} contains {@code geom2} but does not
* intersect its boundary. */
public static boolean ST_ContainsProperly(Geom geom1, Geom geom2) {
return GeometryEngine.contains(geom1.g(), geom2.g(), geom1.sr())
&& !GeometryEngine.crosses(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns whether no point in {@code geom2} is outside {@code geom1}. */
private static boolean ST_Covers(Geom geom1, Geom geom2) {
throw todo();
}
/** Returns whether {@code geom1} crosses {@code geom2}. */
public static boolean ST_Crosses(Geom geom1, Geom geom2) {
return GeometryEngine.crosses(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns whether {@code geom1} and {@code geom2} are disjoint. */
public static boolean ST_Disjoint(Geom geom1, Geom geom2) {
return GeometryEngine.disjoint(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns whether the envelope of {@code geom1} intersects the envelope of
* {@code geom2}. */
public static boolean ST_EnvelopesIntersect(Geom geom1, Geom geom2) {
final Geometry e1 = envelope(geom1.g());
final Geometry e2 = envelope(geom2.g());
return intersects(e1, e2, geom1.sr());
}
/** Returns whether {@code geom1} equals {@code geom2}. */
public static boolean ST_Equals(Geom geom1, Geom geom2) {
return GeometryEngine.equals(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns whether {@code geom1} intersects {@code geom2}. */
public static boolean ST_Intersects(Geom geom1, Geom geom2) {
final Geometry g1 = geom1.g();
final Geometry g2 = geom2.g();
final SpatialReference sr = geom1.sr();
return intersects(g1, g2, sr);
}
private static boolean intersects(Geometry g1, Geometry g2,
SpatialReference sr) {
final OperatorIntersects op = (OperatorIntersects) OperatorFactoryLocal
.getInstance().getOperator(Operator.Type.Intersects);
return op.execute(g1, g2, sr, null);
}
/** Returns whether {@code geom1} equals {@code geom2} and their coordinates
* and component Geometries are listed in the same order. */
public static boolean ST_OrderingEquals(Geom geom1, Geom geom2) {
return GeometryEngine.equals(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns {@code geom1} overlaps {@code geom2}. */
public static boolean ST_Overlaps(Geom geom1, Geom geom2) {
return GeometryEngine.overlaps(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns whether {@code geom1} touches {@code geom2}. */
public static boolean ST_Touches(Geom geom1, Geom geom2) {
return GeometryEngine.touches(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns whether {@code geom1} is within {@code geom2}. */
public static boolean ST_Within(Geom geom1, Geom geom2) {
return GeometryEngine.within(geom1.g(), geom2.g(), geom1.sr());
}
/** Returns whether {@code geom1} and {@code geom2} are within
* {@code distance} of each other. */
public static boolean ST_DWithin(Geom geom1, Geom geom2, double distance) {
final double distance1 =
GeometryEngine.distance(geom1.g(), geom2.g(), geom1.sr());
return distance1 <= distance;
}
// Geometry operators (2D and 3D) ===========================================
/** Computes a buffer around {@code geom}. */
public static Geom ST_Buffer(Geom geom, double distance) {
final Polygon g = GeometryEngine.buffer(geom.g(), geom.sr(), distance);
return geom.wrap(g);
}
/** Computes a buffer around {@code geom} with . */
public static Geom ST_Buffer(Geom geom, double distance, int quadSegs) {
throw todo();
}
/** Computes a buffer around {@code geom}. */
public static Geom ST_Buffer(Geom geom, double bufferSize, String style) {
int quadSegCount = 8;
CapStyle endCapStyle = CapStyle.ROUND;
JoinStyle joinStyle = JoinStyle.ROUND;
float mitreLimit = 5f;
int i = 0;
parse:
for (;;) {
int equals = style.indexOf('=', i);
if (equals < 0) {
break;
}
int space = style.indexOf(' ', equals);
if (space < 0) {
space = style.length();
}
String name = style.substring(i, equals);
String value = style.substring(equals + 1, space);
switch (name) {
case "quad_segs":
quadSegCount = Integer.valueOf(value);
break;
case "endcap":
endCapStyle = CapStyle.of(value);
break;
case "join":
joinStyle = JoinStyle.of(value);
break;
case "mitre_limit":
case "miter_limit":
mitreLimit = Float.parseFloat(value);
break;
default:
// ignore the value
}
i = space;
for (;;) {
if (i >= style.length()) {
break parse;
}
if (style.charAt(i) != ' ') {
break;
}
++i;
}
}
return buffer(geom, bufferSize, quadSegCount, endCapStyle, joinStyle,
mitreLimit);
}
private static Geom buffer(Geom geom, double bufferSize,
int quadSegCount, CapStyle endCapStyle, JoinStyle joinStyle,
float mitreLimit) {
Util.discard(endCapStyle + ":" + joinStyle + ":" + mitreLimit
+ ":" + quadSegCount);
throw todo();
}
/** Computes the union of {@code geom1} and {@code geom2}. */
public static Geom ST_Union(Geom geom1, Geom geom2) {
SpatialReference sr = geom1.sr();
final Geometry g =
GeometryEngine.union(new Geometry[]{geom1.g(), geom2.g()}, sr);
return bind(g, sr);
}
/** Computes the union of the geometries in {@code geomCollection}. */
@SemiStrict public static Geom ST_Union(Geom geomCollection) {
SpatialReference sr = geomCollection.sr();
final Geometry g =
GeometryEngine.union(new Geometry[] {geomCollection.g()}, sr);
return bind(g, sr);
}
// Geometry projection functions ============================================
/** Transforms {@code geom} from one coordinate reference
* system (CRS) to the CRS specified by {@code srid}. */
public static Geom ST_Transform(Geom geom, int srid) {
return geom.transform(srid);
}
/** Returns a copy of {@code geom} with a new SRID. */
public static Geom ST_SetSRID(Geom geom, int srid) {
return geom.transform(srid);
}
// Inner classes ============================================================
/** How the "buffer" command terminates the end of a line. */
enum CapStyle {
ROUND, FLAT, SQUARE;
static CapStyle of(String value) {
switch (value) {
case "round":
return ROUND;
case "flat":
case "butt":
return FLAT;
case "square":
return SQUARE;
default:
throw new IllegalArgumentException("unknown endcap value: " + value);
}
}
}
/** How the "buffer" command decorates junctions between line segments. */
enum JoinStyle {
ROUND, MITRE, BEVEL;
static JoinStyle of(String value) {
switch (value) {
case "round":
return ROUND;
case "mitre":
case "miter":
return MITRE;
case "bevel":
return BEVEL;
default:
throw new IllegalArgumentException("unknown join value: " + value);
}
}
}
/** Geometry. It may or may not have a spatial reference
* associated with it. */
public interface Geom {
Geometry g();
SpatialReference sr();
Geom transform(int srid);
Geom wrap(Geometry g);
}
/** Sub-class of geometry that has no spatial reference. */
static class SimpleGeom implements Geom {
final Geometry g;
SimpleGeom(Geometry g) {
this.g = Objects.requireNonNull(g);
}
@Override public String toString() {
return g.toString();
}
public Geometry g() {
return g;
}
public SpatialReference sr() {
return SPATIAL_REFERENCE;
}
public Geom transform(int srid) {
if (srid == SPATIAL_REFERENCE.getID()) {
return this;
}
return bind(g, srid);
}
public Geom wrap(Geometry g) {
return new SimpleGeom(g);
}
}
/** Sub-class of geometry that has a spatial reference. */
static class MapGeom implements Geom {
final MapGeometry mg;
MapGeom(MapGeometry mg) {
this.mg = Objects.requireNonNull(mg);
}
@Override public String toString() {
return mg.toString();
}
public Geometry g() {
return mg.getGeometry();
}
public SpatialReference sr() {
return mg.getSpatialReference();
}
public Geom transform(int srid) {
if (srid == NO_SRID) {
return new SimpleGeom(mg.getGeometry());
}
if (srid == mg.getSpatialReference().getID()) {
return this;
}
return bind(mg.getGeometry(), srid);
}
public Geom wrap(Geometry g) {
return bind(g, this.mg.getSpatialReference());
}
}
/** Geometry types, with the names and codes assigned by OGC. */
enum Type {
Geometry(0),
POINT(1),
LINESTRING(2),
POLYGON(3),
MULTIPOINT(4),
MULTILINESTRING(5),
MULTIPOLYGON(6),
GEOMCOLLECTION(7),
CURVE(13),
SURFACE(14),
POLYHEDRALSURFACE(15);
final int code;
Type(int code) {
this.code = code;
}
}
}
// End GeoFunctions.java
© 2015 - 2024 Weber Informatics LLC | Privacy Policy