Search code examples
javahibernatespatialhibernate-5.xhibernate-spatial

HibernateException: Positions are collinear in 2D


I'm using Hibernate Spatial verion 5.0.7.Final as ORM. Sometimes when I perform a query using a Geometry as named parameter I get the following exception :

org.hibernate.HibernateException: Positions are collinear in 2D

I understand that sometimes my geometries are collinear and NumericalMethods module in the Geolatte library is checking whether my geometry isCounterClockwise, it would raise this exception.

I wonder why it is doing this, but more than that, what I could do to avoid this error.

The Hibernate code underneath in NumericalMethods.java is only checking the first three coordinates. In my case, sometimes those three first coordinates are collinear but the fourth one will make it a valid polygon. I can't think why it wouldn't iterate through the rest of the coordinates to tell whether it isCounterClockwise or not.

Full stacktrace :

org.hibernate.HibernateException: Positions are collinear in 2D
at org.hibernate.spatial.dialect.oracle.SDOGeometryValueBinder.toNative(SDOGeometryValueBinder.java:71)
at org.hibernate.spatial.dialect.oracle.SDOGeometryValueBinder.bind(SDOGeometryValueBinder.java:52)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:257)
at org.hibernate.type.AbstractStandardBasicType.nullSafeSet(AbstractStandardBasicType.java:252)
at org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:52)
at org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:627)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1944)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1897)
at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1875)
at org.hibernate.loader.Loader.doQuery(Loader.java:919)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:336)
at org.hibernate.loader.Loader.doList(Loader.java:2611)
at org.hibernate.loader.Loader.doList(Loader.java:2594)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2423)
at org.hibernate.loader.Loader.list(Loader.java:2418)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:501)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:371)
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:216)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1326)
at org.hibernate.internal.QueryImpl.list(QueryImpl.java:87)

Solution

  • As commented, it looks like it is a bug in Hibernate. When trying to create a polygon with co-linear latitudes hibernate spatial is throwing an exception. It was registered as a Jira ticket :

    https://hibernate.atlassian.net/browse/HHH-10410

    It looks like the culprit is the isCounterClockwise function in NumericalMethods.java , Geolatte library. Probably that's happening since the first version of Hibernate 5.

    In case somebody finds it useful, I'll paste below the code to create your own customized version of this library that will prevent this error from happening. I'll also submit a pull request in case they consider merging it (or something similar) to their master :

    https://github.com/GeoLatte/geolatte-geom/pull/43

    /**
     * Determines whether the specified {@code PositionSequence} is counter-clockwise.
     * <p/>
     * <p>Only the first three positions, are inspected to determine whether the sequence is counter-clockwise.
     * In case are less than three positions in the sequence, the method returns true.</p>
     *
     * @param positions a {@code PositionSequence}
     * @return true if the positions in the specified sequence are counter-clockwise, or if the sequence contains
     * less than three elements.
     */
    public static boolean isCounterClockwise(PositionSequence<?> positions) {
        if (positions.size() < 3) return true;
        Position p0 = positions.getPositionN(0);
        Position p1 = positions.getPositionN(1);
        double det = 0;
        int positionsSize = positions.size();
        int i = 2;
        while(i < positionsSize && det == 0) {
            Position p2 = positions.getPositionN(i);
            det = deltaDeterminant(p0, p1, p2);
            i++;
        }
        if (det == 0) {
            throw new IllegalArgumentException("Positions are collinear in 2D");
        }
        return det > 0;
    }
    

    Update : the pull request was merged into master, so it will be released eventually.