package nl.ipo.cds.validation.geometry; import com.vividsolutions.jts.algorithm.RobustLineIntersector; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geomgraph.GeometryGraph; import com.vividsolutions.jts.operation.valid.ConnectedInteriorTester; import nl.ipo.cds.validation.*; import nl.ipo.cds.validation.execute.Compiler; import nl.ipo.cds.validation.execute.CompilerException; import nl.ipo.cds.validation.execute.ExpressionExecutor; import org.deegree.geometry.Geometry; import org.deegree.geometry.multi.MultiGeometry; import org.deegree.geometry.points.Points; import org.deegree.geometry.primitive.Curve; import org.deegree.geometry.primitive.Point; import org.deegree.geometry.primitive.Ring; import org.deegree.geometry.primitive.Surface; import org.deegree.geometry.standard.DefaultEnvelope; import org.deegree.geometry.standard.primitive.DefaultLinearRing; import org.deegree.geometry.standard.primitive.DefaultPoint; import org.deegree.geometry.validation.GeometryValidator; import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; public class GeometryExpression<K extends Enum<K> & ValidationMessage<K, C>, C extends ValidatorContext<K, C>, T extends Geometry> extends AttributeExpression<K, C, T> { public GeometryExpression(final String name, final Class<T> type) { super(name, type); } public GeometryExpression(final String name, final Class<T> type, final String label) { super(name, type, label); } @Override public GeometryExpression<K, C, T> label(final String label) { return new GeometryExpression<K, C, T>(name, type, label); } public Expression<K, C, Boolean> isEmptyMultiGeometry() { return new AbstractUnaryTestExpression<K, C, T>(this, "IsEmptyMultiGeometry") { @Override public boolean test(final T value, final C context) { return value instanceof MultiGeometry<?> && ((MultiGeometry<?>) value).size() == 0; } }; } public Expression<K, C, Boolean> isPoint() { return new AbstractUnaryTestExpression<K, C, T>(this, "IsPoint") { @Override public boolean test(final T value, final C context) { return value instanceof Point; } }; } public Expression<K, C, Boolean> isPointOrMultiPoint() { return new AbstractUnaryTestExpression<K, C, T>(this, "IsPointOrMultiPoint") { @Override public boolean test(final T value, final C context) { if (value instanceof Point) { return true; } if (value instanceof MultiGeometry<?>) { for (Geometry member : ((MultiGeometry<?>) value)) { if (!(member instanceof Point)) { return false; } } return true; } return false; } }; } public Expression<K, C, Boolean> isCurveOrMultiCurve() { return new AbstractUnaryTestExpression<K, C, T>(this, "IsCurveOrMultiCurve") { @Override public boolean test(final T value, final C context) { if (value instanceof Curve) { return true; } if (value instanceof MultiGeometry<?>) { for (Geometry member : ((MultiGeometry<?>) value)) { if (!(member instanceof Curve)) { return false; } } return true; } return false; } }; } public Expression<K, C, Boolean> isSurface() { return new AbstractUnaryTestExpression<K, C, T>(this, "IsSurface") { @Override public boolean test(final T value, final C context) { if (value instanceof Surface) { return true; } return false; } }; } public Expression<K, C, Boolean> isSurfaceOrMultiSurface() { return new AbstractUnaryTestExpression<K, C, T>(this, "IsSurfaceOrMultiSurface") { @Override public boolean test(final T value, final C context) { if (value instanceof Surface) { return true; } if (value instanceof MultiGeometry<?>) { for (Geometry member : ((MultiGeometry<?>) value)) { if (!(member instanceof Surface)) { return false; } } return true; } return false; } }; } public Expression<K, C, Boolean> isInteriorDisconnected() { return new AbstractUnaryTestExpression<K, C, T>(this, "IsInteriorDisconnected") { @Override public boolean test(T value, C context) { if (!(value instanceof Surface)) { return false; } try { final GeometryValidator validator = context.validateGeometry(value).validator; final Surface polygon = (Surface) value; final Ring exterior = new DefaultLinearRing("exterior", null, null, polygon .getExteriorRingCoordinates()); final GeometryFactory geometryFactory = new GeometryFactory(); final LinearRing exteriorRing = getJTSRing(exterior, validator); final ArrayList<LinearRing> interiorRings = new ArrayList<>(); for (final Points points : polygon.getInteriorRingsCoordinates()) { final Ring ring = new DefaultLinearRing("interior", null, null, points); interiorRings.add(getJTSRing(ring, validator)); } com.vividsolutions.jts.geom.Polygon jtsPolygon = geometryFactory.createPolygon(exteriorRing, interiorRings.toArray(new LinearRing[interiorRings.size()])); final GeometryGraph geometryGraph = new GeometryGraph(0, jtsPolygon); geometryGraph.computeSelfNodes(new RobustLineIntersector(), true); ConnectedInteriorTester connectedInteriorTester = new ConnectedInteriorTester(geometryGraph); return !connectedInteriorTester.isInteriorsConnected(); } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException e) { throw new ExpressionEvaluationException(e); } } }; } public Expression<K, C, Boolean> hasSrs() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasSrs") { @Override public boolean test(final T value, final C context) { return value != null && value.getCoordinateSystem() != null; } }; } public Expression<K, C, Boolean> isSrs(final Expression<K, C, String> srsName) { return new AbstractBinaryTestExpression<K, C, T, String>(this, srsName, "IsSrs") { @Override public boolean test(T a, String b, C context) { return a != null && b != null && a.getCoordinateSystem() != null && a.getCoordinateSystem().getName().contains(b); } }; } /** * Een coordinaat in RD dat binnen de BBOX valt: west:-35995, south:305979, east:291490, north: 855885 */ public Expression<K, C, Boolean> hasValidCoordinateRD() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasValidCoordinateRD") { @Override public boolean test(T value, C context) { Point xVal = new DefaultPoint( null, null, null , new double[] { (-35995), 855885} ); Point yVal = new DefaultPoint( null, null, null , new double[] { 291490, 305979} ); Geometry bBoxRd = new DefaultEnvelope(xVal, yVal); if(value.isWithin(bBoxRd)){ return true; } return false; } }; } public Expression<K, C, Boolean> hasCurveDuplicatePoint() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasCurveDuplicatePoint") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).curvePointDuplication(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasCurveDiscontinuity() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasCurveDiscontinuity") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).curveDiscontinuity(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasCurveSelfIntersection() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasCurveSelfIntersection") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).curveSelfIntersection(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasUnclosedRing() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasUnclosedRing") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).ringNotClosed(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasRingSelfIntersection() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasRingSelfIntersection") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).ringSelfIntersection(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasExteriorRingCW() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasExteriorRingCW") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).exteriorRingCW(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasInteriorRingCCW() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasInteriorRingCCW") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).interiorRingCCW(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasTouchingInteriorRings() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasTouchingInteriorRings") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).interiorRingsTouch(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasIntersectingInteriorRings() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasIntersectingInteriorRings") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).interiorRingsIntersect(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasInteriorRingsWithin() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasInteriorRingsWithin") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).interiorRingsWithin(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasInteriorRingTouchingExterior() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasInteriorRingTouchingExterior") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).interiorRingTouchesExterior(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasInteriorRingIntersectingExterior() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasInteriorRingIntersectingExterior") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).interiorRingTouchesExterior(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public Expression<K, C, Boolean> hasInteriorRingOutsideExterior() { return new AbstractUnaryTestExpression<K, C, T>(this, "HasInteriorRingOutsideExterior") { @Override public boolean test(final T value, final C context) { final GeometryValidationStatus status = context.validateGeometry(value).interiorRingOutsideExterior(); if (status.status()) { context.setLastLocation(status.location()); return true; } return false; } }; } public class SrsNameExpression extends AbstractExpression<K, C, String> { @Override public Class<String> getResultType() { return String.class; } public String evaluate(final C context, final Geometry input) { final Geometry value = input; return value == null || value.getCoordinateSystem() == null ? null : value.getCoordinateSystem().getName(); } @Override public ExpressionExecutor<C> getExecutor(final Compiler<C> compiler) throws CompilerException { return ExpressionExecutor.create( this, GeometryExpression.this, false, true, Compiler.findMethod(SrsNameExpression.class, "evaluate", MethodType.methodType(String.class, ValidatorContext.class, Geometry.class)).bindTo(this), false); } } public Expression<K, C, String> srsName() { return new SrsNameExpression(); } private final static Method getJTSRingMethod; static { try { getJTSRingMethod = GeometryValidator.class.getDeclaredMethod("getJTSRing", Ring.class); getJTSRingMethod.setAccessible(true); } catch (Exception e) { throw new RuntimeException(e); } } private static LinearRing getJTSRing(final Ring exteriorRing, final GeometryValidator geometryValidator) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { return (LinearRing) getJTSRingMethod.invoke(geometryValidator, exteriorRing); } }