diff --git a/core/src/main/java/org/polypheny/db/algebra/enumerable/RexImpTable.java b/core/src/main/java/org/polypheny/db/algebra/enumerable/RexImpTable.java index e6c94821ae..1d9626669c 100644 --- a/core/src/main/java/org/polypheny/db/algebra/enumerable/RexImpTable.java +++ b/core/src/main/java/org/polypheny/db/algebra/enumerable/RexImpTable.java @@ -385,6 +385,18 @@ public Expression implement( RexToLixTranslator translator, RexCall call, ListST_WithinDistance operator function: check if two {@link org.polypheny.db.type.entity.spatial.PolyGeometry} are withing the given distance + */ + ST_WITHINDISTANCE( Function.class ), + + /** + * The ST_Disjoint operator function: check if two {@link org.polypheny.db.type.entity.spatial.PolyGeometry} are disjoint + */ + ST_DISJOINT( Function.class ), + + /** + * The ST_Touches operator function: check if {@link org.polypheny.db.type.entity.spatial.PolyGeometry} touches another + */ + ST_TOUCHES( Function.class ), + + /** + * The ST_Intersects operator function: check if {@link org.polypheny.db.type.entity.spatial.PolyGeometry} intersects another + */ + ST_INTERSECTS( Function.class ), + + /** + * The ST_Crosses operator function: check if {@link org.polypheny.db.type.entity.spatial.PolyGeometry} crosses another + */ + ST_CROSSES( Function.class ), + + /** + * The ST_Within operator function: check if {@link org.polypheny.db.type.entity.spatial.PolyGeometry} is within another + */ + ST_WITHIN( Function.class ), + + /** + * The ST_Contains operator function: check if {@link org.polypheny.db.type.entity.spatial.PolyGeometry} contains another + */ + ST_CONTAINS( Function.class ), + + /** + * The ST_Overlaps operator function: check if {@link org.polypheny.db.type.entity.spatial.PolyGeometry} overlaps another + */ + ST_OVERLAPS( Function.class ), + + /** + * The ST_Covers operator function: check if {@link org.polypheny.db.type.entity.spatial.PolyGeometry} covers another + */ + ST_COVERS( Function.class ), + + /** + * The ST_CoveredBy operator function: check if {@link org.polypheny.db.type.entity.spatial.PolyGeometry} is covered by another + */ + ST_COVEREDBY( Function.class ), + + /** + * The ST_Relate operator function: check if two {@link org.polypheny.db.type.entity.spatial.PolyGeometry} are relate + */ + ST_RELATE( Function.class ), + // Yield metric values + /** * The ST_Distance operator function: compute the distance between two {@link org.polypheny.db.type.entity.spatial.PolyGeometry} */ diff --git a/core/src/main/java/org/polypheny/db/functions/GeoFunctions.java b/core/src/main/java/org/polypheny/db/functions/GeoFunctions.java index 4b3d26c97b..d9e02c36ab 100644 --- a/core/src/main/java/org/polypheny/db/functions/GeoFunctions.java +++ b/core/src/main/java/org/polypheny/db/functions/GeoFunctions.java @@ -182,10 +182,96 @@ public static PolyGeometry stBuffer( PolyGeometry geometry, PolyNumber distance, } /* - * TODO: Spatial relationships + * Spatial relationships */ + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stWithinDistance( PolyGeometry g1, PolyGeometry g2, PolyNumber distance ) { + restrictToSrid( g1, g2 ); + try { + return PolyBoolean.of( g1.isWithinDistance( g2, distance.doubleValue() ) ); + } catch ( GeometryTopologicalException e ) { + throw toUnchecked( e ); + } + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stDisjoint( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.disjoint( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stTouches( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.touches( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stIntersects( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.intersects( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stCrosses( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.crosses( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stWithin( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.within( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stContains( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.contains( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stOverlaps( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.overlaps( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stCovers( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.covers( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stCoveredBy( PolyGeometry g1, PolyGeometry g2 ) { + restrictToSrid( g1, g2 ); + return PolyBoolean.of( g1.coveredBy( g2 ) ); + } + + + @SuppressWarnings("UnusedDeclaration") + public static PolyBoolean stRelate( PolyGeometry g1, PolyGeometry g2, PolyString pattern ) { + restrictToSrid( g1, g2 ); + try { + return PolyBoolean.of( g1.relate( g2, pattern.value ) ); + } catch ( GeometryTopologicalException e ) { + throw toUnchecked( e ); + } + } + + + /* * Yield metric values */ diff --git a/core/src/main/java/org/polypheny/db/functions/spatial/GeoDistanceFunctions.java b/core/src/main/java/org/polypheny/db/functions/spatial/GeoDistanceFunctions.java index 310c4736a1..689e770365 100644 --- a/core/src/main/java/org/polypheny/db/functions/spatial/GeoDistanceFunctions.java +++ b/core/src/main/java/org/polypheny/db/functions/spatial/GeoDistanceFunctions.java @@ -27,7 +27,7 @@ import org.polypheny.db.type.entity.spatial.PolyPoint; /** - * Calculate the spherical distances between various geometries + * Calculate the spherical distances between various geometries on the PERFECT SPHERE */ public class GeoDistanceFunctions { @@ -40,12 +40,30 @@ private GeoDistanceFunctions() { } + /** + * Check that the distance between two geometries is within the given threshold + * + * @param g1 one geometry + * @param g2 another one + * @param distanceThreshold limit the distance between geometries + * @return TRUE if two geometries are within the given distance + * @throws GeometryTopologicalException if distance cannot be calculated + */ public static boolean isWithinSphericalDistance( @NotNull PolyGeometry g1, @NotNull PolyGeometry g2, double distanceThreshold ) throws GeometryTopologicalException { return sphericalDistance( g1, g2 ) <= distanceThreshold; } + /** + * Calculate the spherical distance between 2 geometries + * + * @param g1 one geometry + * @param g2 another + * @return the distance between geometries + * @throws GeometryTopologicalException if distance cannot be calculated + */ public static double sphericalDistance( @NotNull PolyGeometry g1, @NotNull PolyGeometry g2 ) throws GeometryTopologicalException { + // distance calculation are dependent on the type of the geometry if ( g1.isPoint() && g2.isPoint() ) { return calculateSphericalDistance( g1.asPoint(), g2.asPoint() ); } else if ( g1.isLineString() && g2.isLineString() ) { diff --git a/core/src/main/java/org/polypheny/db/functions/spatial/GeoTransformFunctions.java b/core/src/main/java/org/polypheny/db/functions/spatial/GeoTransformFunctions.java index 46d3fffc99..3cb83cb16b 100644 --- a/core/src/main/java/org/polypheny/db/functions/spatial/GeoTransformFunctions.java +++ b/core/src/main/java/org/polypheny/db/functions/spatial/GeoTransformFunctions.java @@ -29,16 +29,22 @@ import org.locationtech.proj4j.CoordinateTransformFactory; import org.locationtech.proj4j.Proj4jException; import org.locationtech.proj4j.ProjCoordinate; +import org.locationtech.proj4j.proj.Projection; import org.polypheny.db.type.entity.spatial.InvalidGeometryException; import org.polypheny.db.type.entity.spatial.PolyGeometry; import org.polypheny.db.type.entity.spatial.PolyGeometryCollection; /** + * Geo functions to: * Transform coordinates to various SRID + * Project SRID coordinates on the plane */ public class GeoTransformFunctions { private static final String SRID_PREFIX = "EPSG:"; + private static final PrecisionModel PRECISION_MODEL = new PrecisionModel( PrecisionModel.FLOATING ); + // Create a CRSFactory to manage coordinate reference systems + private static final CRSFactory crsFactory = new CRSFactory(); private GeoTransformFunctions() { @@ -46,62 +52,83 @@ private GeoTransformFunctions() { } + /** + * Transform coordinates {@link PolyGeometry} into the given SRID. + * + * @param geometry to transform + * @param srid new spatial reference + * @return {@link PolyGeometry} with coordinates in given SRID + * @throws InvalidGeometryException if coordinates cannot be transformed + */ public static PolyGeometry transform( PolyGeometry geometry, int srid ) throws InvalidGeometryException { - // Create a CRSFactory to manage coordinate reference systems - CRSFactory crsFactory = new CRSFactory(); // Create the original and target coordinate reference systems CoordinateReferenceSystem sourceCrs = crsFactory.createFromName( SRID_PREFIX + geometry.getSRID() ); CoordinateReferenceSystem targetCrs = crsFactory.createFromName( SRID_PREFIX + srid ); // create Geometry factory with new srid - GeometryFactory geometryFactory = new GeometryFactory( new PrecisionModel( PrecisionModel.FLOATING ), srid ); + GeometryFactory geometryFactory = new GeometryFactory( PRECISION_MODEL, srid ); + // transform coordinates + return modifyCoordinates( geometry, geometryFactory, (coordinates -> transformCoordinates( coordinates, sourceCrs, targetCrs )) ); + } + + + /** + * Project coordinates of {@link PolyGeometry} with SRID on the plane. + * + * @param geometry to transform + * @return {@link PolyGeometry} with coordinates projected on the plane(SRID=0) + * @throws InvalidGeometryException if coordinates cannot be projected + */ + public static PolyGeometry project( PolyGeometry geometry ) throws InvalidGeometryException { + // Create the coordinate reference systems + CoordinateReferenceSystem crs = crsFactory.createFromName( SRID_PREFIX + geometry.getSRID() ); + // create Geometry factory with srid of the plane + GeometryFactory geometryFactory = new GeometryFactory( PRECISION_MODEL, PolyGeometry.NO_SRID ); + // project coordinates + return modifyCoordinates( geometry, geometryFactory, (coordinates -> projectCoordinates( coordinates, crs )) ); + } + + /** + * Modify the coordinates using the modification function. + * + * @param geometry {@link PolyGeometry} original geometry + * @param geometryFactory {@link GeometryFactory} with SRID that will create correct geometries + * @param func to apply to coordinates + * @return {@link PolyGeometry} with modified coordinates + * @throws InvalidGeometryException in case modification of coordinates failed + */ + private static PolyGeometry modifyCoordinates( PolyGeometry geometry, GeometryFactory geometryFactory, CoordinatesModificationFunction func ) throws InvalidGeometryException { if ( geometry.isGeometryCollection() ) { + // for GeometryCollection every geometry is needed to be modified + // and then the new GeometryCollection of modified geometries is created PolyGeometryCollection geometryCollection = geometry.asGeometryCollection(); Geometry[] geometries = new Geometry[geometryCollection.getNumGeometries()]; for ( int i = 0; i < geometryCollection.getNumGeometries(); i++ ) { Geometry geom = geometryCollection.getGeometryN( i ).getJtsGeometry(); // Convert the Geometry to Proj4J coordinates - ProjCoordinate[] originalCoords = convertToProj4JCoordinates( geom ); - // Perform the SRID conversion - ProjCoordinate[] targetCoords = convertCoordinates( originalCoords, sourceCrs, targetCrs ); + ProjCoordinate[] coordinates = convertToProj4JCoordinates( geom ); + // Perform the SRID modification of coordinates + ProjCoordinate[] modifiedCoordinates = func.apply( coordinates ); // Convert Proj4J coordinates back to Geometry - geometries[i] = convertToGeometry( targetCoords, geom, geometryFactory ); - } - switch ( geometry.getGeometryType() ) { - case GEOMETRYCOLLECTION: - return PolyGeometry.of( geometryFactory.createGeometryCollection( geometries ) ); - case MULTIPOINT: - Point[] points = new Point[geometries.length]; - for ( int i = 0; i < geometries.length; i++ ) { - points[i] = geometryFactory.createPoint( geometries[i].getCoordinate() ); - } - return PolyGeometry.of( geometryFactory.createMultiPoint( points ) ); - case MULTILINESTRING: - LineString[] lineStrings = new LineString[geometries.length]; - for ( int i = 0; i < geometries.length; i++ ) { - lineStrings[i] = geometryFactory.createLineString( geometries[i].getCoordinates() ); - } - return PolyGeometry.of( geometryFactory.createMultiLineString( lineStrings ) ); - case MULTIPOLYGON: - Polygon[] polygons = new Polygon[geometries.length]; - for ( int i = 0; i < geometries.length; i++ ) { - polygons[i] = geometryFactory.createPolygon( geometries[i].getCoordinates() ); - } - return PolyGeometry.of( geometryFactory.createMultiPolygon( polygons ) ); - default: - throw new InvalidGeometryException( "Cannot convert back to GeometryCollection" ); + geometries[i] = convertToGeometry( modifiedCoordinates, geom, geometryFactory ); } + return createCorrectGeometryCollection( geometry, geometries, geometryFactory ); } else { - ProjCoordinate[] originalCoords = convertToProj4JCoordinates( geometry.getJtsGeometry() ); - ProjCoordinate[] targetCoords = convertCoordinates( originalCoords, sourceCrs, targetCrs ); - return PolyGeometry.of( convertToGeometry( targetCoords, geometry.getJtsGeometry(), geometryFactory ) ); + // coordinates of a single Geometry are modified + // and Geometry of the same type with coordinates is returned + ProjCoordinate[] coordinates = convertToProj4JCoordinates( geometry.getJtsGeometry() ); + ProjCoordinate[] modifiedCoordinates = func.apply( coordinates ); + return PolyGeometry.of( convertToGeometry( modifiedCoordinates, geometry.getJtsGeometry(), geometryFactory ) ); } - - } - // Convert Geometry coordinates to Proj4J coordinates + /** + * Convert Geometry coordinates to Proj4J coordinates + * + * @param geometry original {@link Geometry} + * @return array of {@link ProjCoordinate} objects with coordinates of original {@link Geometry} + */ private static ProjCoordinate[] convertToProj4JCoordinates( Geometry geometry ) { ProjCoordinate[] projCoords = new ProjCoordinate[geometry.getNumPoints()]; for ( int i = 0; i < geometry.getNumPoints(); i++ ) { @@ -111,7 +138,15 @@ private static ProjCoordinate[] convertToProj4JCoordinates( Geometry geometry ) } - // Convert Proj4J coordinates back to Geometry + /** + * Convert Proj4J coordinates back to Geometry of correct type. + * + * @param projCoords coordinates in {@link ProjCoordinate} + * @param geometry original {@link Geometry} for the reference + * @param geometryFactory {@link GeometryFactory} with SRID that will create correct geometries + * @return {@link Geometry} of correct type with new coordinates + * @throws InvalidGeometryException if geometry cannot be created from provided coordinates + */ private static Geometry convertToGeometry( ProjCoordinate[] projCoords, Geometry geometry, GeometryFactory geometryFactory ) throws InvalidGeometryException { Coordinate[] originalCoordinates = geometry.getCoordinates(); Coordinate[] coordinates = new Coordinate[projCoords.length]; @@ -136,8 +171,51 @@ private static Geometry convertToGeometry( ProjCoordinate[] projCoords, Geometry } - // Helper method to perform the SRID conversion - private static ProjCoordinate[] convertCoordinates( + /** + * Create the geometry collection of correct types + * + * @param geometry original of {@link PolyGeometry} + * @param geometries array of {@link Geometry} + * @param geometryFactory {@link GeometryFactory} with SRID that will create correct geometries + * @return {@link PolyGeometry} that incorporates the correct {@link org.locationtech.jts.geom.GeometryCollection} + */ + private static PolyGeometry createCorrectGeometryCollection( PolyGeometry geometry, Geometry[] geometries, GeometryFactory geometryFactory ) throws InvalidGeometryException { + switch ( geometry.getGeometryType() ) { + case GEOMETRYCOLLECTION: + return PolyGeometry.of( geometryFactory.createGeometryCollection( geometries ) ); + case MULTIPOINT: + Point[] points = new Point[geometries.length]; + for ( int i = 0; i < geometries.length; i++ ) { + points[i] = geometryFactory.createPoint( geometries[i].getCoordinate() ); + } + return PolyGeometry.of( geometryFactory.createMultiPoint( points ) ); + case MULTILINESTRING: + LineString[] lineStrings = new LineString[geometries.length]; + for ( int i = 0; i < geometries.length; i++ ) { + lineStrings[i] = geometryFactory.createLineString( geometries[i].getCoordinates() ); + } + return PolyGeometry.of( geometryFactory.createMultiLineString( lineStrings ) ); + case MULTIPOLYGON: + Polygon[] polygons = new Polygon[geometries.length]; + for ( int i = 0; i < geometries.length; i++ ) { + polygons[i] = geometryFactory.createPolygon( geometries[i].getCoordinates() ); + } + return PolyGeometry.of( geometryFactory.createMultiPolygon( polygons ) ); + default: + throw new InvalidGeometryException( "Cannot convert back to GeometryCollection" ); + } + } + + + /** + * Perform the SRID transformation + * + * @param originalCoords in the original SRID + * @param sourceCrs original reference system + * @param targetCrs target reference system with new SRID + * @return coordinates in target SRID + */ + private static ProjCoordinate[] transformCoordinates( ProjCoordinate[] originalCoords, CoordinateReferenceSystem sourceCrs, CoordinateReferenceSystem targetCrs ) { try { CoordinateTransform trans = new CoordinateTransformFactory().createTransform( sourceCrs, targetCrs ); @@ -146,10 +224,42 @@ private static ProjCoordinate[] convertCoordinates( targetCoords[i] = new ProjCoordinate(); trans.transform( originalCoords[i], targetCoords[i] ); } - return targetCoords; + return targetCoords; } catch ( Proj4jException e ) { throw new RuntimeException( "Error in coordinate transformation.", e ); } } + + /** + * Perform the SRID projection + * + * @param originalCoords in the original SRID + * @param crs spatial reference system + * @return projected coordinates on the plane + */ + private static ProjCoordinate[] projectCoordinates( + ProjCoordinate[] originalCoords, CoordinateReferenceSystem crs ) { + try { + Projection projection = crs.getProjection(); + ProjCoordinate[] targetCoords = new ProjCoordinate[originalCoords.length]; + for ( int i = 0; i < originalCoords.length; i++ ) { + targetCoords[i] = new ProjCoordinate(); + // project radians on the plane + projection.projectRadians( originalCoords[i], targetCoords[i] ); + } + return targetCoords; + } catch ( Proj4jException e ) { + throw new RuntimeException( "Error in coordinate projection.", e ); + } + } + + + @FunctionalInterface + interface CoordinatesModificationFunction { + + ProjCoordinate[] apply( ProjCoordinate[] coordinates ); + + } + } diff --git a/core/src/main/java/org/polypheny/db/type/checker/OperandTypes.java b/core/src/main/java/org/polypheny/db/type/checker/OperandTypes.java index ce54ee442f..9903b05515 100644 --- a/core/src/main/java/org/polypheny/db/type/checker/OperandTypes.java +++ b/core/src/main/java/org/polypheny/db/type/checker/OperandTypes.java @@ -288,6 +288,8 @@ public Consistency getConsistency() { public static final PolySingleOperandTypeChecker GEOMETRY = family( PolyTypeFamily.GEO ); public static final PolySingleOperandTypeChecker GEOMETRY_GEOMETRY = family( PolyTypeFamily.GEO, PolyTypeFamily.GEO ); public static final PolySingleOperandTypeChecker GEOMETRY_INTEGER = family( PolyTypeFamily.GEO, PolyTypeFamily.INTEGER ); + public static final PolySingleOperandTypeChecker GEOMETRY_GEOMETRY_STRING = family( PolyTypeFamily.GEO, PolyTypeFamily.GEO, PolyTypeFamily.STRING ); + public static final PolySingleOperandTypeChecker GEOMETRY_GEOMETRY_NUMERIC = family( PolyTypeFamily.GEO, PolyTypeFamily.GEO, PolyTypeFamily.NUMERIC ); /** * Checks that returns whether a value is a multiset or an array. Cf Java, where list and set are collections but a map is not. diff --git a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometry.java b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometry.java index 718a177b6b..50c4a41c78 100644 --- a/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometry.java +++ b/core/src/main/java/org/polypheny/db/type/entity/spatial/PolyGeometry.java @@ -52,7 +52,7 @@ public class PolyGeometry extends PolyValue { // default plane - private static final int NO_SRID = 0; + public static final int NO_SRID = 0; // World Geodetic System 1984 private static final int WGS_84 = 4326; diff --git a/core/src/main/java/org/polypheny/db/util/BuiltInMethod.java b/core/src/main/java/org/polypheny/db/util/BuiltInMethod.java index 4773c09a07..3ea5eeb3a8 100644 --- a/core/src/main/java/org/polypheny/db/util/BuiltInMethod.java +++ b/core/src/main/java/org/polypheny/db/util/BuiltInMethod.java @@ -463,6 +463,18 @@ public enum BuiltInMethod { ST_CENTROID( GeoFunctions.class, "stCentroid", PolyGeometry.class ), ST_REVERSE( GeoFunctions.class, "stReverse", PolyGeometry.class ), ST_BUFFER( GeoFunctions.class, "stBuffer", PolyGeometry.class, PolyNumber.class ), + // Spatial relationships + ST_WITHINDISTANCE( GeoFunctions.class, "stWithinDistance", PolyGeometry.class, PolyGeometry.class, PolyNumber.class ), + ST_DISJOINT( GeoFunctions.class, "stDisjoint", PolyGeometry.class, PolyGeometry.class ), + ST_TOUCHES( GeoFunctions.class, "stTouches", PolyGeometry.class, PolyGeometry.class ), + ST_INTERSECTS( GeoFunctions.class, "stIntersects", PolyGeometry.class, PolyGeometry.class ), + ST_CROSSES( GeoFunctions.class, "stCrosses", PolyGeometry.class, PolyGeometry.class ), + ST_WITHIN( GeoFunctions.class, "stWithin", PolyGeometry.class, PolyGeometry.class ), + ST_CONTAINS( GeoFunctions.class, "stContains", PolyGeometry.class, PolyGeometry.class ), + ST_OVERLAPS( GeoFunctions.class, "stOverlaps", PolyGeometry.class, PolyGeometry.class ), + ST_COVERS( GeoFunctions.class, "stCovers", PolyGeometry.class, PolyGeometry.class ), + ST_COVEREDBY( GeoFunctions.class, "stCoveredBy", PolyGeometry.class, PolyGeometry.class ), + ST_RELATE( GeoFunctions.class, "stRelate", PolyGeometry.class, PolyGeometry.class, PolyString.class ), // Yield metric values ST_DISTANCE( GeoFunctions.class, "stDistance", PolyGeometry.class, PolyGeometry.class ), // on Points diff --git a/dbms/src/test/java/org/polypheny/db/sql/fun/GeoFunctionsTest.java b/dbms/src/test/java/org/polypheny/db/sql/fun/GeoFunctionsTest.java index 68be6ff3db..99419cd134 100644 --- a/dbms/src/test/java/org/polypheny/db/sql/fun/GeoFunctionsTest.java +++ b/dbms/src/test/java/org/polypheny/db/sql/fun/GeoFunctionsTest.java @@ -166,6 +166,57 @@ public void transformFunctions() throws SQLException { } + @Test + public void spatialRelationsFunctions() throws SQLException { + try ( TestHelper.JdbcConnection polyphenyDbConnection = new TestHelper.JdbcConnection( true ) ) { + Connection connection = polyphenyDbConnection.getConnection(); + try ( Statement statement = connection.createStatement() ) { + // check that geo are withing the distance + TestHelper.checkResultSet( + statement.executeQuery( "SELECT ST_WithinDistance(ST_GeoFromText('POINT (7.852923 47.998949)', 4326), ST_GeoFromText('POINT (9.289382 48.741588)', 4326), 135)" ), + ImmutableList.of( + new Object[]{ true } + ) ); + // check that geo are disjoint + TestHelper.checkResultSet( + statement.executeQuery( "SELECT ST_Disjoint(ST_GeoFromText('POINT (7.852923 47.998949)', 4326), ST_GeoFromText('LINESTRING (9.289382 48.741588, 10.289382 47.741588, 12.289382 45.741588)', 4326))" ), + ImmutableList.of( + new Object[]{ true } + ) ); + // check that point touches the polygon + TestHelper.checkResultSet( + statement.executeQuery( "SELECT ST_Touches(ST_GeoFromText('POINT (7.852923 47.998949)', 4326), ST_GeoFromText('POLYGON ((9.289382 48.741588, 10.289382 47.741588, 9.289382 47.741588, 9.289382 48.741588))', 4326))" ), + ImmutableList.of( + new Object[]{ false } + ) ); + // check that line intersects the polygon + TestHelper.checkResultSet( + statement.executeQuery( "SELECT ST_Intersects(ST_GeoFromText('LINESTRING (9.289382 48.741588, 10.289382 47.741588, 12.289382 45.741588)', 4326), ST_GeoFromText('POLYGON ((9.289382 48.741588, 10.289382 47.741588, 9.289382 47.741588, 9.289382 48.741588))', 4326))" ), + ImmutableList.of( + new Object[]{ true } + ) ); + // check that line crosses the polygon + TestHelper.checkResultSet( + statement.executeQuery( "SELECT ST_Crosses(ST_GeoFromText('LINESTRING (9.289382 48.741588, 10.289382 47.741588, 12.289382 45.741588)', 4326), ST_GeoFromText('POLYGON ((9.289382 48.741588, 10.289382 47.741588, 9.289382 47.741588, 9.289382 48.741588))', 4326))" ), + ImmutableList.of( + new Object[]{ false } + ) ); + // check that point is within the polygon + TestHelper.checkResultSet( + statement.executeQuery( "SELECT ST_Within(ST_GeoFromText('POINT (9.3 48)', 4326), ST_GeoFromText('POLYGON ((9.289382 48.741588, 10.289382 47.741588, 9.289382 47.741588, 9.289382 48.741588))', 4326))" ), + ImmutableList.of( + new Object[]{ true } + ) ); + // check that point is relate with polygon + TestHelper.checkResultSet( + statement.executeQuery( "SELECT ST_Relate(ST_GeoFromText('POINT (9.3 48)', 4326), ST_GeoFromText('POLYGON ((9.289382 48.741588, 10.289382 47.741588, 9.289382 47.741588, 9.289382 48.741588))', 4326), 'T********')" ), + ImmutableList.of( + new Object[]{ true } + ) ); + } + } + } + @Test public void distanceFunctions() throws SQLException { try ( TestHelper.JdbcConnection polyphenyDbConnection = new TestHelper.JdbcConnection( true ) ) { diff --git a/plugins/sql-language/src/main/java/org/polypheny/db/sql/SqlLanguagePlugin.java b/plugins/sql-language/src/main/java/org/polypheny/db/sql/SqlLanguagePlugin.java index 09c764744f..492cf984ab 100644 --- a/plugins/sql-language/src/main/java/org/polypheny/db/sql/SqlLanguagePlugin.java +++ b/plugins/sql-language/src/main/java/org/polypheny/db/sql/SqlLanguagePlugin.java @@ -2511,6 +2511,7 @@ public void unparse( SqlWriter writer, SqlCall call, int leftPrec, int rightPrec FunctionCategory.GEOMETRY ) ); // Common properties + register( OperatorName.ST_ISSIMPLE, new SqlFunction( @@ -2633,7 +2634,120 @@ public void unparse( SqlWriter writer, SqlCall call, int leftPrec, int rightPrec register( OperatorName.ST_BUFFER, new SqlStBuffer() ); + // Spatial relationships + + register( + OperatorName.ST_WITHINDISTANCE, + new SqlFunction( + "ST_WITHINDISTANCE", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY_NUMERIC, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_DISJOINT, + new SqlFunction( + "ST_DISJOINT", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_TOUCHES, + new SqlFunction( + "ST_TOUCHES", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_INTERSECTS, + new SqlFunction( + "ST_INTERSECTS", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_CROSSES, + new SqlFunction( + "ST_CROSSES", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_WITHIN, + new SqlFunction( + "ST_WITHIN", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_CONTAINS, + new SqlFunction( + "ST_CONTAINS", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_OVERLAPS, + new SqlFunction( + "ST_OVERLAPS", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_COVERS, + new SqlFunction( + "ST_COVERS", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_COVEREDBY, + new SqlFunction( + "ST_COVEREDBY", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY, + FunctionCategory.GEOMETRY ) ); + + register( + OperatorName.ST_RELATE, + new SqlFunction( + "ST_RELATE", + Kind.GEO, + ReturnTypes.BOOLEAN, + InferTypes.GEOMETRY, + OperandTypes.GEOMETRY_GEOMETRY_STRING, + FunctionCategory.GEOMETRY ) ); + // Yield metric values + register( OperatorName.ST_DISTANCE, new SqlFunction(