org.niord.core.db.MySQLSpatialDialect.java Source code

Java tutorial

Introduction

Here is the source code for org.niord.core.db.MySQLSpatialDialect.java

Source

/*
 * Copyright 2016 Danish Maritime Authority.
 *
 * Licensed 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.niord.core.db;

/**
 * Bizarrely, using the proper MySQL56SpatialDialect class (or MySQL56InnoDBSpatialDialect) will cause an error.
 * And, alas, MySQLSpatialDialect will not give very precise results, since is only matches the
 * Minimum Bounding Rectangle (MBR), not the actual shape.
 * <p/>
 * Hence, the class below is based on MySQL's MySQLSpatialDialect, but use the "ST_" versions of the
 * spatial functions.
 */

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.function.StandardSQLFunction;

import org.hibernate.service.ServiceRegistry;
import org.hibernate.spatial.GeolatteGeometryType;
import org.hibernate.spatial.JTSGeometryType;
import org.hibernate.spatial.SpatialDialect;
import org.hibernate.spatial.SpatialFunction;
import org.hibernate.spatial.SpatialRelation;
import org.hibernate.spatial.dialect.mysql.MySQLGeometryTypeDescriptor;
import org.hibernate.type.StandardBasicTypes;

/**
 * A Dialect for MySQL with support for its spatial features
 *
 * @author Karel Maesen, Boni Gopalan
 */
public class MySQLSpatialDialect extends MySQLDialect implements SpatialDialect {

    /**
     * Constructs an instance
     */
    public MySQLSpatialDialect() {
        super();
        registerColumnType(MySQLGeometryTypeDescriptor.INSTANCE.getSqlType(), "GEOMETRY");
        final MySQLSpatialFunctions functionsToRegister = overrideObjectShapeFunctions(new MySQLSpatialFunctions());
        for (Map.Entry<String, StandardSQLFunction> entry : functionsToRegister) {
            registerFunction(entry.getKey(), entry.getValue());
        }
    }

    private MySQLSpatialFunctions overrideObjectShapeFunctions(MySQLSpatialFunctions mysqlFunctions) {
        mysqlFunctions.put("contains", new StandardSQLFunction("ST_Contains", StandardBasicTypes.BOOLEAN));
        mysqlFunctions.put("crosses", new StandardSQLFunction("ST_Crosses", StandardBasicTypes.BOOLEAN));
        mysqlFunctions.put("disjoint", new StandardSQLFunction("ST_Disjoint", StandardBasicTypes.BOOLEAN));
        mysqlFunctions.put("equals", new StandardSQLFunction("ST_Equals", StandardBasicTypes.BOOLEAN));
        mysqlFunctions.put("intersects", new StandardSQLFunction("ST_Intersects", StandardBasicTypes.BOOLEAN));
        mysqlFunctions.put("overlaps", new StandardSQLFunction("ST_Overlaps", StandardBasicTypes.BOOLEAN));
        mysqlFunctions.put("touches", new StandardSQLFunction("ST_Touches", StandardBasicTypes.BOOLEAN));
        mysqlFunctions.put("within", new StandardSQLFunction("ST_Within", StandardBasicTypes.BOOLEAN));
        return mysqlFunctions;
    }

    @Override
    public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
        super.contributeTypes(typeContributions, serviceRegistry);
        typeContributions.contributeType(new GeolatteGeometryType(MySQLGeometryTypeDescriptor.INSTANCE));
        typeContributions.contributeType(new JTSGeometryType(MySQLGeometryTypeDescriptor.INSTANCE));
    }

    @Override
    public String getSpatialRelateSQL(String columnName, int spatialRelation) {
        switch (spatialRelation) {
        case SpatialRelation.WITHIN:
            return " ST_Within(" + columnName + ",?)";
        case SpatialRelation.CONTAINS:
            return " ST_Contains(" + columnName + ", ?)";
        case SpatialRelation.CROSSES:
            return " ST_Crosses(" + columnName + ", ?)";
        case SpatialRelation.OVERLAPS:
            return " ST_Overlaps(" + columnName + ", ?)";
        case SpatialRelation.DISJOINT:
            return " ST_Disjoint(" + columnName + ", ?)";
        case SpatialRelation.INTERSECTS:
            return " ST_Intersects(" + columnName + ", ?)";
        case SpatialRelation.TOUCHES:
            return " ST_Touches(" + columnName + ", ?)";
        case SpatialRelation.EQUALS:
            return " ST_Equals(" + columnName + ", ?)";
        default:
            throw new IllegalArgumentException("Spatial relation is not known by this dialect");
        }
    }

    @Override
    public String getSpatialFilterExpression(String columnName) {
        return "MBRIntersects(" + columnName + ", ? ) ";
    }

    @Override
    public String getSpatialAggregateSQL(String columnName, int aggregation) {
        throw new UnsupportedOperationException("Mysql has no spatial aggregate SQL functions.");
    }

    @Override
    public String getDWithinSQL(String columnName) {
        throw new UnsupportedOperationException("Mysql doesn't support the within function");
    }

    @Override
    public String getHavingSridSQL(String columnName) {
        return " (srid(" + columnName + ") = ?) ";
    }

    @Override
    public String getIsEmptySQL(String columnName, boolean isEmpty) {
        final String emptyExpr = " IsEmpty(" + columnName + ") ";
        return isEmpty ? emptyExpr : "( NOT " + emptyExpr + ")";
    }

    @Override
    public boolean supportsFiltering() {
        return false;
    }

    @Override
    public boolean supports(SpatialFunction function) {
        switch (function) {
        case boundary:
        case relate:
        case distance:
        case buffer:
        case convexhull:
        case difference:
        case symdifference:
        case intersection:
        case geomunion:
        case dwithin:
        case transform:
            return false;
        default:
            return true;
        }
    }

}

/**
 * An {@code Iterable} over the spatial functions supported by MySQL.
 *
 * @author Karel Maesen, Geovise BVBA
 *
 */
class MySQLSpatialFunctions implements Iterable<Map.Entry<String, StandardSQLFunction>> {

    private final Map<String, StandardSQLFunction> functionsToRegister = new HashMap<>();

    MySQLSpatialFunctions() {
        functionsToRegister.put("dimension", new StandardSQLFunction("dimension", StandardBasicTypes.INTEGER));
        functionsToRegister.put("geometrytype", new StandardSQLFunction("geometrytype", StandardBasicTypes.STRING));
        functionsToRegister.put("srid", new StandardSQLFunction("srid", StandardBasicTypes.INTEGER));
        functionsToRegister.put("envelope", new StandardSQLFunction("envelope"));
        functionsToRegister.put("astext", new StandardSQLFunction("astext", StandardBasicTypes.STRING));
        functionsToRegister.put("asbinary", new StandardSQLFunction("asbinary", StandardBasicTypes.BINARY));
        functionsToRegister.put("isempty", new StandardSQLFunction("isempty", StandardBasicTypes.BOOLEAN));
        functionsToRegister.put("issimple", new StandardSQLFunction("issimple", StandardBasicTypes.BOOLEAN));
        // Register functions for spatial relation constructs
        functionsToRegister.put("overlaps", new StandardSQLFunction("overlaps", StandardBasicTypes.BOOLEAN));
        functionsToRegister.put("intersects", new StandardSQLFunction("intersects", StandardBasicTypes.BOOLEAN));
        functionsToRegister.put("equals", new StandardSQLFunction("equals", StandardBasicTypes.BOOLEAN));
        functionsToRegister.put("contains", new StandardSQLFunction("contains", StandardBasicTypes.BOOLEAN));
        functionsToRegister.put("crosses", new StandardSQLFunction("crosses", StandardBasicTypes.BOOLEAN));
        functionsToRegister.put("disjoint", new StandardSQLFunction("disjoint", StandardBasicTypes.BOOLEAN));
        functionsToRegister.put("touches", new StandardSQLFunction("touches", StandardBasicTypes.BOOLEAN));
        functionsToRegister.put("within", new StandardSQLFunction("within", StandardBasicTypes.BOOLEAN));
    }

    public void put(String name, StandardSQLFunction function) {
        this.functionsToRegister.put(name, function);
    }

    @Override
    public Iterator<Map.Entry<String, StandardSQLFunction>> iterator() {
        return functionsToRegister.entrySet().iterator();
    }
}