Skip to content

Commit

Permalink
Handle case of empty offending geometry, improve error point
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickackermann committed Aug 22, 2024
1 parent 1c92390 commit 8c01e7c
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,21 @@
import ch.interlis.iom.IomObject;
import ch.interlis.iox_j.jts.Iox2jtsext;
import ch.interlis.iox_j.validator.Value;
import com.vividsolutions.jts.algorithm.InteriorPointArea;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;

import java.util.*;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

public final class IsInsideAreaByCodeIoxPlugin extends BaseInterlisFunction {
private static final Map<InsideAreaKey, Value> OBJECTS_CACHE = new HashMap<>();
private static GeometryFactory factory = new GeometryFactory();

@Override
public String getQualifiedIliName() {
Expand Down Expand Up @@ -89,19 +93,44 @@ private Value isInsideArea(String usageScope, Collection<IomObject> objects, Pat
Geometry current = sortedGeometries.get(i);
Geometry next = sortedGeometries.get(i + 1);

if (!next.contains(current)) {
if (!next.covers(current)) {
Geometry offendingGeometry = current.difference(next);
Point centroid = offendingGeometry.getCentroid();
String offendingCentroidWkt = centroid.toText();

logger.addEvent(logger.logErrorMsg(
"IsInsideAreaByCode found an invalid overlap between code '{0}' and '{1}'. The offending geometry has it's centroid at point: {2}",
centroid.getX(),
centroid.getY(),
null,
current.getUserData().toString(),
next.getUserData().toString(),
offendingCentroidWkt));
if (offendingGeometry.isEmpty()) {
// Buffer current geometry to get a nonempty geometry.
Geometry bufferedCurrent = current.buffer(0.01);
offendingGeometry = bufferedCurrent.difference(next);
if (offendingGeometry.isEmpty()) {
logger.addEvent(logger.logErrorMsg(
"IsInsideAreaByCode found a topological error between code '{0}' and '{1}'",
current.getUserData().toString(),
next.getUserData().toString()));
} else {
Geometry envelope = offendingGeometry.getEnvelope();
String envelopeWkt = envelope.toText();
Point errorPoint = envelope.getCentroid();

logger.addEvent(logger.logErrorMsg(
"IsInsideAreaByCode found a topological error (probably missing support point) between code '{0}' and '{1}'. The offending geometry is inside the envelope: {2}",
errorPoint.getX(),
errorPoint.getY(),
null,
current.getUserData().toString(),
next.getUserData().toString(),
envelopeWkt));
}
} else {
Coordinate errorPoint = new InteriorPointArea(offendingGeometry).getInteriorPoint();
String errorPointWkt = factory.createPoint(errorPoint).toText();

logger.addEvent(logger.logErrorMsg(
"IsInsideAreaByCode found an invalid overlap or topological error (missing support point) between code '{0}' and '{1}'. The offending geometry is near: {2}",
errorPoint.x,
errorPoint.y,
null,
current.getUserData().toString(),
next.getUserData().toString(),
errorPointWkt));
}

result = false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/data/IsInsideAreaByCode/SetConstraints.ili
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ MODEL TestSuite
1045000.000 .. 1310000.000 [INTERLIS.m],
ROTATION 2 -> 1;

Coord2 = COORD 0.0 .. 100.0,
0.0 .. 100.0;
Coord2 = COORD 0.000 .. 2870000.000,
0.000 .. 1310000.000;

CLASS BaseClass =
codeEnum : CodeEnum;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ public void setConstraintFail() throws Ili2cFailure, IoxException {
LogCollector logger = vh.runValidation(new String[]{TEST_DATA_FAIL}, new String[]{ILI_FILE});
Assert.equals(9, logger.getErrs().size());

AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code 'code_2' and 'code_3'", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code 'code_3' and 'code_4'", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code '22' and '33'", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code '33' and '44'", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap or topological error \\(missing support point\\) between code 'code_2' and 'code_3'", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap or topological error \\(missing support point\\) between code 'code_3' and 'code_4'", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap or topological error \\(missing support point\\) between code '22' and '33'", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap or topological error \\(missing support point\\) between code '33' and '44'", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^Custom message\\.$", 4);
AssertionHelper.assertConstraintErrors(logger, 1, "insideAreaConstraintNumeric");
}
Expand Down Expand Up @@ -111,7 +111,7 @@ public void multipleCodesWithSameOrderingOverlap() throws Ili2cFailure {

LogCollector logger = vh.runValidation(new String[]{ILI_FILE}, TOPIC, objects.stream().map(Supplier::get).toArray(IomObject[]::new));
AssertionHelper.assertEventMessagesAreEqual(logger.getErrs(),
"IsInsideAreaByCode found an invalid overlap between code 'code_blue_20, code_magenta_20' and 'code_30'. The offending geometry has it's centroid at point: POINT (30 65)",
"IsInsideAreaByCode found an invalid overlap or topological error (missing support point) between code 'code_blue_20, code_magenta_20' and 'code_30'. The offending geometry is near: POINT (30 65)",
"Set Constraint TestSuite.FunctionTestTopic.TestClass.insideAreaConstraint is not true.");
}

Expand Down Expand Up @@ -164,7 +164,7 @@ public void disjointInvalidPolygons() throws Ili2cFailure {

LogCollector logger = vh.runValidation(new String[]{ILI_FILE}, TOPIC, objects.stream().map(Supplier::get).toArray(IomObject[]::new));
AssertionHelper.assertEventMessagesAreEqual(logger.getErrs(),
"IsInsideAreaByCode found an invalid overlap between code 'code_10' and 'code_30'. The offending geometry has it's centroid at point: POINT (17.5 30)",
"IsInsideAreaByCode found an invalid overlap or topological error (missing support point) between code 'code_10' and 'code_30'. The offending geometry is near: POINT (17.5 30)",
"Set Constraint TestSuite.FunctionTestTopic.TestClass.insideAreaConstraint is not true.");
}

Expand Down Expand Up @@ -213,10 +213,10 @@ public void allCodesStackedOverlap() throws Ili2cFailure {

LogCollector logger = vh.runValidation(new String[]{ILI_FILE}, TOPIC, objects.stream().map(Supplier::get).toArray(IomObject[]::new));
AssertionHelper.assertEventMessagesAreEqual(logger.getErrs(),
"IsInsideAreaByCode found an invalid overlap between code 'code_10' and 'code_blue_20, code_magenta_20'. The offending geometry has it's centroid at point: POINT (85 50)",
"IsInsideAreaByCode found an invalid overlap between code 'code_blue_20, code_magenta_20' and 'code_30'. The offending geometry has it's centroid at point: POINT (70 50)",
"IsInsideAreaByCode found an invalid overlap between code 'code_30' and 'code_40'. The offending geometry has it's centroid at point: POINT (55 50)",
"IsInsideAreaByCode found an invalid overlap between code 'code_40' and 'code_noNumber15, code_, code_without_number'. The offending geometry has it's centroid at point: POINT (45 50)",
"IsInsideAreaByCode found an invalid overlap or topological error (missing support point) between code 'code_10' and 'code_blue_20, code_magenta_20'. The offending geometry is near: POINT (85 50)",
"IsInsideAreaByCode found an invalid overlap or topological error (missing support point) between code 'code_blue_20, code_magenta_20' and 'code_30'. The offending geometry is near: POINT (70 50)",
"IsInsideAreaByCode found an invalid overlap or topological error (missing support point) between code 'code_30' and 'code_40'. The offending geometry is near: POINT (55 50)",
"IsInsideAreaByCode found an invalid overlap or topological error (missing support point) between code 'code_40' and 'code_noNumber15, code_, code_without_number'. The offending geometry is near: POINT (45 50)",
"Set Constraint TestSuite.FunctionTestTopic.TestClass.insideAreaConstraint is not true.");
}

Expand Down Expand Up @@ -296,7 +296,7 @@ public void invalidOverlap() throws Ili2cFailure {

LogCollector logger = vh.runValidation(new String[]{ILI_FILE}, TOPIC, objects.stream().map(Supplier::get).toArray(IomObject[]::new));
AssertionHelper.assertEventMessagesAreEqual(logger.getErrs(),
"IsInsideAreaByCode found an invalid overlap between code 'code_10' and 'code_without_number'. The offending geometry has it's centroid at point: POINT (55 30)",
"IsInsideAreaByCode found an invalid overlap or topological error (missing support point) between code 'code_10' and 'code_without_number'. The offending geometry is near: POINT (55 30)",
"Set Constraint TestSuite.FunctionTestTopic.TestClass.insideAreaConstraint is not true.");
}

Expand Down Expand Up @@ -356,7 +356,38 @@ public void sharedArcSegmentDifferentMidPoint() throws Ili2cFailure {
// Because the arcs are stroked differently, thin overlaps occur
Assert.equals(2, logger.getErrs().size());

AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap between code 'code_10' and 'code_30'. The offending geometry has it's centroid at point: POINT \\(43.2\\d+ 42.1\\d+\\)$", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^IsInsideAreaByCode found an invalid overlap or topological error \\(missing support point\\) between code 'code_10' and 'code_30'. The offending geometry is near: POINT \\(15.8\\d+ 59.5\\d+\\)$", 1);
AssertionHelper.assertLogEventsMessages(logger.getErrs(), "^Set Constraint TestSuite.FunctionTestTopic.TestClass.insideAreaConstraint is not true.$", 1);
}

@Test
public void almostCollinearSegment() throws Ili2cFailure {
List<Supplier<IomObject>> objects = Arrays.asList(() -> {
IomObject object = new Iom_jObject(TEST_CLASS, "o1");
object.setattrvalue("code", "code_10");
object.addattrobj("surface", IomObjectHelper.createPolygonFromBoundaries(
IomObjectHelper.createBoundary(
IomObjectHelper.createCoord("2610068.000", "1252503.850"),
IomObjectHelper.createCoord("2610067.670", "1252503.910"),
IomObjectHelper.createCoord("2610067.650", "1252503.970"),
IomObjectHelper.createCoord("2610068.000", "1252503.850"))));
return object;
}, () -> {
IomObject object = new Iom_jObject(TEST_CLASS, "o2");
object.setattrvalue("code", "code_30");
object.addattrobj("surface", IomObjectHelper.createPolygonFromBoundaries(
IomObjectHelper.createBoundary(
IomObjectHelper.createCoord("2610070.000", "1252503.500"),
IomObjectHelper.createCoord("2610067.610", "1252503.370"),
IomObjectHelper.createCoord("2610067.670", "1252503.910"),
IomObjectHelper.createCoord("2610067.580", "1252504.180"),
IomObjectHelper.createCoord("2610070.000", "1252503.500"))));
return object;
});

LogCollector logger = vh.runValidation(new String[]{ILI_FILE}, TOPIC, objects.stream().map(Supplier::get).toArray(IomObject[]::new));
AssertionHelper.assertEventMessagesAreEqual(logger.getErrs(),
"IsInsideAreaByCode found a topological error (probably missing support point) between code 'code_10' and 'code_30'. The offending geometry is inside the envelope: POLYGON ((2610067.640033932 1252503.9000373208, 2610067.640033932 1252503.9794528584, 2610067.67 1252503.9794528584, 2610067.67 1252503.9000373208, 2610067.640033932 1252503.9000373208))",
"Set Constraint TestSuite.FunctionTestTopic.TestClass.insideAreaConstraint is not true.");
}
}

0 comments on commit 8c01e7c

Please sign in to comment.