From 6658c6235fbcaf66fc4e40b3ddb590b5d4cad5be Mon Sep 17 00:00:00 2001 From: Karel Maesen Date: Fri, 18 Nov 2022 00:17:27 +0100 Subject: [PATCH] HHH-15669 Fix test failures when using Oracle 21 Rings in Oracle polygons may be shifted depending on how it is processed. The equality test now takes this into account. Add test to investigate st_within test failure. --- .../oracle/hhh15669/TestStWithinBug.java | 107 +++++++++++++ .../integration/SpatialTestDataProvider.java | 5 + .../functions/CommonFunctionTests.java | 4 +- .../functions/FunctionTestTemplate.java | 12 +- .../functions/RowObjectMapper.java | 40 ++++- .../integration/functions/TestTemplates.java | 12 +- .../spatial/testing/GeometryEquality.java | 15 -- .../spatial/testing/JTSGeometryEquality.java | 106 ------------- .../spatial/testing/SpatialTestBase.java | 15 +- .../testing/datareader/TestSupport.java | 7 + .../dialects/mysql/MySQLGeometryEquality.java | 47 ------ .../dialects/oracle/OracleSDOTestSupport.java | 148 +++++++++++++++++- .../test-sdo-geometry-data-set-2D.xml | 7 + 13 files changed, 325 insertions(+), 200 deletions(-) create mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/oracle/hhh15669/TestStWithinBug.java delete mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/testing/GeometryEquality.java delete mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/testing/JTSGeometryEquality.java delete mode 100644 hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/mysql/MySQLGeometryEquality.java diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/oracle/hhh15669/TestStWithinBug.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/oracle/hhh15669/TestStWithinBug.java new file mode 100644 index 0000000000..37533c50ce --- /dev/null +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/dialect/oracle/hhh15669/TestStWithinBug.java @@ -0,0 +1,107 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ + +package org.hibernate.spatial.dialect.oracle.hhh15669; + + +import java.util.List; + +import org.hibernate.dialect.OracleDialect; +import org.hibernate.spatial.HibernateSpatialConfigurationSettings; +import org.hibernate.spatial.testing.SpatialSessionFactoryAware; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.RequiresDialect; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.geolatte.geom.G2D; +import org.geolatte.geom.Point; +import org.geolatte.geom.Polygon; + +import static org.geolatte.geom.builder.DSL.g; +import static org.geolatte.geom.builder.DSL.point; +import static org.geolatte.geom.builder.DSL.polygon; +import static org.geolatte.geom.builder.DSL.ring; +import static org.geolatte.geom.crs.CoordinateReferenceSystems.WGS84; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize; + +@TestForIssue(jiraKey = "HHH-15669") +@DomainModel(annotatedClasses = { Foo.class }) +@RequiresDialect(value = OracleDialect.class) +@SessionFactory +@ServiceRegistry(settings = { + @Setting(name = HibernateSpatialConfigurationSettings.ORACLE_OGC_STRICT, value = "true") +}) +public class TestStWithinBug extends SpatialSessionFactoryAware { + Polygon filter = polygon( WGS84, ring( + g( 0, 0 ), + g( 10, 0 ), + g( 10, 10 ), + g( 0, 10 ), + g( 0, 0 ) + ) ); + + @BeforeEach + public void before() { + scope.inTransaction( + session -> { + session.persist( new Foo( 1, point( WGS84, g( 5, 5 ) ) ) ); + session.persist( new Foo( 2, point( WGS84, g( 12, 12 ) ) ) ); + session.persist( new Foo( 3, point( WGS84, g( -1, -1 ) ) ) ); + } + ); + } + + + @Test + public void testHql() { + scope.inTransaction( session -> { + + List list = session + .createQuery( "from Foo where st_within(point, :filter) = true", Foo.class ) + .setParameter( "filter", filter ) + .getResultList(); + assertThat( list, hasSize( 1 ) ); + assertThat( list.get( 0 ).id, equalTo( 1L ) ); + } ); + } + + + @AfterEach + public void cleanup() { + scope.inTransaction( session -> session.createMutationQuery( "delete from Foo" ) + .executeUpdate() ); + } + +} + +@Entity(name = "Foo") +@Table(name = "Foo") +class Foo { + @Id + long id; + Point point; + + public Foo() { + } + + public Foo(long id, Point point) { + this.id = id; + this.point = point; + } +} diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/SpatialTestDataProvider.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/SpatialTestDataProvider.java index 28655031d7..af2dd4cbcd 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/SpatialTestDataProvider.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/SpatialTestDataProvider.java @@ -11,6 +11,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import org.geolatte.geom.GeometryEquality; + import org.hibernate.spatial.CommonSpatialFunction; import org.hibernate.spatial.GeomCodec; import org.hibernate.spatial.testing.TestSupportFactories; @@ -38,6 +40,8 @@ public class SpatialTestDataProvider { private final TestData funcTestData; protected TestData testData; protected GeomCodec codec; + + protected GeometryEquality geometryEquality; protected List exludeFromTest; public SpatialTestDataProvider() { @@ -51,6 +55,7 @@ public class SpatialTestDataProvider { exludeFromTest = support.getExcludeFromTests(); funcTestData = support.createTestData( SpatialFunctionsData ); filterGeometry = support.getFilterGeometry(); + geometryEquality = support.getGeometryEquality(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException( e ); diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java index ea90040a9f..1fad9edacc 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/CommonFunctionTests.java @@ -69,7 +69,7 @@ public class CommonFunctionTests extends SpatialTestBase { public Stream testFunction() { return - TestTemplates.all( templates, hqlOverrides, filterGeometry ) + TestTemplates.all( templates, hqlOverrides, geometryEquality, filterGeometry ) .filter( f -> isSupported( f.function ) ) .filter( f -> !exludeFromTest.contains( f.function ) ) .flatMap( t -> Stream.of( @@ -91,7 +91,7 @@ public class CommonFunctionTests extends SpatialTestBase { ) ); } - protected String displayName(FunctionTestTemplate template, String fnName) { + protected String displayName(FunctionTestTemplate template, String fnName) { return String.format( Locale.ROOT, "Test for function %s on entity %s", diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/FunctionTestTemplate.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/FunctionTestTemplate.java index b291c96d9d..82c55b78eb 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/FunctionTestTemplate.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/FunctionTestTemplate.java @@ -33,6 +33,7 @@ import org.hibernate.type.StandardBasicTypes; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.geolatte.geom.Geometry; +import org.geolatte.geom.GeometryEquality; import org.geolatte.geom.codec.Wkt; /** @@ -115,7 +116,7 @@ public class FunctionTestTemplate { } results.set( query.getResultList() ); } ); - return (List) results.get().stream().map( rowObjectMapper::apply ).collect( Collectors.toList() ); + return results.get().stream().map( rowObjectMapper::apply ).collect( Collectors.toList() ); } //only for JtsGeometry because extra mapping of native Geometry object (where needed) @@ -145,6 +146,8 @@ public class FunctionTestTemplate { RowObjectMapper mapper; Geometry testGeometry; + GeometryEquality geomEq; + public Builder(CommonSpatialFunction function) { this.function = function; } @@ -163,7 +166,7 @@ public class FunctionTestTemplate { } } if ( this.mapper == null ) { - this.mapper = new RowObjectMapper() { + this.mapper = new RowObjectMapper( geomEq ) { }; } return new FunctionTestTemplate( function, hql, sql, mapper, model, testGeometry, codec ); @@ -187,5 +190,10 @@ public class FunctionTestTemplate { } return this; } + + public Builder equalityTest(GeometryEquality geomEq) { + this.geomEq = geomEq; + return this; + } } } diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/RowObjectMapper.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/RowObjectMapper.java index e50274cdcb..5768718527 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/RowObjectMapper.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/RowObjectMapper.java @@ -9,25 +9,35 @@ package org.hibernate.spatial.integration.functions; import java.util.Arrays; import java.util.Objects; +import org.geolatte.geom.GeometryEquality; +import org.geolatte.geom.jts.JTS; + /** * Mapper to ensure that the results of the test queries can be compared for equality. - * - * @param the returned object by the test query */ -public interface RowObjectMapper { - default Data apply(Object obj) { +public class RowObjectMapper { + + final GeometryEquality geomEq; + + RowObjectMapper(GeometryEquality geomEq) { + this.geomEq = geomEq; + } + + Data apply(Object obj) { Object[] row = (Object[]) obj; - return new Data( (Number) row[0], row[1] ); + return new Data( (Number) row[0], row[1], geomEq ); } } class Data { final Number id; Object datum; + final GeometryEquality geomEq; - Data(Number id, Object datum) { + Data(Number id, Object datum, GeometryEquality geomEq) { this.id = id; this.datum = datum; + this.geomEq = geomEq; } @Override @@ -42,6 +52,7 @@ class Data { return Objects.equals( id.intValue(), data.id.intValue() ) && isEquals( datum, data.datum ); } + @SuppressWarnings("unchecked") private boolean isEquals(Object thisDatum, Object thatDatum) { if ( thisDatum instanceof byte[] ) { if ( !( thatDatum instanceof byte[] ) ) { @@ -49,11 +60,28 @@ class Data { } return Arrays.equals( (byte[]) thisDatum, (byte[]) thatDatum ); } + if ( thisDatum instanceof org.geolatte.geom.Geometry ) { + return this.geomEq.equals( asGeolatte( thisDatum ), asGeolatte( thatDatum ) ); + } + if ( thisDatum instanceof org.locationtech.jts.geom.Geometry ) { + return this.geomEq.equals( fromJts( thisDatum ), fromJts( thatDatum ) ); + } return Objects.equals( thisDatum, thatDatum ); } + @SuppressWarnings("rawtypes") + private org.geolatte.geom.Geometry asGeolatte(Object obj) { + return (org.geolatte.geom.Geometry) obj; + } + + @SuppressWarnings("rawtypes") + private org.geolatte.geom.Geometry fromJts(Object obj) { + return JTS.from( (org.locationtech.jts.geom.Geometry) obj ); + } + + @Override public int hashCode() { return Objects.hash( id, datum ); diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/TestTemplates.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/TestTemplates.java index de3e5057be..04ad56777f 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/TestTemplates.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/integration/functions/TestTemplates.java @@ -20,14 +20,8 @@ import java.util.stream.Stream; import org.hibernate.spatial.CommonSpatialFunction; import org.hibernate.spatial.testing.dialects.NativeSQLTemplates; -import org.geolatte.geom.G2D; import org.geolatte.geom.Geometry; -import org.geolatte.geom.Polygon; - -import static org.geolatte.geom.builder.DSL.g; -import static org.geolatte.geom.builder.DSL.polygon; -import static org.geolatte.geom.builder.DSL.ring; -import static org.geolatte.geom.crs.CoordinateReferenceSystems.WGS84; +import org.geolatte.geom.GeometryEquality; /** * Makes available all the builders for FunctionTestTemplate @@ -42,7 +36,8 @@ public abstract class TestTemplates { public static Stream all( NativeSQLTemplates sqlTemplates, Map hqlOverrides, - Geometryfilter) { + GeometryEquality geomEq, + Geometry filter) { Map templates = sqlTemplates.all(); return templates @@ -51,6 +46,7 @@ public abstract class TestTemplates { .map( function -> builder( function ) .hql( hqlOverrides.get( function ) ) .sql( templates.get( function ) ) + .equalityTest( geomEq ) .geometry( setFilter( function ) ? filter : null ) ); } diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/GeometryEquality.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/GeometryEquality.java deleted file mode 100644 index e32cbf19c1..0000000000 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/GeometryEquality.java +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later. - * See the lgpl.txt file in the root directory or . - */ - -package org.hibernate.spatial.testing; - -/** - * Created by Karel Maesen, Geovise BVBA on 15/02/2018. - */ -public interface GeometryEquality { - boolean test(G geom1, G geom2); -} diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/JTSGeometryEquality.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/JTSGeometryEquality.java deleted file mode 100644 index 082a222039..0000000000 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/JTSGeometryEquality.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later. - * See the lgpl.txt file in the root directory or . - */ - -package org.hibernate.spatial.testing; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; -import org.locationtech.jts.geom.GeometryCollection; - -/** - * This class tests for the equality between geometries. - *

- * The notion of geometric equality can differ slightly between - * spatial databases. - */ -public class JTSGeometryEquality implements GeometryEquality { - - - @Override - public boolean test(Geometry geom1, Geometry geom2) { - if ( geom1 == null ) { - return geom2 == null; - } - if ( geom1.isEmpty() ) { - return geom2.isEmpty(); - } - if ( !equalSRID( geom1, geom2 ) ) { - return false; - } - - if ( geom1 instanceof GeometryCollection ) { - if ( !( geom2 instanceof GeometryCollection ) ) { - return false; - } - GeometryCollection expectedCollection = (GeometryCollection) geom1; - GeometryCollection receivedCollection = (GeometryCollection) geom2; - for ( int partIndex = 0; partIndex < expectedCollection.getNumGeometries(); partIndex++ ) { - Geometry partExpected = expectedCollection.getGeometryN( partIndex ); - Geometry partReceived = receivedCollection.getGeometryN( partIndex ); - if ( !test( partExpected, partReceived ) ) { - return false; - } - } - return true; - } - else { - return testSimpleGeometryEquality( geom1, geom2 ); - } - } - - /** - * Two geometries are equal iff both have the same SRID, or both are unknown (i.e. a SRID of 0 or -1). - * - * @param geom1 - * @param geom2 - * - * @return - */ - private boolean equalSRID(Geometry geom1, Geometry geom2) { - return geom1.getSRID() == geom2.getSRID() || - ( geom1.getSRID() < 1 && geom2.getSRID() < 1 ); - } - - /** - * Test whether two geometries, not of type GeometryCollection are equal. - * - * @param geom1 - * @param geom2 - * - * @return - */ - protected boolean testSimpleGeometryEquality(Geometry geom1, Geometry geom2) { - //return geom1.equals(geom2); - return testTypeAndVertexEquality( geom1, geom2 ); - } - - protected boolean testTypeAndVertexEquality(Geometry geom1, Geometry geom2) { - if ( !geom1.getGeometryType().equals( geom2.getGeometryType() ) ) { - return false; - } - if ( geom1.getNumGeometries() != geom2.getNumGeometries() ) { - return false; - } - if ( geom1.getNumPoints() != geom2.getNumPoints() ) { - return false; - } - Coordinate[] coordinates1 = geom1.getCoordinates(); - Coordinate[] coordinates2 = geom2.getCoordinates(); - for ( int i = 0; i < coordinates1.length; i++ ) { - Coordinate c1 = coordinates1[i]; - Coordinate c2 = coordinates2[i]; - if ( !testCoordinateEquality( c1, c2 ) ) { - return false; - } - } - return true; - } - - private boolean testCoordinateEquality(Coordinate c1, Coordinate c2) { - return ( Double.isNaN( c1.z ) || !( c1.z != c2.z ) ) && c1.x == c2.x && c1.y == c2.y; - } -} diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/SpatialTestBase.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/SpatialTestBase.java index c63dcba5d1..53f0658752 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/SpatialTestBase.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/SpatialTestBase.java @@ -7,21 +7,18 @@ package org.hibernate.spatial.testing; -import org.hibernate.dialect.H2Dialect; import org.hibernate.spatial.testing.datareader.TestSupport; import org.hibernate.spatial.testing.domain.GeomEntity; import org.hibernate.spatial.testing.domain.JtsGeomEntity; import org.hibernate.spatial.testing.domain.SpatialDomainModel; -import org.hibernate.testing.orm.junit.DialectContext; import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @DomainModel(modelDescriptorClasses = SpatialDomainModel.class) abstract public class SpatialTestBase - extends SpatialSessionFactoryAware { + extends SpatialSessionFactoryAware { public abstract TestSupport.TestDataPurpose purpose(); @@ -31,17 +28,19 @@ abstract public class SpatialTestBase JtsGeomEntity.class, purpose() ) - .forEach( session::save ) ); + .forEach( session::persist ) ); scope.inTransaction( session -> super.entities( GeomEntity.class, purpose() - ).forEach( session::save ) ); + ).forEach( session::persist ) ); } @AfterEach public void cleanup() { - scope.inTransaction( session -> session.createQuery( "delete from GeomEntity" ).executeUpdate() ); - scope.inTransaction( session -> session.createQuery( "delete from JtsGeomEntity" ).executeUpdate() ); + scope.inTransaction( session -> session.createMutationQuery( "delete from GeomEntity" ) + .executeUpdate() ); + scope.inTransaction( session -> session.createMutationQuery( "delete from JtsGeomEntity" ) + .executeUpdate() ); } diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/datareader/TestSupport.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/datareader/TestSupport.java index eb66463ba0..26e8057e9c 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/datareader/TestSupport.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/datareader/TestSupport.java @@ -19,6 +19,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.geolatte.geom.GeometryEquality; +import org.geolatte.geom.GeometryPositionEquality; + import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.spatial.CommonSpatialFunction; import org.hibernate.spatial.GeomCodec; @@ -57,6 +60,10 @@ public abstract class TestSupport { return new ArrayList<>(); } + public GeometryEquality getGeometryEquality() { + return new GeometryPositionEquality(); + } + public enum TestDataPurpose { SpatialFunctionsData, StoreRetrieveData diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/mysql/MySQLGeometryEquality.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/mysql/MySQLGeometryEquality.java deleted file mode 100644 index 00bf6728b6..0000000000 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/mysql/MySQLGeometryEquality.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * License: GNU Lesser General Public License (LGPL), version 2.1 or later. - * See the lgpl.txt file in the root directory or . - */ - -package org.hibernate.spatial.testing.dialects.mysql; - -import org.hibernate.spatial.testing.JTSGeometryEquality; - -import org.locationtech.jts.geom.Coordinate; -import org.locationtech.jts.geom.Geometry; - -/** - * Extends the test for geometry equality, because - * MySQL stores empty geometries as NULL objects. - */ -public class MySQLGeometryEquality extends JTSGeometryEquality { - - @Override - public boolean test(Geometry geom1, Geometry geom2) { - if ( geom1 != null && geom1.isEmpty() ) { - return geom2 == null || geom2.isEmpty(); - } - return super.test( geom1, geom2 ); - } - - @Override - protected boolean testSimpleGeometryEquality(Geometry geom1, Geometry geom2) { - return testVerticesEquality( geom1, geom2 ); - } - - private boolean testVerticesEquality(Geometry geom1, Geometry geom2) { - if ( geom1.getNumPoints() != geom2.getNumPoints() ) { - return false; - } - for ( int i = 0; i < geom1.getNumPoints(); i++ ) { - Coordinate cn1 = geom1.getCoordinates()[i]; - Coordinate cn2 = geom2.getCoordinates()[i]; - if ( !cn1.equals2D( cn2 ) ) { - return false; - } - } - return true; - } -} diff --git a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/oracle/OracleSDOTestSupport.java b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/oracle/OracleSDOTestSupport.java index b4862f6497..940ed13bef 100644 --- a/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/oracle/OracleSDOTestSupport.java +++ b/hibernate-spatial/src/test/java/org/hibernate/spatial/testing/dialects/oracle/OracleSDOTestSupport.java @@ -16,10 +16,25 @@ import org.hibernate.spatial.testing.datareader.TestSupport; import org.hibernate.spatial.testing.dialects.NativeSQLTemplates; import org.hibernate.spatial.testing.dialects.PredicateRegexes; +import org.geolatte.geom.AbstractGeometryCollection; +import org.geolatte.geom.ExactPositionEquality; import org.geolatte.geom.Geometry; +import org.geolatte.geom.GeometryEquality; +import org.geolatte.geom.LinearRing; +import org.geolatte.geom.Polygon; +import org.geolatte.geom.Position; +import org.geolatte.geom.PositionSequence; +import org.geolatte.geom.PositionSequenceBuilders; +import org.geolatte.geom.PositionSequenceEquality; +import org.geolatte.geom.PositionSequencePositionEquality; import org.geolatte.geom.codec.db.oracle.Decoders; import org.geolatte.geom.codec.db.oracle.SDOGeometry; +import static org.geolatte.geom.builder.DSL.g; +import static org.geolatte.geom.builder.DSL.polygon; +import static org.geolatte.geom.builder.DSL.ring; +import static org.geolatte.geom.crs.CoordinateReferenceSystems.WGS84; + /** * @author Karel Maesen, Geovise BVBA * creation-date: Oct 22, 2010 @@ -42,13 +57,134 @@ public class OracleSDOTestSupport extends TestSupport { } public GeomCodec codec() { - return new GeomCodec() { - @Override - public Geometry toGeometry(Object in) { - SDOGeometry geom = SDOGeometry.load( (Struct) in ); - return Decoders.decode( geom ); - } + return in -> { + SDOGeometry geom = SDOGeometry.load( (Struct) in ); + return Decoders.decode( geom ); }; } + + public Geometry getFilterGeometry() { + //ensure the filter geometry has the correct orientation (Counter-clockwise for exterior ring) + // if not this creates problems for the SQL/MM function, esp. ST_GEOMERY.ST_WITHIN(). + return polygon( + WGS84, + ring( g( 0, 0 ), g( 10, 0 ), g( 10, 10 ), g( 0, 10 ), g( 0, 0 ) ) + ); + } + + @Override + public GeometryEquality getGeometryEquality() { + return new OraGeometryEquality(); + } + + @SuppressWarnings("rawtypes") + static class OraGeometryEquality implements GeometryEquality { + + private final ExactPositionEquality pointEquality = new ExactPositionEquality(); + private final PositionSequenceEquality pointSeqEq = new PositionSequencePositionEquality( pointEquality ); + + /** + * {@inheritDoc} + */ + @Override + public

boolean equals(Geometry

first, Geometry

second) { + if ( first == second ) { + return true; + } + if ( first == null || second == null ) { + return false; + } + if ( first.isEmpty() && second.isEmpty() ) { + return true; + } + if ( first.isEmpty() || second.isEmpty() ) { + return false; + } + if ( !first.getCoordinateReferenceSystem().equals( second.getCoordinateReferenceSystem() ) ) { + return false; + } + if ( first.getGeometryType() != second.getGeometryType() ) { + return false; + } + if ( first instanceof AbstractGeometryCollection ) { + assert ( second instanceof AbstractGeometryCollection ); + return equals( (AbstractGeometryCollection) first, (AbstractGeometryCollection) second ); + } + if ( first instanceof Polygon ) { + assert ( second instanceof Polygon ); + return equals( (Polygon

) first, (Polygon

) second ); + } + return pointSeqEq.equals( first.getPositions(), second.getPositions() ); + } + + private

boolean equals(Polygon

first, Polygon

second) { + if ( first.getNumInteriorRing() != second.getNumInteriorRing() ) { + return false; + } + if ( notEqualRings( first.getExteriorRing(), second.getExteriorRing() ) ) { + return false; + } + for ( int i = 0; i < first.getNumInteriorRing(); i++ ) { + if ( notEqualRings( first.getInteriorRingN( i ), second.getInteriorRingN( i ) ) ) { + return false; + } + } + return true; + } + + private

boolean notEqualRings(LinearRing

ring1, LinearRing

ring2) { + var p1 = ring1.getPositions(); + var p2 = ring2.getPositions(); + int shift = determineShift( p1, p2 ); + return !this.pointSeqEq.equals( p1, shiftSeqBy( shift, p2 ) ); + } + + private

PositionSequence

shiftSeqBy(int shift, PositionSequence

p2) { + if ( shift == 0 ) { + return p2; + } + int size = p2.size(); + var bldr = PositionSequenceBuilders.fixedSized( size, p2.getPositionClass() ); + for ( int k = shift; k < shift + size; k++ ) { + var idx = k % size; + if ( idx == 0 ) { + continue; //skip first (will be repeated at end) + } + bldr.add( p2.getPositionN( idx ) ); + } + //repeat element that should be first + bldr.add( p2.getPositionN( shift ) ); + return bldr.toPositionSequence(); + } + + // determines shift, if any. Otherwise return 0; + private

int determineShift(PositionSequence

p1, PositionSequence

p2) { + var startP1 = p1.getPositionN( 0 ); + int shift = 0; + for ( var p : p2 ) { + if ( p.equals( startP1 ) ) { + return shift; + } + shift++; + } + return 0; + } + + @SuppressWarnings("unchecked") + private boolean equals(AbstractGeometryCollection first, AbstractGeometryCollection second) { + if ( first.getNumGeometries() != second.getNumGeometries() ) { + return false; + } + for ( int i = 0; i < first.getNumGeometries(); i++ ) { + if ( !equals( first.getGeometryN( i ), second.getGeometryN( i ) ) ) { + return false; + } + } + return true; + } + + + } + } diff --git a/hibernate-spatial/src/test/resources/oracle10g/test-sdo-geometry-data-set-2D.xml b/hibernate-spatial/src/test/resources/oracle10g/test-sdo-geometry-data-set-2D.xml index 32822819ca..f7e0f04531 100644 --- a/hibernate-spatial/src/test/resources/oracle10g/test-sdo-geometry-data-set-2D.xml +++ b/hibernate-spatial/src/test/resources/oracle10g/test-sdo-geometry-data-set-2D.xml @@ -20,6 +20,13 @@ SRID=4326;POINT(10 5) + + 22 + POINT + SDO_GEOMETRY(2001, 4326, NULL, SDO_ELEM_INFO_ARRAY(1,1,1), SDO_ORDINATE_ARRAY(5.0, 5.0)) + SRID=4326;POINT(5 5) + + 2 POINT