Skip to content

Commit

Permalink
Merge pull request #39 Add IsInside function
Browse files Browse the repository at this point in the history
  • Loading branch information
domi-b committed Jan 10, 2024
2 parents 04f4174 + 1ded8ec commit 5216593
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,28 +17,17 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public abstract class BaseIsInsideFunction extends BaseInterlisFunction {
private static final Map<ValidAreaKey, Geometry> VALID_AREA_CACHE = new HashMap<>();

/**
* Retrieve the geometries from referenced data according to the specified key.
*
* @param key The key that specifies which geometries to retrieve.
* @return The union of all found geometries.
*/
protected abstract Geometry getValidArea(ValidAreaKey key);

protected final Value isInsideValidArea(String usageScope, ValidAreaKey key, Collection<IomObject> testObjects, String geometryAttribute) {
protected final Value isInsideValidArea(String usageScope, Collection<IomObject> testObjects, String geometryAttribute, Supplier<Geometry> validAreaSupplier) {
try {
Geometry validArea = VALID_AREA_CACHE.computeIfAbsent(key, this::getValidArea);
Geometry validArea = validAreaSupplier.get();

if (validArea == null) {
return Value.createUndefined();
Expand Down Expand Up @@ -75,6 +64,10 @@ protected final <T> T logExceptionAsWarning(Callable<T> supplier) {
}
}

protected final void writeLogErrorMessage(String usageScope, String message) {
logger.addEvent(logger.logErrorMsg("{0}: Unable to evaluate {1}. {2}", usageScope, this.getQualifiedIliName(), message));
}

/**
* Convert the geometry {@link IomObject} to a JTS {@link Geometry}.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public final class IsInsideExternalDatasetIoxPlugin extends BaseIsInsideFunction
private static File dataDirectory;
private static Map<String, Set<URL>> modelFilesFromDataDirectory;
private static Map<String, Set<URL>> modelFilesFromJars;
private static final Map<ValidAreaKey, Geometry> VALID_AREA_CACHE = new HashMap<>();
private static final String QUALIFIED_ILI_NAME = "GeoW_FunctionsExt.IsInsideExternalDataset";

@Override
Expand All @@ -49,11 +50,16 @@ protected Value evaluateInternal(String validationKind, String usageScope, IomOb
ValidAreaKey key = new ValidAreaKey(null, datasetName, transferIds);
String testObjectGeometryAttribute = argTestObjectgeometry.getValue();

return isInsideValidArea(usageScope, key, argTestObject.getComplexObjects(), testObjectGeometryAttribute);
return isInsideValidArea(usageScope, argTestObject.getComplexObjects(), testObjectGeometryAttribute, () -> VALID_AREA_CACHE.computeIfAbsent(key, this::getValidArea));
}

@Override
protected Geometry getValidArea(ValidAreaKey key) {
/**
* Retrieve the geometries from referenced data according to the specified key.
*
* @param key The key that specifies which geometries to retrieve.
* @return The union of all found geometries.
*/
private Geometry getValidArea(ValidAreaKey key) {
String datasetName = key.getDatasetName();
int firstPoint = datasetName.indexOf('.');
int lastPoint = datasetName.lastIndexOf('.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public final class IsInsideExternalDatasetResourceIoxPlugin extends BaseIsInsideFunction {
private static final Map<ValidAreaKey, Geometry> VALID_AREA_CACHE = new HashMap<>();
private static final String QUALIFIED_ILI_NAME = "GeoW_FunctionsExt.IsInsideExternalDatasetResource";

@Override
Expand All @@ -37,11 +40,16 @@ protected Value evaluateInternal(String validationKind, String usageScope, IomOb
ValidAreaKey key = new ValidAreaKey(transferFile, datasetName, transferIds);
String testObjectGeometryAttribute = argTestObjectgeometry.getValue();

return isInsideValidArea(usageScope, key, argTestObject.getComplexObjects(), testObjectGeometryAttribute);
return isInsideValidArea(usageScope, argTestObject.getComplexObjects(), testObjectGeometryAttribute, () -> VALID_AREA_CACHE.computeIfAbsent(key, this::getValidArea));
}

@Override
protected Geometry getValidArea(ValidAreaKey key) {
/**
* Retrieve the geometries from referenced data according to the specified key.
*
* @param key The key that specifies which geometries to retrieve.
* @return The union of all found geometries.
*/
private Geometry getValidArea(ValidAreaKey key) {
String transferFile = key.getDataSource();
String datasetName = key.getDatasetName();
int lastPoint = datasetName.lastIndexOf('.');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package ch.geowerkstatt.ilivalidator.extensions.functions;

import ch.ehi.basics.types.OutParam;
import ch.interlis.iom.IomObject;
import ch.interlis.iox.IoxException;
import ch.interlis.iox_j.jts.Iox2jtsext;
import ch.interlis.iox_j.validator.Value;
import com.vividsolutions.jts.geom.Geometry;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

public final class IsInsideIoxPlugin extends BaseIsInsideFunction {
private static final Map<IomObject, Geometry> VALID_AREA_CACHE = new HashMap<>();
private static final String QUALIFIED_ILI_NAME = "GeoW_FunctionsExt.IsInside";

@Override
public String getQualifiedIliName() {
return QUALIFIED_ILI_NAME;
}

@Override
protected Value evaluateInternal(String validationKind, String usageScope, IomObject mainObj, Value[] arguments) {
Value argReferenceGeometry = arguments[0]; // MULTIAREA
Value argTestObject = arguments[1]; // OBJECT OF ANYCLASS
Value argTestObjectgeometry = arguments[2]; // TEXT

if (argTestObject.isUndefined() || argTestObjectgeometry.isUndefined()) {
return Value.createSkipEvaluation();
}
if (argReferenceGeometry.isUndefined()) {
writeLogErrorMessage(usageScope, "Missing reference geometry.");
return Value.createUndefined();
}

Collection<IomObject> referenceGeometryObjects = argReferenceGeometry.getComplexObjects();
if (referenceGeometryObjects.size() != 1) {
writeLogErrorMessage(usageScope, "Expected exactly one reference geometry.");
return Value.createUndefined();
}

IomObject referenceGeometry = referenceGeometryObjects.iterator().next();
String testObjectGeometryAttribute = argTestObjectgeometry.getValue();

return isInsideValidArea(usageScope, argTestObject.getComplexObjects(), testObjectGeometryAttribute, () -> VALID_AREA_CACHE.computeIfAbsent(referenceGeometry, this::getValidArea));
}

private Geometry getValidArea(IomObject referenceGeometry) {
try {
return Iox2jtsext.multisurface2JTS(referenceGeometry, 0.0, new OutParam<>(), logger, 0.0, "warning");
} catch (IoxException e) {
throw new IllegalStateException(e);
}
}
}
6 changes: 6 additions & 0 deletions src/model/GeoW_FunctionsExt.ili
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ MODEL GeoW_FunctionsExt
!!@ fn.since = "2023-12-20";
FUNCTION IsInsideExternalDatasetResource (TransferFile: TEXT; DatasetName: TEXT; Objects: TEXT; TestObject: OBJECT OF ANYCLASS; TestObjectgeometry: TEXT): BOOLEAN;

!!@ fn.description = "Prüft, ob ein Objekt innerhalb der Geometrie eines anderen Objektes liegt.";
!!@ fn.param = "ReferenceGeometry: Referenzgeometrie, innerhalb welcher das TestObject liegen muss. TestObject: Objekt, welches zu prüfen ist. TestObjectgeometry: Geometriefeld, bezogen auf das unter Testobject übergebene Objekt";
!!@ fn.return = "Boolean";
!!@ fn.since = "2023-12-21";
FUNCTION IsInside (ReferenceGeometry: MULTIAREA; TestObject: OBJECT OF ANYCLASS; TestObjectgeometry: TEXT): BOOLEAN;

!!@ fn.description = "Fasst die Flächen-Geometrien aus der Eingabemenge zu einer Flächen-Geometrie zusammen. Für 'Geometries' können nur Geometrien angegeben werden.";
!!@ fn.param = "Geometries: Geometrien, die zusammengefasst werden sollen";
!!@ fn.return = "Zusammengefasste Flächen-Geometrie";
Expand Down
33 changes: 33 additions & 0 deletions src/test/data/IsInside/MandatoryConstraintThis.ili
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
INTERLIS 2.4;

MODEL TestSuite
AT "mailto:[email protected]" VERSION "2023-12-21" =
IMPORTS GeoW_FunctionsExt;

DOMAIN
!!@CRS=EPSG:2056
CHKoord = COORD 2460000.000 .. 2870000.000 [INTERLIS.m],
1045000.000 .. 1310000.000 [INTERLIS.m],
ROTATION 2 -> 1;
CHMultiArea = MULTIAREA WITH (STRAIGHTS) VERTEX CHKoord WITHOUT OVERLAPS > 0.001;

TOPIC FunctionTestTopic =
CLASS ConstraintTestClass (ABSTRACT) =
expected : MANDATORY BOOLEAN;
END ConstraintTestClass;

CLASS IsInsideKantonsgrenze EXTENDS ConstraintTestClass =
testAttributeIsInsideKantonsgrenze : CHKoord;
testAttributeKantonsgrenze : CHMultiArea;
MANDATORY CONSTRAINT IsInsideKantonsgrenze: GeoW_FunctionsExt.IsInside(THIS->testAttributeKantonsgrenze, THIS, "testAttributeIsInsideKantonsgrenze") == expected;
END IsInsideKantonsgrenze;

CLASS InvalidConstraints =
geometryAttribute : CHKoord;
area : CHMultiArea;
MANDATORY CONSTRAINT IsInsideMissingArea: GeoW_FunctionsExt.IsInside(THIS->area, THIS, "geometryAttribute");
END InvalidConstraints;

END FunctionTestTopic;

END TestSuite.
102 changes: 102 additions & 0 deletions src/test/data/IsInside/TestData.xtf
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?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:TestSuite="http://www.interlis.ch/xtf/2.4/TestSuite">
<ili:headersection>
<ili:models>
<ili:model>GeoW_FunctionsExt</ili:model>
<ili:model>TestSuite</ili:model>
</ili:models>
<ili:sender>ili2gpkg-4.6.1-63db90def1260a503f0f2d4cb846686cd4851184</ili:sender>
</ili:headersection>
<ili:datasection>
<TestSuite:FunctionTestTopic ili:bid="TestSuite.FunctionTestTopic">
<TestSuite:InvalidConstraints ili:tid="1">
<TestSuite:geometryAttribute>
<geom:coord>
<geom:c1>2533306.953</geom:c1>
<geom:c2>1184710.792</geom:c2>
</geom:coord>
</TestSuite:geometryAttribute>
<!-- Kein area-Attribut, um fehlende Fläche zu testen -->
</TestSuite:InvalidConstraints>
<TestSuite:IsInsideKantonsgrenze ili:tid="2"> <!-- Erwartet: 2D-Punkt: liegt im Kt. SO -->
<TestSuite:expected>true</TestSuite:expected>
<TestSuite:testAttributeIsInsideKantonsgrenze>
<geom:coord>
<geom:c1>2620570.500</geom:c1>
<geom:c2>1237151.400</geom:c2>
</geom:coord>
</TestSuite:testAttributeIsInsideKantonsgrenze>
<TestSuite:testAttributeKantonsgrenze>
<geom:multisurface>
<geom:surface>
<geom:exterior>
<geom:polyline>
<geom:coord>
<geom:c1>2591994.000</geom:c1>
<geom:c2>1262353.000</geom:c2>
</geom:coord>
<geom:coord>
<geom:c1>2645900.000</geom:c1>
<geom:c2>1262353.000</geom:c2>
</geom:coord>
<geom:coord>
<geom:c1>2645900.000</geom:c1>
<geom:c2>1211927.000</geom:c2>
</geom:coord>
<geom:coord>
<geom:c1>2591994.000</geom:c1>
<geom:c2>1211927.000</geom:c2>
</geom:coord>
<geom:coord>
<geom:c1>2591994.000</geom:c1>
<geom:c2>1262353.000</geom:c2>
</geom:coord>
</geom:polyline>
</geom:exterior>
</geom:surface>
</geom:multisurface>
</TestSuite:testAttributeKantonsgrenze>
</TestSuite:IsInsideKantonsgrenze>
<TestSuite:IsInsideKantonsgrenze ili:tid="3"> <!-- Erwartet: 2D-Punkt: liegt ausserhalb, c1 der Fläche verschoben -->
<TestSuite:expected>false</TestSuite:expected>
<TestSuite:testAttributeIsInsideKantonsgrenze>
<geom:coord>
<geom:c1>2620570.500</geom:c1>
<geom:c2>1237151.400</geom:c2>
</geom:coord>
</TestSuite:testAttributeIsInsideKantonsgrenze>
<TestSuite:testAttributeKantonsgrenze>
<geom:multisurface>
<geom:surface>
<geom:exterior>
<geom:polyline>
<geom:coord>
<geom:c1>2691994.000</geom:c1>
<geom:c2>1262353.000</geom:c2>
</geom:coord>
<geom:coord>
<geom:c1>2745900.000</geom:c1>
<geom:c2>1262353.000</geom:c2>
</geom:coord>
<geom:coord>
<geom:c1>2745900.000</geom:c1>
<geom:c2>1211927.000</geom:c2>
</geom:coord>
<geom:coord>
<geom:c1>2691994.000</geom:c1>
<geom:c2>1211927.000</geom:c2>
</geom:coord>
<geom:coord>
<geom:c1>2691994.000</geom:c1>
<geom:c2>1262353.000</geom:c2>
</geom:coord>
</geom:polyline>
</geom:exterior>
</geom:surface>
</geom:multisurface>
</TestSuite:testAttributeKantonsgrenze>
</TestSuite:IsInsideKantonsgrenze>
</TestSuite:FunctionTestTopic>
</ili:datasection>
</ili:transfer>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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 IsInsideIoxPluginTest {
private static final String TEST_DATA = "IsInside/TestData.xtf";
private ValidationTestHelper vh;

@BeforeEach
void setUp() {
vh = new ValidationTestHelper();
vh.addFunction(new IsInsideIoxPlugin());
}

@Test
void isInside() throws Ili2cFailure, IoxException {
vh.runValidation(new String[]{TEST_DATA}, new String[]{"IsInside/MandatoryConstraintThis.ili"});

Assert.equals(2, vh.getErrs().size());
AssertionHelper.assertLogEventsContainMessage(vh.getErrs(), "TestSuite.FunctionTestTopic.InvalidConstraints.IsInsideMissingArea: Unable to evaluate GeoW_FunctionsExt.IsInside. Missing reference geometry.");
AssertionHelper.assertLogEventsContainMessage(vh.getErrs(), "Mandatory Constraint TestSuite.FunctionTestTopic.InvalidConstraints.IsInsideMissingArea is not true.");
}
}

0 comments on commit 5216593

Please sign in to comment.