Commit 9fed0af9 authored by Luna Riegel's avatar Luna Riegel
Browse files

Refactor: Restructure misaligned utility check

parent 28af755c
......@@ -26,6 +26,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.OtherConstructionObject;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane;
import de.hft.stuttgart.citydoctor2.math.Plane;
......@@ -46,7 +47,8 @@ public class MisalignedUtilityConnectionCheck extends Check {
private static final List<CheckId> dependencies;
private static final String ANGLE_THRESHOLD = "angleThreshold";
private static final String SHALLOW_ANGLE_THRESHOLD = "connectionAngleShallownessThreshold";
private static final String CONNECTION_ANGLE_TOLERANCE = "connectionAngleTolerance";
private static final String CONNECTION_SEARCH_RADIUS = "connectionSearchRadius";
static {
......@@ -61,7 +63,8 @@ public class MisalignedUtilityConnectionCheck extends Check {
private double epsilon = Checks.MIN_VERTEX_DISTANCE_DEFAULT;
private double searchRadius = Checks.MIN_VERTEX_DISTANCE_DEFAULT;
private double angleThreshold = 30;
private double shallownessThreshold = 30;
private double angleTolerance = 3;
private CityObjectCache cache;
@Override
......@@ -100,8 +103,8 @@ public class MisalignedUtilityConnectionCheck extends Check {
if (localParameters.containsKey(CONNECTION_SEARCH_RADIUS)) {
searchRadius = Double.parseDouble(localParameters.get(CONNECTION_SEARCH_RADIUS));
}
if (localParameters.containsKey(ANGLE_THRESHOLD)) {
angleThreshold = Double.parseDouble(localParameters.get(ANGLE_THRESHOLD));
if (localParameters.containsKey(SHALLOW_ANGLE_THRESHOLD)) {
shallownessThreshold = Double.parseDouble(localParameters.get(SHALLOW_ANGLE_THRESHOLD));
}
String epsilonString = localParameters.get(GlobalParameters.MIN_VERTEX_DISTANCE);
if (epsilonString != null) {
......@@ -161,41 +164,65 @@ public class MisalignedUtilityConnectionCheck extends Check {
.filter(isect -> isect.getType() != PolygonIntersection.IntersectionType.NONE)
.toList();
List<Pair<Vertex, Vertex>> pairs = getVertexPairs(sGeo, cGeo);
if (pairs.size() < 3) {
if (!pairs.isEmpty()) {
ConnectionAlignmentType alignment = analyseVertexPairConnectionAlignment(sGeo, cGeo, pairs, intersections);
switch(alignment) {
case ALIGNED:
connected = true;
break;
case BENT:
subject.addCheckResult(new CheckResult(this,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignedConnectionError.MisalignmentType.HANGING, ErrorType.ERROR)));
MisalignmentType.BENT, ErrorType.ERROR)));
connected = true;
misalignment = true;
break;
case SHEARED:
subject.addCheckResult(new CheckResult(this,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignmentType.SHEARED, ErrorType.ERROR)));
connected = true;
misalignment = true;
break;
case UNKNOWN:
break;
default:
throw new IllegalStateException("Vertex pairs connection analysis returned illegal alignment type");
}
if(alignment == ConnectionAlignmentType.UNKNOWN && !intersections.isEmpty()){
List<Pair<Double, Double>> dihedralAngles = new ArrayList<>();
double intersectionLengthMax = 0d;
for(PolygonIntersection intersection : intersections){
Polygon polyA = intersection.getP1();
Polygon polyB = intersection.getP2();
Vector3d normalA = polyA.calculateNormal();
Vector3d normalB = polyB.calculateNormal();
// TODO: Möller-Trumbore intersection of polyA and polyB
double intersectionLength = 1d;
if (intersectionLengthMax < intersectionLength) {
intersectionLengthMax = intersectionLength;
}
double thetaN = Math.atan2(normalA.cross(normalB).getLength(), normalA.dot(normalB));
dihedralAngles.add(new Pair<>((180d-thetaN),intersectionLength));
}
} else {
ConnectionAlignmentType alignment = checkConnectionAlignment(sGeo, cGeo, pairs, intersections);
switch(alignment) {
case ALIGNED:
connected = true;
break;
case ASYMMETRIC_ROLL:
subject.addCheckResult(new CheckResult(this,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignmentType.ASYMMETRIC_ROTATION, ErrorType.ERROR)));
connected = true;
misalignment = true;
break;
case SHEARED:
subject.addCheckResult(new CheckResult(this,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignmentType.SHEARED, ErrorType.ERROR)));
connected = true;
misalignment = true;
break;
case UNKNOWN:
subject.addCheckResult(new CheckResult(this,
ResultStatus.WARNING, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignmentType.UNKNOWN, ErrorType.WARNING)));
misalignment = true;
break;
double finalIntersectionLengthMax = intersectionLengthMax;
double weightedSum = dihedralAngles.stream()
.mapToDouble(
pair->
(pair.getValue0() * pair.getValue1()) / finalIntersectionLengthMax)
.sum();
double mean = weightedSum / dihedralAngles.size();
double std = 0.0;
for (Pair<Double, Double> pair : dihedralAngles) {
std += Math.pow(pair.getValue0() - mean, 2);
}
} else if (alignment == ConnectionAlignmentType.UNKNOWN) {
//TODO: Check if sGeo is contained within the interior volumetric space of cGeo
}
}
if (misalignment) {
misalignedConnections = true;
......@@ -230,8 +257,12 @@ public class MisalignedUtilityConnectionCheck extends Check {
return connectionPairs;
}
private ConnectionAlignmentType checkConnectionAlignment(Geometry subject, Geometry candidate,
List<Pair<Vertex, Vertex>> pairs, List<PolygonIntersection> intersections) {
private ConnectionAlignmentType analyseVertexPairConnectionAlignment(Geometry subject, Geometry candidate,
List<Pair<Vertex, Vertex>> pairs, List<PolygonIntersection> intersections) {
if (pairs.isEmpty() || pairs.size() < 3){
return ConnectionAlignmentType.UNKNOWN;
}
ArrayList<Vertex> sPairVertices = new ArrayList<>();
ArrayList<Vertex> cPairVertices = new ArrayList<>();
......@@ -261,22 +292,26 @@ public class MisalignedUtilityConnectionCheck extends Check {
boolean candPlanar = isPlanarToPlane(cEdgeLoop, connectionPlane, epsilon);
if (subjPlanar && candPlanar) {
// --> Face-to-Face connection with missed vertices, check if centroids are within distance tolerance
return centroidsAligned ? ConnectionAlignmentType.ALIGNED : ConnectionAlignmentType.SHEARED;
}
Plane subjLocalPlane = OrthogonalRegressionPlane.calculateOrthogonalRegressionPlane(sEdgeLoop.size() >= 3 ? sEdgeLoop : sPairVertices);
Plane candLocalPlane = OrthogonalRegressionPlane.calculateOrthogonalRegressionPlane(cEdgeLoop.size() >= 3 ? cEdgeLoop : cPairVertices);
if (subjLocalPlane == null || candLocalPlane == null) {
return ConnectionAlignmentType.UNKNOWN;
}
double angle = angleBetweenPlanes(subjLocalPlane, candLocalPlane);
if (angle > angleThreshold) {
return ConnectionAlignmentType.ASYMMETRIC_ROLL;
// --> Connection is at an angle, check if within angle tolerance for connections
if (angle > angleTolerance) {
return ConnectionAlignmentType.BENT;
}
return centroidsAligned ? ConnectionAlignmentType.ALIGNED : ConnectionAlignmentType.SHEARED;
}
private ConnectionAlignmentType analyseIntersectionConnectionAlignment(Geometry subject, Geometry candidate,
List<PolygonIntersection> intersections) {
return ConnectionAlignmentType.UNKNOWN;
}
private Plane deriveConnectionPlane(List<Vertex> sPairVerts, List<PolygonIntersection> intersections) {
// Calculate intersection plane
if (!intersections.isEmpty()) {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment