Skip to content

Commit

Permalink
Merge pull request #1035 from lat-lon/geometryFixes-28
Browse files Browse the repository at this point in the history
Add geometry fixes
  • Loading branch information
copierrj authored Mar 16, 2022
2 parents 00667f0 + 1740a86 commit 3825b01
Show file tree
Hide file tree
Showing 80 changed files with 1,808 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public static Curve invertOrientation( Curve curve ) {
for ( Curve memberCurve : compositeCurve ) {
fixedMemberCurves.add( invertOrientation( memberCurve ) );
}

Collections.reverse( fixedMemberCurves );
fixedCurve = new DefaultCompositeCurve( curve.getId(), curve.getCoordinateSystem(), curve.getPrecision(),
fixedMemberCurves );
break;
Expand Down Expand Up @@ -243,26 +243,30 @@ public static Ring forceOrientation( Ring ring, boolean ccw ) {
final List<CurveSegment> curveSegments = curve.getCurveSegments();
for ( final CurveSegment curveSegment : curveSegments ) {
final CurveSegmentType segmentType = curveSegment.getSegmentType();

Points points;
switch ( segmentType ) {
case ARC:
points = ( (Arc) curveSegment ).getControlPoints();
break;
case ARC_STRING:
points = ( (ArcString) curveSegment ).getControlPoints();
break;
case LINE_STRING_SEGMENT:
points = ( (LineStringSegment) curveSegment ).getControlPoints();
for ( int i = 0; i < points.size(); i++ ) {
final Point first = points.get( i );
final Point second = points.get( ( i + 1 ) % points.size() );

shoelaceSum += ( second.get0() - first.get0() ) * ( second.get1() + first.get1() );
}

continue;
break;
default:
LOG.warn( "Calculating orientation of " + segmentType.name()
+ " segments is not implemented yet. Ring orientation remains unchanged." );
return ring;
}

for ( int i = 1; i < points.size(); i++ ) {
final Point first = points.get( i - 1 );
final Point second = points.get( i );

shoelaceSum += ( second.get0() - first.get0() ) * ( second.get1() + first.get1() );
}
}
}

Expand All @@ -283,6 +287,7 @@ public static Ring invertOrientation( Ring ring ) {
for ( Curve memberCurve : ring.getMembers() ) {
fixedMemberCurves.add( invertOrientation( memberCurve ) );
}
Collections.reverse( fixedMemberCurves );
fixedRing = new DefaultRing( ring.getId(), ring.getCoordinateSystem(), ring.getPrecision(),
fixedMemberCurves );
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,36 @@
import static org.deegree.geometry.primitive.segments.CurveSegment.CurveSegmentType.ARC;
import static org.deegree.geometry.primitive.segments.CurveSegment.CurveSegmentType.LINE_STRING_SEGMENT;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.deegree.cs.coordinatesystems.ICRS;
import org.deegree.cs.persistence.CRSManager;
import org.deegree.geometry.GeometryFactory;
import org.deegree.geometry.composite.CompositeCurve;
import org.deegree.geometry.points.Points;
import org.deegree.geometry.primitive.Curve;
import org.deegree.geometry.primitive.Curve.CurveType;
import org.deegree.geometry.primitive.Point;
import org.deegree.geometry.primitive.Ring;
import org.deegree.geometry.primitive.segments.CurveSegment;
import org.deegree.geometry.standard.composite.DefaultCompositeCurve;
import org.deegree.geometry.standard.curvesegments.DefaultLineStringSegment;
import org.deegree.geometry.standard.points.PointsArray;
import org.deegree.geometry.standard.primitive.DefaultCurve;
import org.deegree.geometry.standard.primitive.DefaultRing;
import org.deegree.geometry.validation.event.GeometryValidationEvent;
import org.junit.Test;

/**
* @author <a href="mailto:[email protected]">Lyn Goltz</a>
* @author last edited by: $Author: lyn $
*
* @version $Revision: $, $Date: $
* @author <a href="mailto:[email protected]">Stephan Reichhelm</a>
*/
public class GeometryFixerTest {

Expand Down Expand Up @@ -105,6 +112,112 @@ public void testInvertOrientationOfACurveWithMultipleSegments() {
assertTrue( invertedCurve.isClosed() );
}

@Test
public void testForceOrientationWithArcString() {
// Test that forceOrientation accepts combinations of LineStringSegment and ArcString
Curve curve = createCurve( createFirstSegment(), createSecondSegmentAsArcString(), createThirdSegment() );
Ring ring = createRing( curve );

Ring fixedA = GeometryFixer.forceOrientation( ring, true );
List<CurveSegment> curveSegmentsA = fixedA.getCurveSegments();
assertThat( curveSegmentsA.size(), is( 3 ) );

Ring fixedB = GeometryFixer.forceOrientation( ring, false );
List<CurveSegment> curveSegmentsB = fixedB.getCurveSegments();
assertThat( curveSegmentsB.size(), is( 3 ) );
}

@Test
public void testForceOrientationWithArc() {
// Test that forceOrientation accepts combinations of LineStringSegment and Arc
Curve curve = createCurve( createFirstSegment(), createSecondSegment(), createThirdSegment() );
Ring ring = createRing( curve );

Ring fixedA = GeometryFixer.forceOrientation( ring, true );
List<CurveSegment> curveSegmentsA = fixedA.getCurveSegments();
assertThat( curveSegmentsA.size(), is( 3 ) );

Ring fixedB = GeometryFixer.forceOrientation( ring, false );
List<CurveSegment> curveSegmentsB = fixedB.getCurveSegments();
assertThat( curveSegmentsB.size(), is( 3 ) );
}

@Test
public void testInvertOfCompoundCurve() {
CountingValidationEventHandler valevent = new CountingValidationEventHandler();
GeometryValidator validator = new GeometryValidator( valevent );

Curve curve1 = createCurve( createFirstSegment() );
Curve curve2 = createCurve( createSecondSegment() );
Curve curve3 = createCurve( createThirdAltSegment() );
CompositeCurve ocomp = createCompositeCurve( curve1, curve2, curve3 );

assertThat( ocomp.size(), is( 3 ) );
assertFalse( "Compound is open", ocomp.isClosed() );

assertTrue( "Source is valid", validator.validateGeometry( ocomp ) );
assertEquals( "No validation errors", 0, valevent.getEvents().size() );

Curve icurve = GeometryFixer.invertOrientation( ocomp );

assertThat( "Result is CompositeCurve", icurve.getCurveType(), is( CurveType.CompositeCurve ) );
CompositeCurve icomp = (CompositeCurve) icurve;

assertThat( icomp.size(), is( 3 ) );
assertFalse( "Compound is open", icomp.isClosed() );

assertTrue( "Inverted is valid", validator.validateGeometry( icomp ) );
assertEquals( "No validation errors", 0, valevent.getEvents().size() );

// Check that the composite curve was inverted (compare inverted start with original end)
assertThat( "Invert S/E", icomp.getStartPoint().get0(), is( ocomp.getEndPoint().get0() ) );
assertThat( "Invert S/E", icomp.getStartPoint().get1(), is( ocomp.getEndPoint().get1() ) );

assertThat( "Invert E/S", icomp.getEndPoint().get0(), is( ocomp.getStartPoint().get0() ) );
assertThat( "Invert E/S", icomp.getEndPoint().get1(), is( ocomp.getStartPoint().get1() ) );
}

@Test
public void testInvertOfRing() {
CountingValidationEventHandler valevent = new CountingValidationEventHandler();
GeometryValidator validator = new GeometryValidator( valevent );

Curve curve1 = createCurve( createFirstSegment() );
Curve curve2 = createCurve( createSecondSegment() );
Curve curve3 = createCurve( createThirdSegment() );
Ring oring = createRing( curve1, curve2, curve3 );

assertThat( oring.getMembers().size(), is( 3 ) );

assertTrue( "Source is valid", validator.validateGeometry( oring ) );
assertEquals( "No validation errors", 0, valevent.getEvents().size() );

Curve cinv = GeometryFixer.invertOrientation( oring );

assertThat( "Result is Ring", cinv.getCurveType(), is( CurveType.Ring ) );
Ring iring = (Ring) cinv;

assertTrue( "Inverted is valid", validator.validateGeometry( cinv ) );
assertEquals( "Inverted has no validation errors", 0, valevent.getEvents().size() );

assertThat( iring.getMembers().size(), is( 3 ) );

// Check that the composite ring was inverted
Point second = iring.getMembers().get( 0 ).getControlPoints().get( 1 );
int lastCurveCnt = oring.getMembers().get( 2 ).getControlPoints().size();
Point beforeLast = oring.getMembers().get( 2 ).getControlPoints().get( lastCurveCnt - 2 );
assertThat( "Original 2 equals inverted n-1", second.get0(), is( beforeLast.get0() ) );
assertThat( "Original 2 equals inverted n-1", second.get1(), is( beforeLast.get1() ) );
}

private CompositeCurve createCompositeCurve( Curve... curves ) {
return new DefaultCompositeCurve( "id", CRS, null, Arrays.asList( curves ) );
}

private Ring createRing( Curve... curves ) {
return new DefaultRing( "id", CRS, null, Arrays.asList( curves ) );
}

private Curve createCurve() {
return createCurve( createFirstSegment(), createSecondSegment(), createThirdSegment() );
}
Expand Down Expand Up @@ -134,6 +247,14 @@ private CurveSegment createSecondSegment() {
return GEOM_FACTORY.createArc( point1, point2, point3 );
}

private CurveSegment createSecondSegmentAsArcString() {
Point point1 = GEOM_FACTORY.createPoint( "idpa1", 553976.438, 5937165.552, CRS );
Point point2 = GEOM_FACTORY.createPoint( "idpa2", 553974.601, 5937161.092, CRS );
Point point3 = GEOM_FACTORY.createPoint( "idpa3", 553969.988, 5937159.683, CRS );
Points points = new PointsArray( point1, point2, point3 );
return GEOM_FACTORY.createArcString( points );
}

private CurveSegment createThirdSegment() {
Point point1 = GEOM_FACTORY.createPoint( "idp21", 553969.988, 5937159.683, CRS );
Point point2 = GEOM_FACTORY.createPoint( "idp22", 553938.699, 5937160.863, CRS );
Expand All @@ -149,4 +270,31 @@ private CurveSegment createThirdSegment() {
return new DefaultLineStringSegment( points );
}

private CurveSegment createThirdAltSegment() {
// Create non closing this segment
Point point1 = GEOM_FACTORY.createPoint( "idp21", 553969.988, 5937159.683, CRS );
Point point2 = GEOM_FACTORY.createPoint( "idp22", 553938.699, 5937160.863, CRS );
Point point3 = GEOM_FACTORY.createPoint( "idp23", 553903.017, 5937162.213, CRS );
Point point4 = GEOM_FACTORY.createPoint( "idp24", 553897.200, 5937200.469, CRS );
Point point5 = GEOM_FACTORY.createPoint( "idp25", 553897.613, 5937236.333, CRS );
Points points = new PointsArray( point1, point2, point3, point4, point5 );
return new DefaultLineStringSegment( points );
}

class CountingValidationEventHandler implements GeometryValidationEventHandler {

private final List<GeometryValidationEvent> events = new ArrayList<GeometryValidationEvent>();

@Override
public boolean fireEvent( GeometryValidationEvent event ) {
events.add( event );
// to count all errors return true
return true;
}

List<GeometryValidationEvent> getEvents() {
return events;
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@

import java.sql.SQLException;

import org.deegree.sqldialect.oracle.sdo.SDOGeometryConverter.GeomHolder;

import oracle.jdbc.OracleConnection;
import oracle.sql.ARRAY;
import oracle.sql.ArrayDescriptor;
Expand Down Expand Up @@ -114,6 +116,11 @@ protected static double[] fromDoubleArray( STRUCT struct, final double defaultVa
return fromDoubleArray( struct.getOracleAttributes(), defaultValue );
}

protected static STRUCT toSDOGeometry( GeomHolder h, OracleConnection conn )
throws SQLException {
return toSDOGeometry( h.gtype, h.srid, h.elem_info, h.ordinates, conn);
}

/**
* Convert SDO_Geometry informations into Oracle JDBC STRUCT element
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ else if ( sdo.gtype_tt == SDOGTypeTT.UNKNOWN )
case SDOEType.POLYGON_RING_INTERIOR: // see above NOTE 1
case SDOEType.POLYGON_RING_EXTERIOR:
case SDOEType.POLYGON_RING_UNKNOWN:
Ring rng = handleSimpleRing( sdo );
Ring rng = handleSimpleRing( sdo, etype );
switch ( etype ) {
case SDOEType.POLYGON_RING_INTERIOR:
ringi.add( rng );
Expand Down Expand Up @@ -642,7 +642,7 @@ else if ( intpr == 2 )
* <li>4) circle; three distinct points on the circle</li>
* </ol>
*/
private Ring handleSimpleRing( GeomHolder sdo )
private Ring handleSimpleRing( GeomHolder sdo, int etype )
throws SQLException {
int intpr = sdo.elem_info[sdo.elemoff + 2];
int off = sdo.elem_info[sdo.elemoff];
Expand Down Expand Up @@ -672,13 +672,14 @@ else if ( intpr == 2 || intpr == 4 ) {
Point b = _gf.createPoint( null, ll.get0(), ur.get1(), sdo.crs );
Point c = _gf.createPoint( null, ur.get0(), ur.get1(), sdo.crs );
Point d = _gf.createPoint( null, ur.get0(), ll.get1(), sdo.crs );
Points rngp = null;
if ( ll.get0() < ur.get0() || ll.get1() < ur.get1() ) {
rngp = new PointsArray( a, b, c, d, a );
} else {
rngp = new PointsArray( a, d, c, b, a );
rng = _gf.createLinearRing( null, sdo.crs, new PointsArray( a, b, c, d, a ) );

// enforce orientations if type of ring is known
if ( etype == SDOEType.POLYGON_RING_INTERIOR ) {
rng = forceOrientation( rng, false );
} else if ( etype == SDOEType.POLYGON_RING_EXTERIOR ) {
rng = forceOrientation( rng, true );
}
rng = _gf.createLinearRing( null, sdo.crs, rngp );
}

if ( rng == null ) {
Expand Down Expand Up @@ -729,9 +730,7 @@ public Object fromGeometry( OracleConnection conn, int srid, Geometry geometry )
}

@SuppressWarnings("unchecked")
public Object fromGeometry( OracleConnection conn, int srid, Geometry geometry, boolean allowJTSfallback )
throws SQLException {

protected GeomHolder fromGeometry( int srid, Geometry geometry, boolean allowJTSfallback ) {
List<Triplet> info = new LinkedList<Triplet>();
List<Point> pnts = new LinkedList<Point>();
int gtypett = SDOGTypeTT.UNKNOWN;
Expand Down Expand Up @@ -780,7 +779,23 @@ public Object fromGeometry( OracleConnection conn, int srid, Geometry geometry,
int[] elemInfo = buildResultElemeInfo( dim, info );

int gtyp = ( 1000 * dim ) + gtypett;
return OracleObjectTools.toSDOGeometry( gtyp, srid, elemInfo, ordinates, conn );
LOG.trace( "fromGeometry: MDSYS.SDO_GEOMETRY( {}, {}, NULL, MDSYS.SDO_ELEM_INFO_ARRAY{}, MDSYS.SDO_ORDINATE_ARRAY{} )",
gtyp, srid, elemInfo, ordinates );

return new GeomHolder( gtyp, srid, null, elemInfo, ordinates, null );
}

// no parse able geometry found
return null;
}


public Object fromGeometry( OracleConnection conn, int srid, Geometry geometry, boolean allowJTSfallback )
throws SQLException {

GeomHolder holder = fromGeometry( srid, geometry, allowJTSfallback );
if ( holder != null ) {
return OracleObjectTools.toSDOGeometry( holder, conn );
} else {
// create error message
}
Expand Down Expand Up @@ -1040,7 +1055,7 @@ private void buildSurface( List<Triplet> info, List<Point> pnts, Surface geometr
if ( isSimple ) {
buildCurveSegmentSimple( info, pnts, iseg.get( 0 ), false );
} else {
info.add( new Triplet( pnts.size(), SDOEType.COMPOUND_POLYGON_RING_INTERIOR, eseg.size() ) );
info.add( new Triplet( pnts.size(), SDOEType.COMPOUND_POLYGON_RING_INTERIOR, iseg.size() ) );
for ( int i = 0, j = iseg.size(); i < j; i++ ) {
buildCurveSegment( info, pnts, iseg.get( i ), i > 0 );
}
Expand All @@ -1049,6 +1064,21 @@ private void buildSurface( List<Triplet> info, List<Point> pnts, Surface geometr
}
}

/**
* Add points to internal point list
*
* <p>
* <strong>Note:</strong>if the first points equals the lasts point and the geometry should be continued the first
* point will be skipped. This is reduces duplicate points for example with AAA / ALKIS geometries.
* </p>
*
* @param pnts
* current list of points
* @param add
* points to add
* @param continueGeom
* skipp first point if it equals last point in list
*/
private void addPnts( List<Point> pnts, Points add, boolean continueGeom ) {
int off = 0;

Expand Down
Loading

0 comments on commit 3825b01

Please sign in to comment.