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 {
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_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_UTILITY_CONNECTION_MISALIGNED = new CheckId("C_TO_UTILITY_CONNECTION_MISALIGNED");
......
......@@ -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_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_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 SE_POLYGON_WITHOUT_SURFACE = new ErrorId("SE_POLYGON_WITHOUT_SURFACE");
public static final ErrorId SC_SCHEMA_VALIDATION = new ErrorId("SC_SCHEMA_VALIDATION");
......
......@@ -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_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_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_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;
import de.hft.stuttgart.citydoctor2.check.ResultStatus;
import de.hft.stuttgart.citydoctor2.check.error.FeatureCollisionError;
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.database.CityObjectCache;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
......@@ -62,7 +63,7 @@ public class FeatureCollisionCheck extends Check{
@Override
public Set<Requirement> appliesToRequirements() {
return Set.of(Requirement.R_TO_FEATURE_COLLISION);
return CollectionUtils.singletonSet(Requirement.R_TO_FEATURE_COLLISION);
}
@Override
......
......@@ -5,6 +5,7 @@ import de.hft.stuttgart.citydoctor2.check.CheckId;
import de.hft.stuttgart.citydoctor2.check.CheckResult;
import de.hft.stuttgart.citydoctor2.check.Checkable;
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.GlobalParameters;
import de.hft.stuttgart.citydoctor2.check.Requirement;
......@@ -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.MisalignedConnectionError;
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.SelfIntersectionUtil;
import de.hft.stuttgart.citydoctor2.checks.util.UtilityNetworkUtils;
......@@ -29,6 +31,7 @@ import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
import de.hft.stuttgart.citydoctor2.math.Plane;
import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
import de.hft.stuttgart.citydoctor2.utils.Pair;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
import java.util.ArrayList;
import java.util.Collection;
......@@ -46,9 +49,6 @@ public class FloatingUtilityCheck extends Check{
deps.add(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION);
deps.add(CheckId.C_TO_FLOATING_UTILITY);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
classes.add(LinearRing.class);
}
......@@ -58,7 +58,7 @@ public class FloatingUtilityCheck extends Check{
@Override
public Set<Requirement> appliesToRequirements() {
return Set.of(Requirement.R_TO_UTILITY_LINES_DISCONNECTED);
return CollectionUtils.singletonSet(Requirement.R_TO_UTILITY_LINES_DISCONNECTED);
}
@Override
......@@ -76,6 +76,11 @@ public class FloatingUtilityCheck extends Check{
return new FloatingUtilityCheck();
}
@Override
public List<CheckId> getDependencies() {
return dependencies;
}
@Override
public void init(Map<CheckId, Map<String, String>> params, ParserConfiguration config, CityObjectCache cache) {
Map<String, String> localParameters = params.get(getCheckId());
......@@ -115,132 +120,46 @@ public class FloatingUtilityCheck extends Check{
subjGeoms.add(geom);
}
});
for (Geometry geom : subjGeoms) {
for(CityObject candidate : candidates){
}
}
boolean misalignedConnections = false;
boolean freeFloating = false;
boolean floating = true;
for (Geometry sGeo : subjGeoms) {
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() {
@Override
public void check(Geometry cGeo) {
if(sGeo.getLod() != cGeo.getLod()){
return;
}
List<Pair<Vertex, Vertex>> pairs = getVertexPairs(sGeo, cGeo);
if (pairs.size() < 3) {
if (!pairs.isEmpty()) {
subject.addCheckResult(new CheckResult(CheckId.C_TO_UTILITY_CONNECTION_MISALIGNED,
ResultStatus.ERROR, new MisalignedConnectionError(sGeo, subject, candidate.getGmlId(),
MisalignedConnectionError.MisalignmentType.HANGING, ErrorType.ERROR)));
}
} else {
switch(checkConnectionAlignment(sGeo, cGeo, pairs)) {
case ALIGNED:
List<PolygonIntersection> polygonIntersections =
SelfIntersectionUtil.calculateGeometryIntersections(sGeo, cGeo, epsilon);
if(!polygonIntersections.isEmpty()){
} else{
for(PolygonIntersection pi : polygonIntersections){
if(pi.getType() != PolygonIntersection.IntersectionType.NONE){
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 (!misalignedConnections && !freeFloating) {
subject.addCheckResult(new CheckResult(this, ResultStatus.OK, null));
}
}
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));
}
if(connected[0]){
floating = false;
break;
}
}
return connectionPairs;
}
private ConnectionAlignmentType checkConnectionAlignment(Geometry subject, Geometry candidate, List<Pair<Vertex,Vertex>> pairs){
ConcretePolygon sPoly = new ConcretePolygon();
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;
if(floating){
subject.addCheckResult(new CheckResult(CheckId.C_TO_FLOATING_UTILITY, ResultStatus.ERROR,
new FloatingUtilityError(subject, subject.getHighestLodGeometry())));
} else {
subject.addCheckResult(new CheckResult(CheckId.C_TO_FLOATING_UTILITY, ResultStatus.OK, null));
}
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;
import de.hft.stuttgart.citydoctor2.check.error.MisalignedConnectionError;
import de.hft.stuttgart.citydoctor2.check.error.MisalignedConnectionError.MisalignmentType;
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.SelfIntersectionUtil;
import de.hft.stuttgart.citydoctor2.checks.util.UtilityNetworkUtils;
import de.hft.stuttgart.citydoctor2.database.CityObjectCache;
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.CityObject;
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.GeometryType;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.OtherConstructionObject;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
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.utils.Pair;
import de.hft.stuttgart.citydoctor2.utils.PolygonIntersection;
import java.util.ArrayList;
import java.util.Collection;
......@@ -44,6 +51,7 @@ public class MisalignedUtilityConnectionCheck extends Check {
static {
ArrayList<CheckId> deps = new ArrayList<>();
deps.add(CheckId.C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION);
deps.add(CheckId.C_TO_FLOATING_UTILITY);
dependencies = Collections.unmodifiableList(deps);
ArrayList<Class<? extends Checkable>> classes = new ArrayList<>();
......@@ -57,7 +65,12 @@ public class MisalignedUtilityConnectionCheck extends Check {
@Override
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
......@@ -108,24 +121,43 @@ public class MisalignedUtilityConnectionCheck extends Check {
return;
}
List<Geometry> subjGeoms = new ArrayList<>();
// subject.accept(new CheckableUtilsVisitor() {
// @Override
// public void check(Geometry geom) {
// subjGeoms.add(geom);
// }
// });
subject.accept(new CheckableUtilsVisitor() {
@Override
public void check(Geometry geom) {
subjGeoms.add(geom);
public void check(BoundarySurface bs) {
if (bs.getType() == BoundarySurfaceType.CLOSURE){
subjGeoms.addAll(bs.getGeometries());
}
}
});
boolean misalignedConnections = false;
boolean freeFloating = false;
for (Geometry sGeo : subjGeoms) {
final boolean[] connected = {false};
final boolean[] misalignment = {false};
for (CityObject candidate : candidates) {
candidate.accept(new CheckableUtilsVisitor() {
@Override
public void check(Geometry cGeo) {
if(sGeo.getLod() != cGeo.getLod()){
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);
if (pairs.size() < 3) {
if (!pairs.isEmpty()) {
......@@ -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){
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