Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
CityDoctor
CityDoctor2
Commits
15585c6e
Commit
15585c6e
authored
Jun 23, 2026
by
Luna Riegel
Browse files
Feat: Add closure surface fragmentation check
parent
3ff0eb11
Pipeline
#12415
failed with stage
in 1 minute and 1 second
Changes
9
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/CheckId.java
View file @
15585c6e
...
...
@@ -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"
);
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java
View file @
15585c6e
...
...
@@ -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"
);
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Requirement.java
View file @
15585c6e
...
...
@@ -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
);
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/FragmentedClosureError.java
0 → 100644
View file @
15585c6e
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
)
{
}
}
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/semantics/ClosureSurfaceFragmentedCheck.java
0 → 100644
View file @
15585c6e
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
())
));
}
}
}
}
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/topology/FeatureCollisionCheck.java
View file @
15585c6e
...
...
@@ -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
.
singleton
Set
(
Requirement
.
R_TO_FEATURE_COLLISION
);
}
@Override
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/topology/FloatingUtilityCheck.java
View file @
15585c6e
...
...
@@ -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
.
singleton
Set
(
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
;
}
}
}
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/topology/MisalignedUtilityConnectionCheck.java
View file @
15585c6e
...
...
@@ -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
){
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/util/PolygonClusterMap.java
0 → 100644
View file @
15585c6e
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
();
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment