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; ...@@ -26,6 +26,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing; import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.OtherConstructionObject; 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.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane; import de.hft.stuttgart.citydoctor2.math.OrthogonalRegressionPlane;
import de.hft.stuttgart.citydoctor2.math.Plane; import de.hft.stuttgart.citydoctor2.math.Plane;
...@@ -46,7 +47,8 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -46,7 +47,8 @@ public class MisalignedUtilityConnectionCheck extends Check {
private static final List<CheckId> dependencies; 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"; private static final String CONNECTION_SEARCH_RADIUS = "connectionSearchRadius";
static { static {
...@@ -61,7 +63,8 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -61,7 +63,8 @@ public class MisalignedUtilityConnectionCheck extends Check {
private double epsilon = Checks.MIN_VERTEX_DISTANCE_DEFAULT; private double epsilon = Checks.MIN_VERTEX_DISTANCE_DEFAULT;
private double searchRadius = 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; private CityObjectCache cache;
@Override @Override
...@@ -100,8 +103,8 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -100,8 +103,8 @@ public class MisalignedUtilityConnectionCheck extends Check {
if (localParameters.containsKey(CONNECTION_SEARCH_RADIUS)) { if (localParameters.containsKey(CONNECTION_SEARCH_RADIUS)) {
searchRadius = Double.parseDouble(localParameters.get(CONNECTION_SEARCH_RADIUS)); searchRadius = Double.parseDouble(localParameters.get(CONNECTION_SEARCH_RADIUS));
} }
if (localParameters.containsKey(ANGLE_THRESHOLD)) { if (localParameters.containsKey(SHALLOW_ANGLE_THRESHOLD)) {
angleThreshold = Double.parseDouble(localParameters.get(ANGLE_THRESHOLD)); shallownessThreshold = Double.parseDouble(localParameters.get(SHALLOW_ANGLE_THRESHOLD));
} }
String epsilonString = localParameters.get(GlobalParameters.MIN_VERTEX_DISTANCE); String epsilonString = localParameters.get(GlobalParameters.MIN_VERTEX_DISTANCE);
if (epsilonString != null) { if (epsilonString != null) {
...@@ -161,22 +164,15 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -161,22 +164,15 @@ public class MisalignedUtilityConnectionCheck extends Check {
.filter(isect -> isect.getType() != PolygonIntersection.IntersectionType.NONE) .filter(isect -> isect.getType() != PolygonIntersection.IntersectionType.NONE)
.toList(); .toList();
List<Pair<Vertex, Vertex>> pairs = getVertexPairs(sGeo, cGeo); List<Pair<Vertex, Vertex>> pairs = getVertexPairs(sGeo, cGeo);
if (pairs.size() < 3) { ConnectionAlignmentType alignment = analyseVertexPairConnectionAlignment(sGeo, cGeo, pairs, intersections);
if (!pairs.isEmpty()) {
subject.addCheckResult(new CheckResult(this,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignedConnectionError.MisalignmentType.HANGING, ErrorType.ERROR)));
}
} else {
ConnectionAlignmentType alignment = checkConnectionAlignment(sGeo, cGeo, pairs, intersections);
switch(alignment) { switch(alignment) {
case ALIGNED: case ALIGNED:
connected = true; connected = true;
break; break;
case ASYMMETRIC_ROLL: case BENT:
subject.addCheckResult(new CheckResult(this, subject.addCheckResult(new CheckResult(this,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(), ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignmentType.ASYMMETRIC_ROTATION, ErrorType.ERROR))); MisalignmentType.BENT, ErrorType.ERROR)));
connected = true; connected = true;
misalignment = true; misalignment = true;
break; break;
...@@ -188,14 +184,45 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -188,14 +184,45 @@ public class MisalignedUtilityConnectionCheck extends Check {
misalignment = true; misalignment = true;
break; break;
case UNKNOWN: case UNKNOWN:
subject.addCheckResult(new CheckResult(this,
ResultStatus.WARNING, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignmentType.UNKNOWN, ErrorType.WARNING)));
misalignment = true;
break; 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));
}
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) { if (misalignment) {
misalignedConnections = true; misalignedConnections = true;
...@@ -230,9 +257,13 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -230,9 +257,13 @@ public class MisalignedUtilityConnectionCheck extends Check {
return connectionPairs; return connectionPairs;
} }
private ConnectionAlignmentType checkConnectionAlignment(Geometry subject, Geometry candidate, private ConnectionAlignmentType analyseVertexPairConnectionAlignment(Geometry subject, Geometry candidate,
List<Pair<Vertex, Vertex>> pairs, List<PolygonIntersection> intersections) { List<Pair<Vertex, Vertex>> pairs, List<PolygonIntersection> intersections) {
if (pairs.isEmpty() || pairs.size() < 3){
return ConnectionAlignmentType.UNKNOWN;
}
ArrayList<Vertex> sPairVertices = new ArrayList<>(); ArrayList<Vertex> sPairVertices = new ArrayList<>();
ArrayList<Vertex> cPairVertices = new ArrayList<>(); ArrayList<Vertex> cPairVertices = new ArrayList<>();
pairs.forEach(pair -> { pairs.forEach(pair -> {
...@@ -261,22 +292,26 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -261,22 +292,26 @@ public class MisalignedUtilityConnectionCheck extends Check {
boolean candPlanar = isPlanarToPlane(cEdgeLoop, connectionPlane, epsilon); boolean candPlanar = isPlanarToPlane(cEdgeLoop, connectionPlane, epsilon);
if (subjPlanar && candPlanar) { if (subjPlanar && candPlanar) {
// --> Face-to-Face connection with missed vertices, check if centroids are within distance tolerance
return centroidsAligned ? ConnectionAlignmentType.ALIGNED : ConnectionAlignmentType.SHEARED; return centroidsAligned ? ConnectionAlignmentType.ALIGNED : ConnectionAlignmentType.SHEARED;
} }
Plane subjLocalPlane = OrthogonalRegressionPlane.calculateOrthogonalRegressionPlane(sEdgeLoop.size() >= 3 ? sEdgeLoop : sPairVertices); Plane subjLocalPlane = OrthogonalRegressionPlane.calculateOrthogonalRegressionPlane(sEdgeLoop.size() >= 3 ? sEdgeLoop : sPairVertices);
Plane candLocalPlane = OrthogonalRegressionPlane.calculateOrthogonalRegressionPlane(cEdgeLoop.size() >= 3 ? cEdgeLoop : cPairVertices); Plane candLocalPlane = OrthogonalRegressionPlane.calculateOrthogonalRegressionPlane(cEdgeLoop.size() >= 3 ? cEdgeLoop : cPairVertices);
if (subjLocalPlane == null || candLocalPlane == null) {
return ConnectionAlignmentType.UNKNOWN;
}
double angle = angleBetweenPlanes(subjLocalPlane, candLocalPlane); double angle = angleBetweenPlanes(subjLocalPlane, candLocalPlane);
if (angle > angleThreshold) { // --> Connection is at an angle, check if within angle tolerance for connections
return ConnectionAlignmentType.ASYMMETRIC_ROLL; if (angle > angleTolerance) {
return ConnectionAlignmentType.BENT;
} }
return centroidsAligned ? ConnectionAlignmentType.ALIGNED : ConnectionAlignmentType.SHEARED; 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) { private Plane deriveConnectionPlane(List<Vertex> sPairVerts, List<PolygonIntersection> intersections) {
// Calculate intersection plane // Calculate intersection plane
if (!intersections.isEmpty()) { 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