From c069e6b966ba56d977a3c39a507798cb0c3cec87 Mon Sep 17 00:00:00 2001 From: Oliver Grimm Date: Thu, 14 Dec 2023 14:43:45 +0100 Subject: [PATCH 1/9] add testdata --- .../DetectPolylineOverlay.ili | 21 +++++++++++++++++++ .../data/DetectPolylineOverlay/testdata.xtf | 15 +++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 src/test/data/DetectPolylineOverlay/DetectPolylineOverlay.ili create mode 100644 src/test/data/DetectPolylineOverlay/testdata.xtf diff --git a/src/test/data/DetectPolylineOverlay/DetectPolylineOverlay.ili b/src/test/data/DetectPolylineOverlay/DetectPolylineOverlay.ili new file mode 100644 index 0000000..1ff1fa5 --- /dev/null +++ b/src/test/data/DetectPolylineOverlay/DetectPolylineOverlay.ili @@ -0,0 +1,21 @@ +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/DetectPolylineOverlay/testdata.xtf b/src/test/data/DetectPolylineOverlay/testdata.xtf new file mode 100644 index 0000000..3cedf90 --- /dev/null +++ b/src/test/data/DetectPolylineOverlay/testdata.xtf @@ -0,0 +1,15 @@ + +TestSuiteili2gpkg-5.0.0-20f2bb62307ba6329a125fc6f10965ad9a4e6300 + + +2645657.4661249752.5422645626.7821249819.8262645687.0411249842.7462645691.7021249830.0362645693.1411249826.110t1 +2645542.6771249774.5382645543.0271249774.7612645564.1191249788.2172645584.8221249800.6022645613.1031249812.9862645625.6731249785.6292645629.6241249777.0492645637.5031249759.936t1 +2645691.7021249830.0362645693.1411249826.1102645699.2411249810.7222645704.1391249797.921t1 +2645543.0271249774.7612645564.1191249788.2172645565.8751249785.3062645572.5301249789.5572645581.0321249773.106t2 +2645549.8051249782.5682645564.7781249757.798t3 +2645657.4661249752.5422645626.7821249819.8262645687.0411249842.7462645694.4461249833.0302645689.6401249826.1912645678.7351249822.125t2 +2645647.1261249816.2092645641.9501249828.2242645648.7901249831.3672645651.5621249825.0822645660.0651249828.4092645657.8471249834.324t3 +2645629.6241249777.0492645637.5031249759.9362645646.9411249738.2052645643.0601249727.669t3 + + + \ No newline at end of file From 09a9120f71656c3816baea8b7ceae632a35a8444 Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Tue, 19 Dec 2023 10:11:15 +0100 Subject: [PATCH 2/9] 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"); + } +} From b0f2342668930f256ce0122e2d191cdc975c306a Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Tue, 19 Dec 2023 15:03:52 +0100 Subject: [PATCH 3/9] Change PolylinesNotOverlapping to PolylinesOverlap --- ...in.java => PolylinesOverlapIoxPlugin.java} | 6 ++--- src/model/GeoW_FunctionsExt.ili | 6 ++--- .../PolylinesOverlap.ili} | 10 ++++----- .../TestData.xtf | 0 ...ava => PolylinesOverlapIoxPluginTest.java} | 22 +++++++++---------- 5 files changed, 22 insertions(+), 22 deletions(-) rename src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/{PolylinesNotOverlappingIoxPlugin.java => PolylinesOverlapIoxPlugin.java} (93%) rename src/test/data/{PolylinesNotOverlapping/PolylinesNotOverlapping.ili => PolylinesOverlap/PolylinesOverlap.ili} (69%) rename src/test/data/{PolylinesNotOverlapping => PolylinesOverlap}/TestData.xtf (100%) rename src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/{PolylinesNotOverlappingIoxPluginTest.java => PolylinesOverlapIoxPluginTest.java} (54%) diff --git a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPlugin.java b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java similarity index 93% rename from src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPlugin.java rename to src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java index 48f0a8d..48605e9 100644 --- a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPlugin.java +++ b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java @@ -13,10 +13,10 @@ import java.util.Objects; import java.util.stream.Collectors; -public final class PolylinesNotOverlappingIoxPlugin extends BaseInterlisFunction { +public final class PolylinesOverlapIoxPlugin extends BaseInterlisFunction { @Override public String getQualifiedIliName() { - return "GeoW_FunctionsExt.PolylinesNotOverlapping"; + return "GeoW_FunctionsExt.PolylinesOverlap"; } @Override @@ -48,7 +48,7 @@ protected Value evaluateInternal(String validationKind, String usageScope, IomOb List lines = convertToJTSLines(polylineObjects); boolean hasOverlap = hasLineOverlap(lines); - return new Value(!hasOverlap); + return new Value(hasOverlap); } private List convertToJTSLines(Collection polylines) { diff --git a/src/model/GeoW_FunctionsExt.ili b/src/model/GeoW_FunctionsExt.ili index d957670..f35c8c2 100644 --- a/src/model/GeoW_FunctionsExt.ili +++ b/src/model/GeoW_FunctionsExt.ili @@ -40,9 +40,9 @@ MODEL GeoW_FunctionsExt !!@ 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.description = "Prüft, ob sich die Linien-Geometrien ü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.return = "TRUE, wenn sich die Linien überlappen"; !!@ fn.since = "2023-12-18"; - FUNCTION PolylinesNotOverlapping (Objects: OBJECTS OF ANYCLASS; LineAttr: TEXT): BOOLEAN; + FUNCTION PolylinesOverlap (Objects: OBJECTS OF ANYCLASS; LineAttr: TEXT): BOOLEAN; END GeoW_FunctionsExt. \ No newline at end of file diff --git a/src/test/data/PolylinesNotOverlapping/PolylinesNotOverlapping.ili b/src/test/data/PolylinesOverlap/PolylinesOverlap.ili similarity index 69% rename from src/test/data/PolylinesNotOverlapping/PolylinesNotOverlapping.ili rename to src/test/data/PolylinesOverlap/PolylinesOverlap.ili index fbe48d3..12e1f3d 100644 --- a/src/test/data/PolylinesNotOverlapping/PolylinesNotOverlapping.ili +++ b/src/test/data/PolylinesOverlap/PolylinesOverlap.ili @@ -16,11 +16,11 @@ MODEL TestSuite 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"); + SET CONSTRAINT setConstraintAllNoOverlaps : NOT(GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry")); + SET CONSTRAINT setConstraintT1 : WHERE type == #t1 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); + SET CONSTRAINT setConstraintT2 : WHERE type == #t2 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); + SET CONSTRAINT setConstraintT3 : WHERE type == #t3 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); + SET CONSTRAINT setConstraintT2or3 : WHERE type == #t2 OR type == #t3 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); END TestClass; END FunctionTestTopic; diff --git a/src/test/data/PolylinesNotOverlapping/TestData.xtf b/src/test/data/PolylinesOverlap/TestData.xtf similarity index 100% rename from src/test/data/PolylinesNotOverlapping/TestData.xtf rename to src/test/data/PolylinesOverlap/TestData.xtf diff --git a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPluginTest.java b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPluginTest.java similarity index 54% rename from src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPluginTest.java rename to src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPluginTest.java index a9db70b..0d7ba82 100644 --- a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesNotOverlappingIoxPluginTest.java +++ b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPluginTest.java @@ -6,24 +6,24 @@ 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"; +public final class PolylinesOverlapIoxPluginTest { + private static final String TEST_DATA = "PolylinesOverlap/TestData.xtf"; private ValidationTestHelper vh = null; @BeforeEach void setUp() { vh = new ValidationTestHelper(); - vh.addFunction(new PolylinesNotOverlappingIoxPlugin()); + vh.addFunction(new PolylinesOverlapIoxPlugin()); } @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"); + void polylinesOverlap() throws Ili2cFailure, IoxException { + vh.runValidation(new String[]{TEST_DATA}, new String[]{"PolylinesOverlap/PolylinesOverlap.ili"}); + Assert.equals(4, vh.getErrs().size()); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintAllNoOverlaps"); + AssertionHelper.assertNoConstraintError(vh, "setConstraintT1"); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT2"); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT3"); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT2or3"); } } From 28aff98f897316fcdb1b472c855b1a50f3f2728c Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Tue, 19 Dec 2023 15:05:54 +0100 Subject: [PATCH 4/9] Format TestData xtf file --- src/test/data/PolylinesOverlap/TestData.xtf | 246 ++++++++++++++++++-- 1 file changed, 231 insertions(+), 15 deletions(-) diff --git a/src/test/data/PolylinesOverlap/TestData.xtf b/src/test/data/PolylinesOverlap/TestData.xtf index 3cedf90..eeec078 100644 --- a/src/test/data/PolylinesOverlap/TestData.xtf +++ b/src/test/data/PolylinesOverlap/TestData.xtf @@ -1,15 +1,231 @@ - -TestSuiteili2gpkg-5.0.0-20f2bb62307ba6329a125fc6f10965ad9a4e6300 - - -2645657.4661249752.5422645626.7821249819.8262645687.0411249842.7462645691.7021249830.0362645693.1411249826.110t1 -2645542.6771249774.5382645543.0271249774.7612645564.1191249788.2172645584.8221249800.6022645613.1031249812.9862645625.6731249785.6292645629.6241249777.0492645637.5031249759.936t1 -2645691.7021249830.0362645693.1411249826.1102645699.2411249810.7222645704.1391249797.921t1 -2645543.0271249774.7612645564.1191249788.2172645565.8751249785.3062645572.5301249789.5572645581.0321249773.106t2 -2645549.8051249782.5682645564.7781249757.798t3 -2645657.4661249752.5422645626.7821249819.8262645687.0411249842.7462645694.4461249833.0302645689.6401249826.1912645678.7351249822.125t2 -2645647.1261249816.2092645641.9501249828.2242645648.7901249831.3672645651.5621249825.0822645660.0651249828.4092645657.8471249834.324t3 -2645629.6241249777.0492645637.5031249759.9362645646.9411249738.2052645643.0601249727.669t3 - - - \ No newline at end of file + + + + + TestSuite + + ili2gpkg-5.0.0-20f2bb62307ba6329a125fc6f10965ad9a4e6300 + + + + + + + + 2645657.466 + 1249752.542 + + + 2645626.782 + 1249819.826 + + + 2645687.041 + 1249842.746 + + + 2645691.702 + 1249830.036 + + + 2645693.141 + 1249826.110 + + + + t1 + + + + + + 2645542.677 + 1249774.538 + + + 2645543.027 + 1249774.761 + + + 2645564.119 + 1249788.217 + + + 2645584.822 + 1249800.602 + + + 2645613.103 + 1249812.986 + + + 2645625.673 + 1249785.629 + + + 2645629.624 + 1249777.049 + + + 2645637.503 + 1249759.936 + + + + t1 + + + + + + 2645691.702 + 1249830.036 + + + 2645693.141 + 1249826.110 + + + 2645699.241 + 1249810.722 + + + 2645704.139 + 1249797.921 + + + + t1 + + + + + + 2645543.027 + 1249774.761 + + + 2645564.119 + 1249788.217 + + + 2645565.875 + 1249785.306 + + + 2645572.530 + 1249789.557 + + + 2645581.032 + 1249773.106 + + + + t2 + + + + + + 2645549.805 + 1249782.568 + + + 2645564.778 + 1249757.798 + + + + t3 + + + + + + 2645657.466 + 1249752.542 + + + 2645626.782 + 1249819.826 + + + 2645687.041 + 1249842.746 + + + 2645694.446 + 1249833.030 + + + 2645689.640 + 1249826.191 + + + 2645678.735 + 1249822.125 + + + + t2 + + + + + + 2645647.126 + 1249816.209 + + + 2645641.950 + 1249828.224 + + + 2645648.790 + 1249831.367 + + + 2645651.562 + 1249825.082 + + + 2645660.065 + 1249828.409 + + + 2645657.847 + 1249834.324 + + + + t3 + + + + + + 2645629.624 + 1249777.049 + + + 2645637.503 + 1249759.936 + + + 2645646.941 + 1249738.205 + + + 2645643.060 + 1249727.669 + + + + t3 + + + + From d46f9cc056f5e623254ef471b0690d0ec894db03 Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Tue, 19 Dec 2023 16:53:58 +0100 Subject: [PATCH 5/9] Use STRtree to filter line checks by envelopes --- .../functions/PolylinesOverlapIoxPlugin.java | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java index 48605e9..ca0f1ba 100644 --- a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java +++ b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java @@ -7,10 +7,13 @@ import ch.interlis.iox.IoxException; import ch.interlis.iox_j.jts.Iox2jtsext; import ch.interlis.iox_j.validator.Value; +import com.vividsolutions.jts.geom.IntersectionMatrix; +import com.vividsolutions.jts.index.strtree.STRtree; import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; public final class PolylinesOverlapIoxPlugin extends BaseInterlisFunction { @@ -66,14 +69,30 @@ private List convertToJTSLines(Collection polylines) { } 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; - } + if (lines.size() <= 1) { + return false; + } + + STRtree tree = new STRtree(lines.size()); + for (CompoundCurve line : lines) { + tree.insert(line.getEnvelopeInternal(), line); + } + + AtomicBoolean hasOverlap = new AtomicBoolean(false); + for (CompoundCurve line : lines) { + if (hasOverlap.get()) { + break; } + tree.query(line.getEnvelopeInternal(), o -> { + if (!hasOverlap.get() && o != line && linesOverlap(line, (CompoundCurve) o)) { + hasOverlap.set(true); + } + }); } + return hasOverlap.get(); + } - return false; + private static boolean linesOverlap(CompoundCurve a, CompoundCurve b) { + return a.overlaps(b); } } From 3fafbb77103600571d4c0ca66e6bd48ede3108f3 Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Wed, 20 Dec 2023 10:31:02 +0100 Subject: [PATCH 6/9] Treat lines with equal parts as overlaps --- .../functions/PolylinesOverlapIoxPlugin.java | 14 ++- src/model/GeoW_FunctionsExt.ili | 4 +- .../PolylinesOverlap/PolylinesOverlap.ili | 1 + .../PolylinesOverlap/TestDataSameLine.xtf | 99 +++++++++++++++++++ .../PolylinesOverlapIoxPluginTest.java | 16 ++- 5 files changed, 126 insertions(+), 8 deletions(-) create mode 100644 src/test/data/PolylinesOverlap/TestDataSameLine.xtf diff --git a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java index ca0f1ba..1e1cc29 100644 --- a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java +++ b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java @@ -50,7 +50,7 @@ protected Value evaluateInternal(String validationKind, String usageScope, IomOb } List lines = convertToJTSLines(polylineObjects); - boolean hasOverlap = hasLineOverlap(lines); + boolean hasOverlap = hasEqualLinePart(lines); return new Value(hasOverlap); } @@ -68,7 +68,7 @@ private List convertToJTSLines(Collection polylines) { .collect(Collectors.toList()); } - private static boolean hasLineOverlap(List lines) { + private static boolean hasEqualLinePart(List lines) { if (lines.size() <= 1) { return false; } @@ -84,7 +84,7 @@ private static boolean hasLineOverlap(List lines) { break; } tree.query(line.getEnvelopeInternal(), o -> { - if (!hasOverlap.get() && o != line && linesOverlap(line, (CompoundCurve) o)) { + if (!hasOverlap.get() && o != line && linesHaveEqualPart(line, (CompoundCurve) o)) { hasOverlap.set(true); } }); @@ -92,7 +92,11 @@ private static boolean hasLineOverlap(List lines) { return hasOverlap.get(); } - private static boolean linesOverlap(CompoundCurve a, CompoundCurve b) { - return a.overlaps(b); + private static boolean linesHaveEqualPart(CompoundCurve a, CompoundCurve b) { + IntersectionMatrix relation = a.relate(b); + + // If the intersection of the interiors is a line, they have at least one part of a section in common + int interiorIntersection = relation.get(0, 0); + return interiorIntersection == 1; } } diff --git a/src/model/GeoW_FunctionsExt.ili b/src/model/GeoW_FunctionsExt.ili index f35c8c2..d34b8d4 100644 --- a/src/model/GeoW_FunctionsExt.ili +++ b/src/model/GeoW_FunctionsExt.ili @@ -40,9 +40,9 @@ MODEL GeoW_FunctionsExt !!@ fn.since = "2023-12-13"; FUNCTION Union (Geometries: ANYSTRUCTURE): MULTIAREA; - !!@ fn.description = "Prüft, ob sich die Linien-Geometrien ü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.description = "Prüft, ob sich die Linien-Geometrien überlappen oder eine gemeinsame Teilstrecke vorhanden ist (wenn die Schnittmenge der Innenbereiche einer Linie entspricht). 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 überlappen"; + !!@ fn.return = "TRUE, wenn sich zwei Linien überlappen oder zwischen zwei Linien eine gemeinsame Teilstrecke vorhanden ist"; !!@ fn.since = "2023-12-18"; FUNCTION PolylinesOverlap (Objects: OBJECTS OF ANYCLASS; LineAttr: TEXT): BOOLEAN; END GeoW_FunctionsExt. \ No newline at end of file diff --git a/src/test/data/PolylinesOverlap/PolylinesOverlap.ili b/src/test/data/PolylinesOverlap/PolylinesOverlap.ili index 12e1f3d..76435c9 100644 --- a/src/test/data/PolylinesOverlap/PolylinesOverlap.ili +++ b/src/test/data/PolylinesOverlap/PolylinesOverlap.ili @@ -20,6 +20,7 @@ MODEL TestSuite SET CONSTRAINT setConstraintT1 : WHERE type == #t1 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); SET CONSTRAINT setConstraintT2 : WHERE type == #t2 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); SET CONSTRAINT setConstraintT3 : WHERE type == #t3 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); + SET CONSTRAINT setConstraintT1or2 : WHERE type == #t1 OR type == #t2 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); SET CONSTRAINT setConstraintT2or3 : WHERE type == #t2 OR type == #t3 : GeoW_FunctionsExt.PolylinesOverlap(ALL, "geometry"); END TestClass; diff --git a/src/test/data/PolylinesOverlap/TestDataSameLine.xtf b/src/test/data/PolylinesOverlap/TestDataSameLine.xtf new file mode 100644 index 0000000..651db27 --- /dev/null +++ b/src/test/data/PolylinesOverlap/TestDataSameLine.xtf @@ -0,0 +1,99 @@ + + + + + TestSuite + + ili2gpkg-5.0.0-20f2bb62307ba6329a125fc6f10965ad9a4e6300 + + + + + + + + 2645626.782 + 1249819.826 + + + 2645687.041 + 1249842.746 + + + + t1 + + + + + + 2645549.805 + 1249782.568 + + + 2645564.778 + 1249757.798 + + + + t1 + + + + + + 2645657.466 + 1249752.542 + + + 2645626.782 + 1249819.826 + + + 2645687.041 + 1249842.746 + + + 2645691.702 + 1249830.036 + + + 2645693.141 + 1249826.110 + + + + t2 + + + + + + 2645657.466 + 1249752.542 + + + 2645626.782 + 1249819.826 + + + 2645687.041 + 1249842.746 + + + 2645691.702 + 1249830.036 + + + 2645693.141 + 1249826.110 + + + + t3 + + + + diff --git a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPluginTest.java b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPluginTest.java index 0d7ba82..f6ee501 100644 --- a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPluginTest.java +++ b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPluginTest.java @@ -8,6 +8,7 @@ public final class PolylinesOverlapIoxPluginTest { private static final String TEST_DATA = "PolylinesOverlap/TestData.xtf"; + private static final String TEST_DATA_SAME_LINE = "PolylinesOverlap/TestDataSameLine.xtf"; private ValidationTestHelper vh = null; @BeforeEach @@ -21,9 +22,22 @@ void polylinesOverlap() throws Ili2cFailure, IoxException { vh.runValidation(new String[]{TEST_DATA}, new String[]{"PolylinesOverlap/PolylinesOverlap.ili"}); Assert.equals(4, vh.getErrs().size()); AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintAllNoOverlaps"); - AssertionHelper.assertNoConstraintError(vh, "setConstraintT1"); + AssertionHelper.assertNoConstraintError(vh, "setConstraintT1"); // Some lines in T1 overlap AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT2"); AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT3"); + AssertionHelper.assertNoConstraintError(vh, "setConstraintT1or2"); AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT2or3"); } + + @Test + void sameLineOverlaps() throws Ili2cFailure, IoxException { + vh.runValidation(new String[]{TEST_DATA_SAME_LINE}, new String[]{"PolylinesOverlap/PolylinesOverlap.ili"}); + Assert.equals(4, vh.getErrs().size()); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintAllNoOverlaps"); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT1"); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT2"); + AssertionHelper.assertConstraintErrors(vh, 1, "setConstraintT3"); + AssertionHelper.assertNoConstraintError(vh, "setConstraintT1or2"); // One line in T1 is a segment of T2 + AssertionHelper.assertNoConstraintError(vh, "setConstraintT2or3"); // The lines in T2 and T3 are equal + } } From af843e9721609be991c6b38249bf700836849697 Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Wed, 20 Dec 2023 11:36:59 +0100 Subject: [PATCH 7/9] Cache result of PolylinesOverlap based on object ids --- .../functions/PolylinesOverlapIoxPlugin.java | 47 ++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java index 1e1cc29..4bd7c7a 100644 --- a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java +++ b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java @@ -11,12 +11,16 @@ import com.vividsolutions.jts.index.strtree.STRtree; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; public final class PolylinesOverlapIoxPlugin extends BaseInterlisFunction { + private static final Map HAS_EQUAL_LINE_PART_CACHE = new HashMap<>(); + @Override public String getQualifiedIliName() { return "GeoW_FunctionsExt.PolylinesOverlap"; @@ -49,8 +53,20 @@ protected Value evaluateInternal(String validationKind, String usageScope, IomOb polylineObjects = EvaluationHelper.evaluateAttributes(validator, argObjects, attributePath); } - List lines = convertToJTSLines(polylineObjects); - boolean hasOverlap = hasEqualLinePart(lines); + Collection inputObjects = argObjects.getComplexObjects(); + boolean hasObjectIds = inputObjects.stream().anyMatch(o -> o.getobjectoid() != null); + if (!hasObjectIds) { + List lines = convertToJTSLines(polylineObjects); + return new Value(hasEqualLinePart(lines)); + } + + List objectIds = inputObjects.stream().map(IomObject::getobjectoid).collect(Collectors.toList()); + HasEqualLinePartKey key = new HasEqualLinePartKey(objectIds, argPath.isUndefined() ? null : argPath.getValue()); + + boolean hasOverlap = HAS_EQUAL_LINE_PART_CACHE.computeIfAbsent(key, k -> { + List lines = convertToJTSLines(polylineObjects); + return hasEqualLinePart(lines); + }); return new Value(hasOverlap); } @@ -99,4 +115,31 @@ private static boolean linesHaveEqualPart(CompoundCurve a, CompoundCurve b) { int interiorIntersection = relation.get(0, 0); return interiorIntersection == 1; } + + private static final class HasEqualLinePartKey { + private final List objectIds; + private final String attributeName; + + HasEqualLinePartKey(List objectIds, String attributeName) { + this.objectIds = objectIds; + this.attributeName = attributeName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + HasEqualLinePartKey that = (HasEqualLinePartKey) o; + return Objects.equals(objectIds, that.objectIds) && Objects.equals(attributeName, that.attributeName); + } + + @Override + public int hashCode() { + return Objects.hash(objectIds, attributeName); + } + } } From f5e7d6d9732c7a19a74b1c6a0d51006368744f7b Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Wed, 20 Dec 2023 15:46:02 +0100 Subject: [PATCH 8/9] Use default node capacity for STRtree --- .../extensions/functions/PolylinesOverlapIoxPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java index 4bd7c7a..f94a6c4 100644 --- a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java +++ b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java @@ -89,7 +89,7 @@ private static boolean hasEqualLinePart(List lines) { return false; } - STRtree tree = new STRtree(lines.size()); + STRtree tree = new STRtree(); for (CompoundCurve line : lines) { tree.insert(line.getEnvelopeInternal(), line); } From 7e21c701cd50ded94a29936c07ee6e9a170725da Mon Sep 17 00:00:00 2001 From: Dominic Burger Date: Wed, 20 Dec 2023 15:49:37 +0100 Subject: [PATCH 9/9] Check that all objects have an id for PolylinesOverlap cache --- .../extensions/functions/PolylinesOverlapIoxPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java index f94a6c4..9d7f2e0 100644 --- a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java +++ b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java @@ -54,13 +54,13 @@ protected Value evaluateInternal(String validationKind, String usageScope, IomOb } Collection inputObjects = argObjects.getComplexObjects(); - boolean hasObjectIds = inputObjects.stream().anyMatch(o -> o.getobjectoid() != null); + List objectIds = inputObjects.stream().map(IomObject::getobjectoid).collect(Collectors.toList()); + boolean hasObjectIds = objectIds.stream().allMatch(Objects::nonNull); if (!hasObjectIds) { List lines = convertToJTSLines(polylineObjects); return new Value(hasEqualLinePart(lines)); } - List objectIds = inputObjects.stream().map(IomObject::getobjectoid).collect(Collectors.toList()); HasEqualLinePartKey key = new HasEqualLinePartKey(objectIds, argPath.isUndefined() ? null : argPath.getValue()); boolean hasOverlap = HAS_EQUAL_LINE_PART_CACHE.computeIfAbsent(key, k -> {