Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PolylinesOverlap function #36

Merged
merged 9 commits into from
Dec 20, 2023
Merged
Original file line number Diff line number Diff line change
@@ -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<IomObject> 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<CompoundCurve> lines = convertToJTSLines(polylineObjects);
boolean hasOverlap = hasLineOverlap(lines);
return new Value(!hasOverlap);
}

private List<CompoundCurve> convertToJTSLines(Collection<IomObject> 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<CompoundCurve> 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))) {
domi-b marked this conversation as resolved.
Show resolved Hide resolved
return true;
}
}
}

return false;
}
}
6 changes: 6 additions & 0 deletions src/model/GeoW_FunctionsExt.ili
Original file line number Diff line number Diff line change
Expand Up @@ -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.";
domi-b marked this conversation as resolved.
Show resolved Hide resolved
!!@ 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;
domi-b marked this conversation as resolved.
Show resolved Hide resolved
END GeoW_FunctionsExt.
28 changes: 28 additions & 0 deletions src/test/data/PolylinesNotOverlapping/PolylinesNotOverlapping.ili
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
INTERLIS 2.4;

MODEL TestSuite
AT "mailto:[email protected]" 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.
15 changes: 15 additions & 0 deletions src/test/data/PolylinesNotOverlapping/TestData.xtf
domi-b marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?><ili:transfer xmlns:ili="http://www.interlis.ch/xtf/2.4/INTERLIS" xmlns:geom="http://www.interlis.ch/geometry/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:TestSuite="http://www.interlis.ch/xtf/2.4/TestSuite">
<ili:headersection><ili:models><ili:model>TestSuite</ili:model></ili:models><ili:sender>ili2gpkg-5.0.0-20f2bb62307ba6329a125fc6f10965ad9a4e6300</ili:sender></ili:headersection>
<ili:datasection>
<TestSuite:FunctionTestTopic ili:bid="TestSuite.FunctionTestTopic">
<TestSuite:TestClass ili:tid="6a6902d8-61c0-4778-8733-ef81432e2665"><TestSuite:geometry><geom:polyline><geom:coord><geom:c1>2645657.466</geom:c1><geom:c2>1249752.542</geom:c2></geom:coord><geom:coord><geom:c1>2645626.782</geom:c1><geom:c2>1249819.826</geom:c2></geom:coord><geom:coord><geom:c1>2645687.041</geom:c1><geom:c2>1249842.746</geom:c2></geom:coord><geom:coord><geom:c1>2645691.702</geom:c1><geom:c2>1249830.036</geom:c2></geom:coord><geom:coord><geom:c1>2645693.141</geom:c1><geom:c2>1249826.110</geom:c2></geom:coord></geom:polyline></TestSuite:geometry><TestSuite:type>t1</TestSuite:type></TestSuite:TestClass>
<TestSuite:TestClass ili:tid="dc1c980a-598d-4c1e-8a9f-ab2f81b6a433"><TestSuite:geometry><geom:polyline><geom:coord><geom:c1>2645542.677</geom:c1><geom:c2>1249774.538</geom:c2></geom:coord><geom:coord><geom:c1>2645543.027</geom:c1><geom:c2>1249774.761</geom:c2></geom:coord><geom:coord><geom:c1>2645564.119</geom:c1><geom:c2>1249788.217</geom:c2></geom:coord><geom:coord><geom:c1>2645584.822</geom:c1><geom:c2>1249800.602</geom:c2></geom:coord><geom:coord><geom:c1>2645613.103</geom:c1><geom:c2>1249812.986</geom:c2></geom:coord><geom:coord><geom:c1>2645625.673</geom:c1><geom:c2>1249785.629</geom:c2></geom:coord><geom:coord><geom:c1>2645629.624</geom:c1><geom:c2>1249777.049</geom:c2></geom:coord><geom:coord><geom:c1>2645637.503</geom:c1><geom:c2>1249759.936</geom:c2></geom:coord></geom:polyline></TestSuite:geometry><TestSuite:type>t1</TestSuite:type></TestSuite:TestClass>
<TestSuite:TestClass ili:tid="be5879c9-9406-4d2f-9205-dbd4b85fc385"><TestSuite:geometry><geom:polyline><geom:coord><geom:c1>2645691.702</geom:c1><geom:c2>1249830.036</geom:c2></geom:coord><geom:coord><geom:c1>2645693.141</geom:c1><geom:c2>1249826.110</geom:c2></geom:coord><geom:coord><geom:c1>2645699.241</geom:c1><geom:c2>1249810.722</geom:c2></geom:coord><geom:coord><geom:c1>2645704.139</geom:c1><geom:c2>1249797.921</geom:c2></geom:coord></geom:polyline></TestSuite:geometry><TestSuite:type>t1</TestSuite:type></TestSuite:TestClass>
<TestSuite:TestClass ili:tid="1bf1e7c0-4f03-4823-a04b-106a6948cb7c"><TestSuite:geometry><geom:polyline><geom:coord><geom:c1>2645543.027</geom:c1><geom:c2>1249774.761</geom:c2></geom:coord><geom:coord><geom:c1>2645564.119</geom:c1><geom:c2>1249788.217</geom:c2></geom:coord><geom:coord><geom:c1>2645565.875</geom:c1><geom:c2>1249785.306</geom:c2></geom:coord><geom:coord><geom:c1>2645572.530</geom:c1><geom:c2>1249789.557</geom:c2></geom:coord><geom:coord><geom:c1>2645581.032</geom:c1><geom:c2>1249773.106</geom:c2></geom:coord></geom:polyline></TestSuite:geometry><TestSuite:type>t2</TestSuite:type></TestSuite:TestClass>
<TestSuite:TestClass ili:tid="15ff699c-b6a9-498e-a394-58af6ae0a8ef"><TestSuite:geometry><geom:polyline><geom:coord><geom:c1>2645549.805</geom:c1><geom:c2>1249782.568</geom:c2></geom:coord><geom:coord><geom:c1>2645564.778</geom:c1><geom:c2>1249757.798</geom:c2></geom:coord></geom:polyline></TestSuite:geometry><TestSuite:type>t3</TestSuite:type></TestSuite:TestClass>
<TestSuite:TestClass ili:tid="a9ee6314-d0bb-430c-834b-7c7ad60a9608"><TestSuite:geometry><geom:polyline><geom:coord><geom:c1>2645657.466</geom:c1><geom:c2>1249752.542</geom:c2></geom:coord><geom:coord><geom:c1>2645626.782</geom:c1><geom:c2>1249819.826</geom:c2></geom:coord><geom:coord><geom:c1>2645687.041</geom:c1><geom:c2>1249842.746</geom:c2></geom:coord><geom:coord><geom:c1>2645694.446</geom:c1><geom:c2>1249833.030</geom:c2></geom:coord><geom:coord><geom:c1>2645689.640</geom:c1><geom:c2>1249826.191</geom:c2></geom:coord><geom:coord><geom:c1>2645678.735</geom:c1><geom:c2>1249822.125</geom:c2></geom:coord></geom:polyline></TestSuite:geometry><TestSuite:type>t2</TestSuite:type></TestSuite:TestClass>
<TestSuite:TestClass ili:tid="371789bd-7793-4a7b-a80d-fe4154f4c503"><TestSuite:geometry><geom:polyline><geom:coord><geom:c1>2645647.126</geom:c1><geom:c2>1249816.209</geom:c2></geom:coord><geom:coord><geom:c1>2645641.950</geom:c1><geom:c2>1249828.224</geom:c2></geom:coord><geom:coord><geom:c1>2645648.790</geom:c1><geom:c2>1249831.367</geom:c2></geom:coord><geom:coord><geom:c1>2645651.562</geom:c1><geom:c2>1249825.082</geom:c2></geom:coord><geom:coord><geom:c1>2645660.065</geom:c1><geom:c2>1249828.409</geom:c2></geom:coord><geom:coord><geom:c1>2645657.847</geom:c1><geom:c2>1249834.324</geom:c2></geom:coord></geom:polyline></TestSuite:geometry><TestSuite:type>t3</TestSuite:type></TestSuite:TestClass>
<TestSuite:TestClass ili:tid="4a209413-5d36-4064-953a-582dea509387"><TestSuite:geometry><geom:polyline><geom:coord><geom:c1>2645629.624</geom:c1><geom:c2>1249777.049</geom:c2></geom:coord><geom:coord><geom:c1>2645637.503</geom:c1><geom:c2>1249759.936</geom:c2></geom:coord><geom:coord><geom:c1>2645646.941</geom:c1><geom:c2>1249738.205</geom:c2></geom:coord><geom:coord><geom:c1>2645643.060</geom:c1><geom:c2>1249727.669</geom:c2></geom:coord></geom:polyline></TestSuite:geometry><TestSuite:type>t3</TestSuite:type></TestSuite:TestClass>
</TestSuite:FunctionTestTopic>
</ili:datasection>
</ili:transfer>
Original file line number Diff line number Diff line change
@@ -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 {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: Wie ist das Verhalten bei zwei identischen Linien, wird das getestet?

Copy link
Contributor Author

@domi-b domi-b Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ich habe das Verhalten noch etwas angepasst, dass Linien mit gleichen Teilstrecken nun auch als Overlap gelten (es entspricht somit nicht mehr zu 100% der Definition von overlap, aber ich habe keinen passenderen Begriff gefunden).
Dabei wird geprüft, dass die Schnittmenge der Linien ebenfalls eine Linie ist (entspricht den Beispielen 29-47 im folgenden Bild).

https://onlinelibrary.wiley.com/cms/asset/22450c38-3698-4f48-bd9e-86c24819356a/tgis12328-fig-0004-m.jpg

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");
}
}
Loading