diff --git a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/IsInsideAreaByCodeIoxPlugin.java b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/IsInsideAreaByCodeIoxPlugin.java index 230d755..d6fbd1e 100644 --- a/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/IsInsideAreaByCodeIoxPlugin.java +++ b/src/main/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/IsInsideAreaByCodeIoxPlugin.java @@ -9,6 +9,7 @@ import ch.interlis.iom.IomObject; import ch.interlis.iox_j.jts.Iox2jtsext; import ch.interlis.iox_j.validator.Value; +import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; @@ -70,7 +71,7 @@ private Value isInsideArea(String usageScope, Collection objects, Pat Geometry::union )); - List sortedGeometries; + List> sortedGeometries; ValueKey firstKey = geometriesByCodeValue.keySet().iterator().next(); Type keyType = firstKey.getType(); @@ -88,33 +89,48 @@ private Value isInsideArea(String usageScope, Collection objects, Pat return Value.createSkipEvaluation(); } + boolean result = true; for (int i = 0; i < sortedGeometries.size() - 1; i++) { - Geometry current = sortedGeometries.get(i); - Geometry next = sortedGeometries.get(i + 1); - - if (!next.contains(current)) { - return new Value(false); + Map.Entry current = sortedGeometries.get(i); + Map.Entry next = sortedGeometries.get(i + 1); + + if (!next.getValue().contains(current.getValue())) { + Geometry offendingGeometry = current.getValue().difference(next.getValue()); + Coordinate position = offendingGeometry.getCoordinate(); + String offendingEnvelopeWkt = offendingGeometry.getEnvelope().toText(); + + String currentCode = current.getKey().getStringValue(); + String nextCode = next.getKey().getStringValue(); + + logger.addEvent(logger.logErrorMsg( + "IsInsideAreaByCode found an invalid overlap between code '{0}' and '{1}'. The offending geometry is inside this envelope: {2}", + position.x, + position.y, + position.z, + currentCode, + nextCode, + offendingEnvelopeWkt)); + + result = false; } } - return new Value(true); + return new Value(result); } - private List sortByEnumValues(Map map, EnumerationType enumType) { + private List> sortByEnumValues(Map map, EnumerationType enumType) { List enumValues = enumType.getValues(); return map.entrySet() .stream() .sorted(Comparator.comparingInt(entry -> enumValues.indexOf(entry.getKey().getStringValue()))) - .map(Map.Entry::getValue) .collect(Collectors.toList()); } - private List sortByNumericValues(Map map) { + private List> sortByNumericValues(Map map) { return map.entrySet() .stream() .sorted(Comparator.comparingDouble(entry -> entry.getKey().getNumericValue())) - .map(Map.Entry::getValue) .collect(Collectors.toList()); } diff --git a/src/test/data/IsInsideAreaByCode/SetConstraints.ili b/src/test/data/IsInsideAreaByCode/SetConstraints.ili index ab39039..1c6fdd9 100644 --- a/src/test/data/IsInsideAreaByCode/SetConstraints.ili +++ b/src/test/data/IsInsideAreaByCode/SetConstraints.ili @@ -25,6 +25,7 @@ MODEL TestSuite codeNumeric : CodeNumeric; surface : SURFACE WITH (STRAIGHTS) VERTEX CHKoord WITHOUT OVERLAPS > 0.001; + !!@ ilivalid.msg = "Custom message." SET CONSTRAINT insideAreaConstraintEnum: NGK_SO_FunctionsExt.IsInsideAreaByCode(ALL, "surface", "codeEnum"); SET CONSTRAINT insideAreaConstraintNumeric: NGK_SO_FunctionsExt.IsInsideAreaByCode(ALL, "surface", "codeNumeric"); END BaseClass; diff --git a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/AssertionHelper.java b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/AssertionHelper.java index 00ff0dc..5c0a881 100644 --- a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/AssertionHelper.java +++ b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/AssertionHelper.java @@ -60,4 +60,19 @@ public static void assertLogEventsContainMessage(List logs, String fail(String.format("The logs are missing the message <%s>.", expectedMessageRegex)); } } + + /** + * Asserts that the logs contain the expected message regex a specific number of times. + * + * @param logs The logs to check. + * @param expectedMessageRegex The regex to match. + * @param expectedMatchCount The expected number of matches. + */ + public static void assertLogEventsMessages(List logs, String expectedMessageRegex, long expectedMatchCount) { + Pattern pattern = Pattern.compile(expectedMessageRegex); + long actualMatchCount = logs.stream().filter(log -> pattern.matcher(log.getEventMsg()).find()).count(); + if (actualMatchCount != expectedMatchCount) { + fail(String.format("Expected %d messages to match the regex <%s> but found %d.", expectedMatchCount, expectedMessageRegex, actualMatchCount)); + } + } } diff --git a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/IsInsideAreaByCodeIoxPluginTest.java b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/IsInsideAreaByCodeIoxPluginTest.java index cba2dd8..6f34ea1 100644 --- a/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/IsInsideAreaByCodeIoxPluginTest.java +++ b/src/test/java/ch/geowerkstatt/ilivalidator/extensions/functions/ngk/IsInsideAreaByCodeIoxPluginTest.java @@ -29,8 +29,13 @@ public void setConstraintOk() throws Ili2cFailure, IoxException { @Test public void setConstraintFail() throws Ili2cFailure, IoxException { vh.runValidation(new String[]{TEST_DATA_FAIL}, new String[]{ILI_FILE}); - Assert.equals(2, vh.getErrs().size()); - AssertionHelper.assertConstraintErrors(vh, 1, "insideAreaConstraintEnum"); + Assert.equals(9, vh.getErrs().size()); + + AssertionHelper.assertLogEventsMessages(vh.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code 'code_2' and 'code_3'", 1); + AssertionHelper.assertLogEventsMessages(vh.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code 'code_3' and 'code_4'", 1); + AssertionHelper.assertLogEventsMessages(vh.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code '22' and '33'", 1); + AssertionHelper.assertLogEventsMessages(vh.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code '33' and '44'", 1); + AssertionHelper.assertLogEventsMessages(vh.getErrs(), "^Custom message\\.$", 4); AssertionHelper.assertConstraintErrors(vh, 1, "insideAreaConstraintNumeric"); } }