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.
This commit is contained in:
parent
bf128ddbfa
commit
6658c6235f
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
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<G2D> 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<Foo> 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<G2D> point;
|
||||
|
||||
public Foo() {
|
||||
}
|
||||
|
||||
public Foo(long id, Point<G2D> point) {
|
||||
this.id = id;
|
||||
this.point = point;
|
||||
}
|
||||
}
|
|
@ -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<CommonSpatialFunction> 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 );
|
||||
|
|
|
@ -69,7 +69,7 @@ public class CommonFunctionTests extends SpatialTestBase {
|
|||
public Stream<DynamicTest> 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 <T> 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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 <T> the returned object by the test query
|
||||
*/
|
||||
public interface RowObjectMapper<T> {
|
||||
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 );
|
||||
|
|
|
@ -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<FunctionTestTemplate.Builder> all(
|
||||
NativeSQLTemplates sqlTemplates,
|
||||
Map<CommonSpatialFunction, String> hqlOverrides,
|
||||
Geometry<?>filter) {
|
||||
GeometryEquality geomEq,
|
||||
Geometry<?> filter) {
|
||||
|
||||
Map<CommonSpatialFunction, String> 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 ) );
|
||||
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
package org.hibernate.spatial.testing;
|
||||
|
||||
/**
|
||||
* Created by Karel Maesen, Geovise BVBA on 15/02/2018.
|
||||
*/
|
||||
public interface GeometryEquality<G> {
|
||||
boolean test(G geom1, G geom2);
|
||||
}
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
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.
|
||||
* <p/>
|
||||
* The notion of geometric equality can differ slightly between
|
||||
* spatial databases.
|
||||
*/
|
||||
public class JTSGeometryEquality implements GeometryEquality<Geometry> {
|
||||
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -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() );
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -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 <P extends Position> boolean equals(Geometry<P> first, Geometry<P> 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<P>) first, (Polygon<P>) second );
|
||||
}
|
||||
return pointSeqEq.equals( first.getPositions(), second.getPositions() );
|
||||
}
|
||||
|
||||
private <P extends Position> boolean equals(Polygon<P> first, Polygon<P> 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 <P extends Position> boolean notEqualRings(LinearRing<P> ring1, LinearRing<P> ring2) {
|
||||
var p1 = ring1.getPositions();
|
||||
var p2 = ring2.getPositions();
|
||||
int shift = determineShift( p1, p2 );
|
||||
return !this.pointSeqEq.equals( p1, shiftSeqBy( shift, p2 ) );
|
||||
}
|
||||
|
||||
private <P extends Position> PositionSequence<P> shiftSeqBy(int shift, PositionSequence<P> 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 <P extends Position> int determineShift(PositionSequence<P> p1, PositionSequence<P> 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,6 +20,13 @@
|
|||
<wkt>SRID=4326;POINT(10 5)</wkt>
|
||||
</Element>
|
||||
|
||||
<Element>
|
||||
<id>22</id>
|
||||
<type>POINT</type>
|
||||
<sdo>SDO_GEOMETRY(2001, 4326, NULL, SDO_ELEM_INFO_ARRAY(1,1,1), SDO_ORDINATE_ARRAY(5.0, 5.0))</sdo>
|
||||
<wkt>SRID=4326;POINT(5 5)</wkt>
|
||||
</Element>
|
||||
|
||||
<Element>
|
||||
<id>2</id>
|
||||
<type>POINT</type>
|
||||
|
|
Loading…
Reference in New Issue