-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #36 Add PolylinesOverlap function
- Loading branch information
Showing
6 changed files
with
553 additions
and
0 deletions.
There are no files selected for viewing
145 changes: 145 additions & 0 deletions
145
...ain/java/ch/geowerkstatt/ilivalidator/extensions/functions/PolylinesOverlapIoxPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
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 com.vividsolutions.jts.geom.IntersectionMatrix; | ||
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<HasEqualLinePartKey, Boolean> HAS_EQUAL_LINE_PART_CACHE = new HashMap<>(); | ||
|
||
@Override | ||
public String getQualifiedIliName() { | ||
return "GeoW_FunctionsExt.PolylinesOverlap"; | ||
} | ||
|
||
@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); | ||
} | ||
|
||
Collection<IomObject> inputObjects = argObjects.getComplexObjects(); | ||
List<String> objectIds = inputObjects.stream().map(IomObject::getobjectoid).collect(Collectors.toList()); | ||
boolean hasObjectIds = objectIds.stream().allMatch(Objects::nonNull); | ||
if (!hasObjectIds) { | ||
List<CompoundCurve> lines = convertToJTSLines(polylineObjects); | ||
return new Value(hasEqualLinePart(lines)); | ||
} | ||
|
||
HasEqualLinePartKey key = new HasEqualLinePartKey(objectIds, argPath.isUndefined() ? null : argPath.getValue()); | ||
|
||
boolean hasOverlap = HAS_EQUAL_LINE_PART_CACHE.computeIfAbsent(key, k -> { | ||
List<CompoundCurve> lines = convertToJTSLines(polylineObjects); | ||
return hasEqualLinePart(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 hasEqualLinePart(List<CompoundCurve> lines) { | ||
if (lines.size() <= 1) { | ||
return false; | ||
} | ||
|
||
STRtree tree = new STRtree(); | ||
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 && linesHaveEqualPart(line, (CompoundCurve) o)) { | ||
hasOverlap.set(true); | ||
} | ||
}); | ||
} | ||
return hasOverlap.get(); | ||
} | ||
|
||
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; | ||
} | ||
|
||
private static final class HasEqualLinePartKey { | ||
private final List<String> objectIds; | ||
private final String attributeName; | ||
|
||
HasEqualLinePartKey(List<String> 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); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
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 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 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; | ||
|
||
END FunctionTestTopic; | ||
|
||
END TestSuite. |
Oops, something went wrong.