Commit 15585c6e authored by Luna Riegel's avatar Luna Riegel
Browse files

Feat: Add closure surface fragmentation check

parent 3ff0eb11
Pipeline #12415 failed with stage
in 1 minute and 1 second
...@@ -56,6 +56,7 @@ public record CheckId(String name) implements Serializable { ...@@ -56,6 +56,7 @@ public record CheckId(String name) implements Serializable {
public static final CheckId C_GE_S_NOT_CLOSED = new CheckId("C_GE_S_NOT_CLOSED"); public static final CheckId C_GE_S_NOT_CLOSED = new CheckId("C_GE_S_NOT_CLOSED");
public static final CheckId C_GE_P_ORIENTATION_RINGS_SAME = new CheckId("C_GE_P_ORIENTATION_RINGS_SAME"); public static final CheckId C_GE_P_ORIENTATION_RINGS_SAME = new CheckId("C_GE_P_ORIENTATION_RINGS_SAME");
public static final CheckId C_SE_POLYGON_WITHOUT_SURFACE = new CheckId("C_SE_POLYGON_WITHOUT_SURFACE"); public static final CheckId C_SE_POLYGON_WITHOUT_SURFACE = new CheckId("C_SE_POLYGON_WITHOUT_SURFACE");
public static final CheckId C_SE_BS_FRAGMENTED_CLOSURE = new CheckId("C_SE_BS_FRAGMENTED_CLOSURE");
public static final CheckId C_TO_FEATURE_COLLISION = new CheckId("C_TO_FEATURE_COLLISION"); public static final CheckId C_TO_FEATURE_COLLISION = new CheckId("C_TO_FEATURE_COLLISION");
public static final CheckId C_TO_UTILITY_CONNECTION_MISALIGNED = new CheckId("C_TO_UTILITY_CONNECTION_MISALIGNED"); public static final CheckId C_TO_UTILITY_CONNECTION_MISALIGNED = new CheckId("C_TO_UTILITY_CONNECTION_MISALIGNED");
......
...@@ -64,6 +64,7 @@ public record ErrorId(String name) implements Serializable { ...@@ -64,6 +64,7 @@ public record ErrorId(String name) implements Serializable {
public static final ErrorId SE_BS_NOT_GROUND = new ErrorId("SE_BS_NOT_GROUND"); public static final ErrorId SE_BS_NOT_GROUND = new ErrorId("SE_BS_NOT_GROUND");
public static final ErrorId SE_SCHEMATRON_ERROR = new ErrorId("SE_SCHEMATRON_ERROR"); public static final ErrorId SE_SCHEMATRON_ERROR = new ErrorId("SE_SCHEMATRON_ERROR");
public static final ErrorId SE_BS_UNFRAGMENTED = new ErrorId("SE_BS_UNFRAGMENTED"); public static final ErrorId SE_BS_UNFRAGMENTED = new ErrorId("SE_BS_UNFRAGMENTED");
public static final ErrorId SE_BS_FRAGMENTED_CLOSURE = new ErrorId("SE_BS_FRAGMENTED_CLOSURE");
public static final ErrorId GE_P_DEGENERATED_RING = new ErrorId("GE_P_DEGENERATED_POLYGON"); public static final ErrorId GE_P_DEGENERATED_RING = new ErrorId("GE_P_DEGENERATED_POLYGON");
public static final ErrorId SE_POLYGON_WITHOUT_SURFACE = new ErrorId("SE_POLYGON_WITHOUT_SURFACE"); public static final ErrorId SE_POLYGON_WITHOUT_SURFACE = new ErrorId("SE_POLYGON_WITHOUT_SURFACE");
public static final ErrorId SC_SCHEMA_VALIDATION = new ErrorId("SC_SCHEMA_VALIDATION"); public static final ErrorId SC_SCHEMA_VALIDATION = new ErrorId("SC_SCHEMA_VALIDATION");
......
...@@ -69,6 +69,7 @@ public class Requirement implements Serializable { ...@@ -69,6 +69,7 @@ public class Requirement implements Serializable {
public static final Requirement R_SE_BS_IS_WALL = new Requirement("R_SE_BS_IS_WALL", RequirementType.SEMANTIC); public static final Requirement R_SE_BS_IS_WALL = new Requirement("R_SE_BS_IS_WALL", RequirementType.SEMANTIC);
public static final Requirement R_SE_BS_IS_GROUND = new Requirement("R_SE_BS_IS_GROUND", RequirementType.SEMANTIC); public static final Requirement R_SE_BS_IS_GROUND = new Requirement("R_SE_BS_IS_GROUND", RequirementType.SEMANTIC);
public static final Requirement R_SE_POLYGON_WITHOUT_SURFACE = new Requirement("R_SE_POLYGON_WITHOUT_SURFACE", RequirementType.SEMANTIC); public static final Requirement R_SE_POLYGON_WITHOUT_SURFACE = new Requirement("R_SE_POLYGON_WITHOUT_SURFACE", RequirementType.SEMANTIC);
public static final Requirement R_SE_BS_FRAGMENTED_CLOSURE = new Requirement("R_SE_BS_FRAGMENTED_CLOSURE", RequirementType.SEMANTIC);
public static final Requirement R_TO_FEATURE_COLLISION = new Requirement("R_TO_FEATURE_COLLISION", RequirementType.TOPOLOGY); public static final Requirement R_TO_FEATURE_COLLISION = new Requirement("R_TO_FEATURE_COLLISION", RequirementType.TOPOLOGY);
public static final Requirement R_TO_UTILITY_LINES_DISCONNECTED = new Requirement("R_TO_UTILITY_LINES_DISCONNECTED", RequirementType.TOPOLOGY); public static final Requirement R_TO_UTILITY_LINES_DISCONNECTED = new Requirement("R_TO_UTILITY_LINES_DISCONNECTED", RequirementType.TOPOLOGY);
......
package de.hft.stuttgart.citydoctor2.check.error;
import de.hft.stuttgart.citydoctor2.check.CheckError;
import de.hft.stuttgart.citydoctor2.check.ErrorId;
import de.hft.stuttgart.citydoctor2.check.ErrorReport;
import de.hft.stuttgart.citydoctor2.check.ErrorType;
import de.hft.stuttgart.citydoctor2.check.ErrorVisitor;
import de.hft.stuttgart.citydoctor2.check.HealingMethod;
import de.hft.stuttgart.citydoctor2.check.ModificationListener;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.GmlElement;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import org.apache.commons.lang3.NotImplementedException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class FragmentedClosureError implements CheckError {
private BoundarySurface feature;
private List<Set<GmlId>> clusters;
public FragmentedClosureError(BoundarySurface feature, List<Set<GmlId>> clusters) {
this.feature = feature;
this.clusters = clusters;
}
@Override
public ErrorType getType() {
return ErrorType.ERROR;
}
@Override
public ErrorId getErrorId() {
return ErrorId.SE_BS_FRAGMENTED_CLOSURE;
}
@Override
public GmlElement getFeature() {
return feature;
}
public List<Set<GmlId>> getClusterIds(){
return clusters;
}
public List<Set<Polygon>> getClusterPolygons(){
List<Set<Polygon>> clusterPolygons = new ArrayList<>();
Map<Set<GmlId>, Set<Polygon>> clusterMap = new HashMap<>();
//TODO: Implement conversion of id to Polygons
for (Set<GmlId> clusterIds : clusters){
Set<Polygon> clusterPolygon = new HashSet<>();
for (GmlId clusterId : clusterIds){
}
}
throw new NotImplementedException();
// return null;
}
@Override
public void accept(ErrorVisitor errorVisitor) {
}
@Override
public boolean accept(HealingMethod method, ModificationListener l) {
return false;
}
@Override
public void report(ErrorReport report) {
}
}
package de.hft.stuttgart.citydoctor2.checks.semantics;
import de.hft.stuttgart.citydoctor2.check.Check;
import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.Requirement;
import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.FragmentedClosureError;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.checks.util.PolygonClusterMap;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
public class ClosureSurfaceFragmentedCheck extends Check {
private static final List<CheckId> dependencies;
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_P_NON_PLANAR);
dependencies = Collections.unmodifiableList(deps);
}
@Override
public Set<Requirement> appliesToRequirements() {
return CollectionUtils.singletonSet(Requirement.R_SE_BS_FRAGMENTED_CLOSURE);
}
@Override
public CheckId getCheckId() {
return CheckId.C_SE_BS_FRAGMENTED_CLOSURE;
}
@Override
public RequirementType getType() {
return RequirementType.SEMANTIC;
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public Check createNewInstance() {
return new ClosureSurfaceFragmentedCheck();
}
@Override
public void check(BoundarySurface bs) {
if(bs.getType() != BoundarySurfaceType.CLOSURE){
bs.addCheckResult(new CheckResult(this, ResultStatus.OK, null));
} else {
}
}
private void checkClosureForFragmentation(BoundarySurface bs){
List<Geometry> geoms = bs.getGeometries();
for(Geometry geom : geoms){
PolygonClusterMap map = PolygonClusterMap.of(geom);
if (map.isFragmented()){
bs.addCheckResult(new CheckResult(this, ResultStatus.ERROR, new FragmentedClosureError(bs, map.getClusters()) ));
}
}
}
}
...@@ -11,6 +11,7 @@ import de.hft.stuttgart.citydoctor2.check.RequirementType; ...@@ -11,6 +11,7 @@ import de.hft.stuttgart.citydoctor2.check.RequirementType;
import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.FeatureCollisionError; import de.hft.stuttgart.citydoctor2.check.error.FeatureCollisionError;
import de.hft.stuttgart.citydoctor2.checks.Checks; import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil; import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache; import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
...@@ -62,7 +63,7 @@ public class FeatureCollisionCheck extends Check{ ...@@ -62,7 +63,7 @@ public class FeatureCollisionCheck extends Check{
@Override @Override
public Set<Requirement> appliesToRequirements() { public Set<Requirement> appliesToRequirements() {
return Set.of(Requirement.R_TO_FEATURE_COLLISION); return CollectionUtils.singletonSet(Requirement.R_TO_FEATURE_COLLISION);
} }
@Override @Override
......
...@@ -5,6 +5,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckId; ...@@ -5,6 +5,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.Checkable; import de.hft.stuttgart.citydoctor2.check.Checkable;
import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor; import de.hft.stuttgart.citydoctor2.check.CheckableUtilsVisitor;
import de.hft.stuttgart.citydoctor2.check.CheckableVisitor;
import de.hft.stuttgart.citydoctor2.check.ErrorType; import de.hft.stuttgart.citydoctor2.check.ErrorType;
import de.hft.stuttgart.citydoctor2.check.GlobalParameters; import de.hft.stuttgart.citydoctor2.check.GlobalParameters;
import de.hft.stuttgart.citydoctor2.check.Requirement; import de.hft.stuttgart.citydoctor2.check.Requirement;
...@@ -13,6 +14,7 @@ import de.hft.stuttgart.citydoctor2.check.ResultStatus; ...@@ -13,6 +14,7 @@ import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.FloatingUtilityError; import de.hft.stuttgart.citydoctor2.check.error.FloatingUtilityError;
import de.hft.stuttgart.citydoctor2.check.error.MisalignedConnectionError; import de.hft.stuttgart.citydoctor2.check.error.MisalignedConnectionError;
import de.hft.stuttgart.citydoctor2.checks.Checks; import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.checks.util.ConnectionAlignmentType; import de.hft.stuttgart.citydoctor2.checks.util.ConnectionAlignmentType;
import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil; import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil;
import de.hft.stuttgart.citydoctor2.checks.util.UtilityNetworkUtils; import de.hft.stuttgart.citydoctor2.checks.util.UtilityNetworkUtils;
...@@ -29,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex; ...@@ -29,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Plane; import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Pair; import de.hft.stuttgart.citydoctor2.utils.Pair;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
...@@ -46,9 +49,6 @@ public class FloatingUtilityCheck extends Check{ ...@@ -46,9 +49,6 @@ public class FloatingUtilityCheck extends Check{
deps.add(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION); deps.add(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION);
deps.add(CheckId.C_TO_FLOATING_UTILITY); deps.add(CheckId.C_TO_FLOATING_UTILITY);
dependencies = Collections.unmodifiableList(deps); dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(LinearRing.class);
} }
...@@ -58,7 +58,7 @@ public class FloatingUtilityCheck extends Check{ ...@@ -58,7 +58,7 @@ public class FloatingUtilityCheck extends Check{
@Override @Override
public Set<Requirement> appliesToRequirements() { public Set<Requirement> appliesToRequirements() {
return Set.of(Requirement.R_TO_UTILITY_LINES_DISCONNECTED); return CollectionUtils.singletonSet(Requirement.R_TO_UTILITY_LINES_DISCONNECTED);
} }
@Override @Override
...@@ -76,6 +76,11 @@ public class FloatingUtilityCheck extends Check{ ...@@ -76,6 +76,11 @@ public class FloatingUtilityCheck extends Check{
return new FloatingUtilityCheck(); return new FloatingUtilityCheck();
} }
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override @Override
public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config, CityObjectCache cache) { public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config, CityObjectCache cache) {
Map<String, String> localParameters = params.get(getCheckId()); Map<String, String> localParameters = params.get(getCheckId());
...@@ -115,132 +120,46 @@ public class FloatingUtilityCheck extends Check{ ...@@ -115,132 +120,46 @@ public class FloatingUtilityCheck extends Check{
subjGeoms.add(geom); subjGeoms.add(geom);
} }
}); });
for (Geometry geom : subjGeoms) { boolean floating = true;
for(CityObject candidate : candidates){
}
}
boolean misalignedConnections = false;
boolean freeFloating = false;
for (Geometry sGeo : subjGeoms) { for (Geometry sGeo : subjGeoms) {
final boolean[] connected = {false}; final boolean[] connected = {false};
final boolean[] misalignment = {false}; for(CityObject candidate : candidates){
for (CityObject candidate : candidates) { if (connected[0]) {
// A connection was found, skip the remaining candidates
break;
}
candidate.accept(new CheckableUtilsVisitor() { candidate.accept(new CheckableUtilsVisitor() {
@Override @Override
public void check(Geometry cGeo) { public void check(Geometry cGeo) {
if(sGeo.getLod() != cGeo.getLod()){ if(sGeo.getLod() != cGeo.getLod()){
return; return;
} }
List<Pair<Vertex, Vertex>> pairs = getVertexPairs(sGeo, cGeo); List<PolygonIntersection> polygonIntersections =
if (pairs.size() < 3) { SelfIntersectionUtil.calculateGeometryIntersections(sGeo, cGeo, epsilon);
if (!pairs.isEmpty()) { if(!polygonIntersections.isEmpty()){
subject.addCheckResult(new CheckResult(CheckId.C_TO_UTILITY_CONNECTION_MISALIGNED, } else{
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(), for(PolygonIntersection pi : polygonIntersections){
MisalignedConnectionError.MisalignmentType.HANGING, ErrorType.ERROR))); if(pi.getType() != PolygonIntersection.IntersectionType.NONE){
}
} else {
switch(checkConnectionAlignment(sGeo, cGeo, pairs)) {
case ALIGNED:
connected[0] = true; connected[0] = true;
break; }
case ASYMMETRIC_ROTATION:
subject.addCheckResult(new CheckResult(CheckId.C_TO_UTILITY_CONNECTION_MISALIGNED,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignedConnectionError.MisalignmentType.ASYMMETRIC_ROTATION, ErrorType.ERROR)));
misalignment[0] = true;
break;
case SHEARED:
subject.addCheckResult(new CheckResult(CheckId.C_TO_UTILITY_CONNECTION_MISALIGNED,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignedConnectionError.MisalignmentType.SHEARED, ErrorType.ERROR)));
misalignment[0] = true;
break;
case UNKNOWN:
subject.addCheckResult(new CheckResult(CheckId.C_TO_UTILITY_CONNECTION_MISALIGNED,
ResultStatus.WARNING, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignedConnectionError.MisalignmentType.UNKNOWN, ErrorType.WARNING)));
misalignment[0] = true;
break;
} }
} }
} }
}); });
if (!connected[0]) {
// Geometry has no connections and is "floating in space"
freeFloating = true;
FloatingUtilityError err = new FloatingUtilityError(subject, sGeo);
subject.addCheckResult(new CheckResult(this, ResultStatus.ERROR, err));
} else if (misalignment[0]) {
misalignedConnections = true;
}
} }
} if(connected[0]){
if (!misalignedConnections && !freeFloating) { floating = false;
subject.addCheckResult(new CheckResult(this, ResultStatus.OK, null)); break;
}
}
private List<Pair<Vertex, Vertex>> getVertexPairs(Geometry subject, Geometry candidate){
if (candidate.getVertices() == null){
candidate.prepareForChecking();
}
List<Vertex> subjectVerts = subject.getVertices();
List<Pair<Vertex, Vertex>> connectionPairs = new ArrayList<>();
for (Vertex cVert : candidate.getVertices()) {
for (Vertex sVert : subjectVerts) {
if(sVert.equalsWithEpsilon(cVert, searchRadius)){
connectionPairs.add(new Pair<>(sVert, cVert));
}
} }
} }
return connectionPairs; if(floating){
} subject.addCheckResult(new CheckResult(CheckId.C_TO_FLOATING_UTILITY, ResultStatus.ERROR,
new FloatingUtilityError(subject, subject.getHighestLodGeometry())));
private ConnectionAlignmentType checkConnectionAlignment(Geometry subject, Geometry candidate, List<Pair<Vertex,Vertex>> pairs){ } else {
ConcretePolygon sPoly = new ConcretePolygon(); subject.addCheckResult(new CheckResult(CheckId.C_TO_FLOATING_UTILITY, ResultStatus.OK, null));
LinearRing sRing = new LinearRing(AbstractLinearRing.LinearRingType.EXTERIOR);
sPoly.setParent(new Geometry(GeometryType.MULTI_SURFACE, subject.getLod(), Geometry.Orientation.INWARD));
ArrayList<Vertex> sPairVerts = new ArrayList<>();
ArrayList<Vertex> cPairVerts = new ArrayList<>();
sPoly.setExteriorRing(sRing);
pairs.forEach(pair -> {
sPairVerts.add(pair.getValue0());
cPairVerts.add(pair.getValue1());
sRing.addVertex(pair.getValue0());
});
Plane connectionPlane = Plane.of(sPoly);
List<Vertex> subjConVerts = UtilityNetworkUtils.edgeLoopSelection(subject, sPairVerts, connectionPlane, searchRadius);
List<Vertex> candConVerts = UtilityNetworkUtils.edgeLoopSelection(candidate, cPairVerts, connectionPlane, searchRadius);
if (subjConVerts.isEmpty() && candConVerts.isEmpty()) {
// No missed vertices near connection, connection of features is aligned
return ConnectionAlignmentType.ALIGNED;
} }
Vertex subjCentroid = UtilityNetworkUtils.getCentroid(subjConVerts);
Vertex candCentroid = UtilityNetworkUtils.getCentroid(candConVerts);
if (subjCentroid.equalsWithEpsilon(candCentroid, searchRadius)) {
if(subjConVerts.size() == candConVerts.size()){
// Connection plane centroids of both features equal, and are missing equal number of connection vertices
// -> both features likely of same shape but with a roll, yaw or pitch mismatch
return ConnectionAlignmentType.ASYMMETRIC_ROTATION;
} else {
// The form of both features differs, and they are connected by some vertex pairs
// Uncertain if erroneous, add warning
return ConnectionAlignmentType.UNKNOWN;
}
} else{
// Centroids are misaligned, connection is sheared off along the connection plane
return ConnectionAlignmentType.SHEARED;
}
} }
} }
...@@ -14,21 +14,28 @@ import de.hft.stuttgart.citydoctor2.check.error.FloatingUtilityError; ...@@ -14,21 +14,28 @@ import de.hft.stuttgart.citydoctor2.check.error.FloatingUtilityError;
import de.hft.stuttgart.citydoctor2.check.error.MisalignedConnectionError; import de.hft.stuttgart.citydoctor2.check.error.MisalignedConnectionError;
import de.hft.stuttgart.citydoctor2.check.error.MisalignedConnectionError.MisalignmentType; import de.hft.stuttgart.citydoctor2.check.error.MisalignedConnectionError.MisalignmentType;
import de.hft.stuttgart.citydoctor2.checks.Checks; import de.hft.stuttgart.citydoctor2.checks.Checks;
import de.hft.stuttgart.citydoctor2.checks.util.CollectionUtils;
import de.hft.stuttgart.citydoctor2.checks.util.ConnectionAlignmentType; import de.hft.stuttgart.citydoctor2.checks.util.ConnectionAlignmentType;
import de.hft.stuttgart.citydoctor2.checks.util.SelfIntersectionUtil;
import de.hft.stuttgart.citydoctor2.checks.util.UtilityNetworkUtils; import de.hft.stuttgart.citydoctor2.checks.util.UtilityNetworkUtils;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache; import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractLinearRing; import de.hft.stuttgart.citydoctor2.datastructure.AbstractLinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurfaceType;
import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon; import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
import de.hft.stuttgart.citydoctor2.datastructure.FeatureType;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry; import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GeometryType; import de.hft.stuttgart.citydoctor2.datastructure.GeometryType;
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.Vertex; import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Plane; import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.math.Segment3d;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Pair; import de.hft.stuttgart.citydoctor2.utils.Pair;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
...@@ -44,6 +51,7 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -44,6 +51,7 @@ public class MisalignedUtilityConnectionCheck extends Check {
static { static {
ArrayList<CheckId> deps = new ArrayList<>(); ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION); deps.add(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION);
deps.add(CheckId.C_TO_FLOATING_UTILITY);
dependencies = Collections.unmodifiableList(deps); dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>(); ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
...@@ -57,7 +65,12 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -57,7 +65,12 @@ public class MisalignedUtilityConnectionCheck extends Check {
@Override @Override
public Set<Requirement> appliesToRequirements() { public Set<Requirement> appliesToRequirements() {
return Set.of(Requirement.R_TO_UTILITY_LINES_DISCONNECTED); return CollectionUtils.singletonSet(Requirement.R_TO_UTILITY_LINES_DISCONNECTED);
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
} }
@Override @Override
...@@ -108,24 +121,43 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -108,24 +121,43 @@ public class MisalignedUtilityConnectionCheck extends Check {
return; return;
} }
List<Geometry> subjGeoms = new ArrayList<>(); List<Geometry> subjGeoms = new ArrayList<>();
// subject.accept(new CheckableUtilsVisitor() {
// @Override
// public void check(Geometry geom) {
// subjGeoms.add(geom);
// }
// });
subject.accept(new CheckableUtilsVisitor() { subject.accept(new CheckableUtilsVisitor() {
@Override @Override
public void check(Geometry geom) { public void check(BoundarySurface bs) {
subjGeoms.add(geom); if (bs.getType() == BoundarySurfaceType.CLOSURE){
subjGeoms.addAll(bs.getGeometries());
}
} }
}); });
boolean misalignedConnections = false; boolean misalignedConnections = false;
boolean freeFloating = false; boolean freeFloating = false;
for (Geometry sGeo : subjGeoms) { for (Geometry sGeo : subjGeoms) {
final boolean[] connected = {false}; final boolean[] connected = {false};
final boolean[] misalignment = {false}; final boolean[] misalignment = {false};
for (CityObject candidate : candidates) { for (CityObject candidate : candidates) {
candidate.accept(new CheckableUtilsVisitor() { candidate.accept(new CheckableUtilsVisitor() {
@Override @Override
public void check(Geometry cGeo) { public void check(Geometry cGeo) {
if(sGeo.getLod() != cGeo.getLod()){ if(sGeo.getLod() != cGeo.getLod()){
return; return;
} }
List<PolygonIntersection> intersections = SelfIntersectionUtil.calculateGeometryIntersections(sGeo, cGeo, epsilon);
intersections = intersections.stream()
.filter(isect -> isect.getType() != PolygonIntersection.IntersectionType.NONE)
.toList();
for (PolygonIntersection intersection : intersections) {
List<Segment3d> lines = intersection.getLines();
}
List<Pair<Vertex, Vertex>> pairs = getVertexPairs(sGeo, cGeo); List<Pair<Vertex, Vertex>> pairs = getVertexPairs(sGeo, cGeo);
if (pairs.size() < 3) { if (pairs.size() < 3) {
if (!pairs.isEmpty()) { if (!pairs.isEmpty()) {
...@@ -176,6 +208,10 @@ public class MisalignedUtilityConnectionCheck extends Check { ...@@ -176,6 +208,10 @@ public class MisalignedUtilityConnectionCheck extends Check {
} }
} }
//
// private boolean checkForEndToEndConnection(Geometry sGeom, CityObject cGeo) {
// if (sGeom.getParent().getFeatureType() != FeatureType.BOUNDARY_SURFACE || cGeo.getPar)
// }
private List<Pair<Vertex, Vertex>> getVertexPairs(Geometry subject, Geometry candidate){ private List<Pair<Vertex, Vertex>> getVertexPairs(Geometry subject, Geometry candidate){
if (candidate.getVertices() == null){ if (candidate.getVertices() == null){
......
package de.hft.stuttgart.citydoctor2.checks.util;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.GmlId;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public class PolygonClusterMap {
private Set<GmlId> includeFilter;
private Map<GmlId, Integer> conpol = new HashMap<>();
private int groupCounter = 1;
private int clusterCount = 0;
private PolygonClusterMap() {
}
public static PolygonClusterMap of(Geometry geom){
PolygonClusterMap map = new PolygonClusterMap();
Set<GmlId> includeFilter = new HashSet<>();
geom.getPolygons().forEach(p-> includeFilter.add(p.getGmlId()));
geom.getPolygons().forEach(p->map.conpol.put(p.getGmlId(), -1));
map.includeFilter = includeFilter;
for(Polygon p : geom.getPolygons()){
for(Vertex v : p.getExteriorRing().getVertices()){
map.insert(v.getAdjacentPolygons(geom));
}
}
return map;
}
public boolean isFragmented(){
return clusterCount > 1;
}
private void insert(Set<Polygon> adjacency){
adjacency.removeIf(p -> !includeFilter.contains(p.getGmlId()));
Set<Integer> matchedGroups = new HashSet<>();
for(Polygon p : adjacency){
matchedGroups.add(conpol.getOrDefault(p.getGmlId(), -1));
}
matchedGroups.remove(-1);
if(matchedGroups.isEmpty() ){
// No matches, insert new group
for(Polygon p : adjacency){
conpol.computeIfPresent(
p.getGmlId(),
(key, value) -> groupCounter);
}
groupCounter++;
clusterCount++;
} else {
Optional<Integer> minGroup = matchedGroups.stream().min(Integer::compareTo);
Integer group = minGroup.get();
for(Polygon p : adjacency){
conpol.computeIfPresent(p.getGmlId(), (key, value) -> group);
}
matchedGroups.remove(group);
for (Integer otherGroup : matchedGroups) {
mergeGroups(otherGroup, group);
}
}
}
private void mergeGroups(Integer sourceGroup, Integer targetGroup){
boolean merged = false;
for (Map.Entry<GmlId, Integer> entry : conpol.entrySet()){
if (Objects.equals(entry.getValue(), sourceGroup)){
conpol.put(entry.getKey(), targetGroup);
merged = true;
}
}
if (merged){
clusterCount--;
}
}
public List<Set<GmlId>> getClusters() {
return conpol.entrySet().stream()
.collect(Collectors.groupingBy(
Map.Entry::getValue,
Collectors.mapping(Map.Entry::getKey, Collectors.toSet())
))
.values()
.stream()
.toList();
}
}
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