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
9fed0af9
Commit
9fed0af9
authored
Oct 01, 2026
by
Luna Riegel
Browse files
Refactor: Restructure misaligned utility check
parent
28af755c
Changes
1
Hide whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/topology/MisalignedUtilityConnectionCheck.java
View file @
9fed0af9
...
...
@@ -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
))
{
angle
Threshold
=
Double
.
parseDouble
(
localParameters
.
get
(
ANGLE_THRESHOLD
));
if
(
localParameters
.
containsKey
(
SHALLOW_
ANGLE_THRESHOLD
))
{
shallowness
Threshold
=
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
=
0
d
;
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
=
1
d
;
if
(
intersectionLengthMax
<
intersectionLength
)
{
intersectionLengthMax
=
intersectionLength
;
}
double
thetaN
=
Math
.
atan2
(
normalA
.
cross
(
normalB
).
getLength
(),
normalA
.
dot
(
normalB
));
dihedralAngles
.
add
(
new
Pair
<>((
180
d
-
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
())
{
...
...
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