From 09a9120f71656c3816baea8b7ceae632a35a8444 Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Tue, 19 Dec 2023 10:11:15 +0100 Subject: [PATCH] Add PolylinesNotOverlapping function --- .../PolylinesNotOverlappingIoxPlugin.java | 79 +++++++++++++++++++ src/model/GeoW_FunctionsExt.ili | 6 ++ .../DetectPolylineOverlay.ili | 21 ----- .../PolylinesNotOverlapping.ili | 28 +++++++ .../TestData.xtf} | 0 .../PolylinesNotOverlappingIoxPluginTest.java | 29 +++++++ 6 files changed, 142 insertions(+), 21 deletions(-) create mode 100644 src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPlugin.java delete mode 100644 src/test/data/DetectPolylineOverlay/DetectPolylineOverlay.ili create mode 100644 src/test/data/PolylinesNotOverlapping/PolylinesNotOverlapping.ili rename src/test/data/{DetectPolylineOverlay/testdata.xtf => PolylinesNotOverlapping/TestData.xtf} (100%) create mode 100644 src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPluginTest.java diff --git a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPlugin.java b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPlugin.java new file mode 100644 index 0000000..48f0a8d --- /dev/null +++ b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPlugin.java @@ -0,0 +1,79 @@ +package ch.geowerkstatt.ilivalidator.extensions.functions; + +import ch.interlis.ili2c.metamodel.PathEl; +import ch.interlis.ili2c.metamodel.Viewable; +import ch.interlis.iom.IomObject; +import ch.interlis.iom_j.itf.impl.jtsext.geom.CompoundCurve; +import ch.interlis.iox.IoxException; +import ch.interlis.iox_j.jts.Iox2jtsext; +import ch.interlis.iox_j.validator.Value; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public final class PolylinesNotOverlappingIoxPlugin extends BaseInterlisFunction { + @Override + public String getQualifiedIliName() { + return "GeoW_FunctionsExt.PolylinesNotOverlapping"; + } + + @Override + protected Value evaluateInternal(String validationKind, String usageScope, IomObject contextObject, Value[] arguments) { + Value argObjects = arguments[0]; + Value argPath = arguments[1]; + + if (argObjects.isUndefined()) { + return Value.createSkipEvaluation(); + } + + if (argObjects.getComplexObjects() == null) { + return Value.createUndefined(); + } + + Collection polylineObjects; + + if (argPath.isUndefined()) { + polylineObjects = argObjects.getComplexObjects(); + } else { + Viewable contextClass = EvaluationHelper.getContextClass(td, contextObject, argObjects); + if (contextClass == null) { + throw new IllegalStateException("unknown class in " + usageScope); + } + + PathEl[] attributePath = EvaluationHelper.getAttributePathEl(validator, contextClass, argPath); + polylineObjects = EvaluationHelper.evaluateAttributes(validator, argObjects, attributePath); + } + + List lines = convertToJTSLines(polylineObjects); + boolean hasOverlap = hasLineOverlap(lines); + return new Value(!hasOverlap); + } + + private List convertToJTSLines(Collection polylines) { + return polylines.stream() + .map(line -> { + try { + return Iox2jtsext.polyline2JTS(line, false, 0); + } catch (IoxException e) { + logger.addEvent(logger.logErrorMsg("Could not calculate {0}", getQualifiedIliName())); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private static boolean hasLineOverlap(List lines) { + for (int i = 0; i < lines.size(); i++) { + for (int j = i + 1; j < lines.size(); j++) { + if (lines.get(i).overlaps(lines.get(j))) { + return true; + } + } + } + + return false; + } +} diff --git a/src/model/GeoW_FunctionsExt.ili b/src/model/GeoW_FunctionsExt.ili index b2104f9..d957670 100644 --- a/src/model/GeoW_FunctionsExt.ili +++ b/src/model/GeoW_FunctionsExt.ili @@ -39,4 +39,10 @@ MODEL GeoW_FunctionsExt !!@ fn.return = "Zusammengefasste Flächen-Geometrie"; !!@ fn.since = "2023-12-13"; FUNCTION Union (Geometries: ANYSTRUCTURE): MULTIAREA; + + !!@ fn.description = "Prüft, dass sich die Linien-Geometrien nicht überlappen. Für 'Objects' können Objekte oder Geometrien angegeben werden. Für 'LineAttr' soll der Pfad zur Linien-Geometrie in INTERLIS 2 Syntax angegeben werden. Falls 'Objects' bereits die Geometrien enthält, soll für 'LineAttr' 'UNDEFINED' übergeben werden."; + !!@ fn.param = "Objects: Ausgangsobjekte oder Geometrien. LineAttr: Pfad zum Geometrieattribut oder UNDEFINED"; + !!@ fn.return = "TRUE, wenn sich die Linien nicht überlappen"; + !!@ fn.since = "2023-12-18"; + FUNCTION PolylinesNotOverlapping (Objects: OBJECTS OF ANYCLASS; LineAttr: TEXT): BOOLEAN; END GeoW_FunctionsExt. \ No newline at end of file diff --git a/src/test/data/DetectPolylineOverlay/DetectPolylineOverlay.ili b/src/test/data/DetectPolylineOverlay/DetectPolylineOverlay.ili deleted file mode 100644 index 1ff1fa5..0000000 --- a/src/test/data/DetectPolylineOverlay/DetectPolylineOverlay.ili +++ /dev/null @@ -1,21 +0,0 @@ -INTERLIS 2.4; - -MODEL TestSuite - AT "mailto:info@geowerkstatt.ch" VERSION "2023-12-14" = - - DOMAIN - !!@CRS=EPSG:2056 - CHKoord = COORD 2460000.000 .. 2870000.000 [INTERLIS.m], - 1045000.000 .. 1310000.000 [INTERLIS.m], - ROTATION 2 -> 1; - - TOPIC FunctionTestTopic = - - CLASS TestClass = - geometry : POLYLINE WITH (STRAIGHTS) VERTEX CHKoord WITHOUT OVERLAPS > 0.001; - type : (t1,t2,t3); - END TestClass; - - END FunctionTestTopic; - -END TestSuite. diff --git a/src/test/data/PolylinesNotOverlapping/PolylinesNotOverlapping.ili b/src/test/data/PolylinesNotOverlapping/PolylinesNotOverlapping.ili new file mode 100644 index 0000000..fbe48d3 --- /dev/null +++ b/src/test/data/PolylinesNotOverlapping/PolylinesNotOverlapping.ili @@ -0,0 +1,28 @@ +INTERLIS 2.4; + +MODEL TestSuite + AT "mailto:info@geowerkstatt.ch" VERSION "2023-12-14" = + IMPORTS GeoW_FunctionsExt; + + DOMAIN + !!@CRS=EPSG:2056 + CHKoord = COORD 2460000.000 .. 2870000.000 [INTERLIS.m], + 1045000.000 .. 1310000.000 [INTERLIS.m], + ROTATION 2 -> 1; + + TOPIC FunctionTestTopic = + + CLASS TestClass = + geometry : POLYLINE WITH (STRAIGHTS) VERTEX CHKoord WITHOUT OVERLAPS > 0.001; + type : (t1,t2,t3); + + SET CONSTRAINT setConstraintAll : GeoW_FunctionsExt.PolylinesNotOverlapping(ALL, "geometry"); + SET CONSTRAINT setConstraintT1 : WHERE type == #t1 : GeoW_FunctionsExt.PolylinesNotOverlapping(ALL, "geometry"); + SET CONSTRAINT setConstraintT2 : WHERE type == #t2 : GeoW_FunctionsExt.PolylinesNotOverlapping(ALL, "geometry"); + SET CONSTRAINT setConstraintT3 : WHERE type == #t3 : GeoW_FunctionsExt.PolylinesNotOverlapping(ALL, "geometry"); + SET CONSTRAINT setConstraintT2or3 : WHERE type == #t2 OR type == #t3 : GeoW_FunctionsExt.PolylinesNotOverlapping(ALL, "geometry"); + END TestClass; + + END FunctionTestTopic; + +END TestSuite. diff --git a/src/test/data/DetectPolylineOverlay/testdata.xtf b/src/test/data/PolylinesNotOverlapping/TestData.xtf similarity index 100% rename from src/test/data/DetectPolylineOverlay/testdata.xtf rename to src/test/data/PolylinesNotOverlapping/TestData.xtf diff --git a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPluginTest.java b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPluginTest.java new file mode 100644 index 0000000..a9db70b --- /dev/null +++ b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPluginTest.java @@ -0,0 +1,29 @@ +package ch.geowerkstatt.ilivalidator.extensions.functions; + +import ch.interlis.ili2c.Ili2cFailure; +import ch.interlis.iox.IoxException; +import com.vividsolutions.jts.util.Assert; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public final class PolylinesNotOverlappingIoxPluginTest { + private static final String TEST_DATA = "PolylinesNotOverlapping/TestData.xtf"; + private ValidationTestHelper vh = null; + + @BeforeEach + void setUp() { + vh = new ValidationTestHelper(); + vh.addFunction(new PolylinesNotOverlappingIoxPlugin()); + } + + @Test + void polylinesNotOverlapping() throws Ili2cFailure, IoxException { + vh.runValidation(new String[]{TEST_DATA}, new String[]{"PolylinesNotOverlapping/PolylinesNotOverlapping.ili"}); + Assert.equals(2, vh.getErrs().size()); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintAll"); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT1"); + AssertionHelper.assertNoConstraintError(vh, "setConstraintT2"); + AssertionHelper.assertNoConstraintError(vh, "setConstraintT3"); + AssertionHelper.assertNoConstraintError(vh, "setConstraintT2or3"); + } +}