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
e763994c
Commit
e763994c
authored
Mar 19, 2021
by
Matthias Betz
Browse files
reworked handling of neighbor vertices, now replaced by the other vertex
parent
d12c7d39
Pipeline
#2448
passed with stage
in 3 minutes and 28 seconds
Changes
14
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Edge.java
View file @
e763994c
...
...
@@ -67,18 +67,18 @@ public class Edge implements Serializable {
numberOfOppositeHalfEdges
++;
return
;
}
throw
new
IllegalStateException
(
"The given vertices do not f
r
om this edge
"
);
throw
new
IllegalStateException
(
"The given vertices do not fo
r
m this edge
\n v0="
+
v0
+
", v1="
+
v1
+
", edge"
+
this
);
}
private
boolean
isPointOfEdge
(
Vertex
v0
,
Vertex
edgePoint
)
{
if
(
v0
.
equals
(
edgePoint
))
{
return
true
;
}
for
(
Vertex
v
:
v0
.
getNeighbors
())
{
if
(
v
.
equals
(
edgePoint
))
{
return
true
;
}
}
//
for (Vertex v : v0.getNeighbors()) {
//
if (v.equals(edgePoint)) {
//
return true;
//
}
//
}
return
false
;
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java
View file @
e763994c
...
...
@@ -145,24 +145,24 @@ public class Geometry extends GmlElement {
Edge
oppositeEdge
=
new
Edge
(
v1
,
v0
);
duplicacyMap
.
put
(
tempEdge
,
tempEdge
);
duplicacyMap
.
put
(
oppositeEdge
,
tempEdge
);
for
(
Vertex
v0Neighbor
:
v0
.
getNeighbors
())
{
Edge
alternativeEdge
=
new
Edge
(
v0Neighbor
,
v1
);
Edge
oppositeAlternativeEdge
=
new
Edge
(
v1
,
v0Neighbor
);
duplicacyMap
.
put
(
alternativeEdge
,
tempEdge
);
duplicacyMap
.
put
(
oppositeAlternativeEdge
,
tempEdge
);
for
(
Vertex
v1Neighbor
:
v1
.
getNeighbors
())
{
alternativeEdge
=
new
Edge
(
v0Neighbor
,
v1Neighbor
);
oppositeAlternativeEdge
=
new
Edge
(
v1Neighbor
,
v0Neighbor
);
duplicacyMap
.
put
(
alternativeEdge
,
tempEdge
);
duplicacyMap
.
put
(
oppositeAlternativeEdge
,
tempEdge
);
}
}
for
(
Vertex
v
:
v1
.
getNeighbors
())
{
Edge
alternativeEdge
=
new
Edge
(
v0
,
v
);
Edge
oppositeAlternativeEdge
=
new
Edge
(
v
,
v0
);
duplicacyMap
.
put
(
alternativeEdge
,
tempEdge
);
duplicacyMap
.
put
(
oppositeAlternativeEdge
,
tempEdge
);
}
//
for (Vertex v0Neighbor : v0.getNeighbors()) {
//
Edge alternativeEdge = new Edge(v0Neighbor, v1);
//
Edge oppositeAlternativeEdge = new Edge(v1, v0Neighbor);
//
duplicacyMap.put(alternativeEdge, tempEdge);
//
duplicacyMap.put(oppositeAlternativeEdge, tempEdge);
//
for (Vertex v1Neighbor : v1.getNeighbors()) {
//
alternativeEdge = new Edge(v0Neighbor, v1Neighbor);
//
oppositeAlternativeEdge = new Edge(v1Neighbor, v0Neighbor);
//
duplicacyMap.put(alternativeEdge, tempEdge);
//
duplicacyMap.put(oppositeAlternativeEdge, tempEdge);
//
}
//
}
//
for (Vertex v : v1.getNeighbors()) {
//
Edge alternativeEdge = new Edge(v0, v);
//
Edge oppositeAlternativeEdge = new Edge(v, v0);
//
duplicacyMap.put(alternativeEdge, tempEdge);
//
duplicacyMap.put(oppositeAlternativeEdge, tempEdge);
//
}
e
=
tempEdge
;
edges
.
add
(
e
);
edgeMap
.
put
(
new
SerializablePair
<>(
v0
,
v1
),
e
);
...
...
@@ -277,10 +277,6 @@ public class Geometry extends GmlElement {
return
vertices
;
}
public
void
setVertices
(
List
<
Vertex
>
vertices
)
{
this
.
vertices
=
vertices
;
}
public
List
<
Edge
>
getEdges
()
{
return
edges
;
}
...
...
@@ -300,24 +296,24 @@ public class Geometry extends GmlElement {
if
(
edge
!=
null
)
{
return
edge
;
}
for
(
Vertex
neighborV1
:
v1
.
getNeighbors
())
{
edge
=
edgeMap
.
get
(
new
SerializablePair
<>(
neighborV1
,
v2
));
if
(
edge
!=
null
)
{
return
edge
;
}
for
(
Vertex
neighborV2
:
v2
.
getNeighbors
())
{
edge
=
edgeMap
.
get
(
new
SerializablePair
<>(
neighborV1
,
neighborV2
));
if
(
edge
!=
null
)
{
return
edge
;
}
}
}
for
(
Vertex
neighborV2
:
v2
.
getNeighbors
())
{
edge
=
edgeMap
.
get
(
new
SerializablePair
<>(
neighborV2
,
v1
));
if
(
edge
!=
null
)
{
return
edge
;
}
}
//
for (Vertex neighborV1 : v1.getNeighbors()) {
//
edge = edgeMap.get(new SerializablePair<>(neighborV1, v2));
//
if (edge != null) {
//
return edge;
//
}
//
for (Vertex neighborV2 : v2.getNeighbors()) {
//
edge = edgeMap.get(new SerializablePair<>(neighborV1, neighborV2));
//
if (edge != null) {
//
return edge;
//
}
//
}
//
}
//
for (Vertex neighborV2 : v2.getNeighbors()) {
//
edge = edgeMap.get(new SerializablePair<>(neighborV2, v1));
//
if (edge != null) {
//
return edge;
//
}
//
}
return
null
;
}
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/LinearRing.java
View file @
e763994c
...
...
@@ -216,6 +216,14 @@ public class LinearRing extends GmlElement {
}
v
.
addAdjacentRing
(
this
,
parent
.
getParent
());
}
public
void
setVertex
(
int
i
,
Vertex
v
)
{
vertices
.
set
(
i
,
v
);
if
(
parent
.
isLinkedTo
())
{
v
.
addAdjacentRing
(
this
,
parent
.
getLinkedFromPolygon
().
getParent
());
}
v
.
addAdjacentRing
(
this
,
parent
.
getParent
());
}
@Override
public
String
toString
()
{
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Vertex.java
View file @
e763994c
...
...
@@ -19,7 +19,6 @@
package
de.hft.stuttgart.citydoctor2.datastructure
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.HashSet
;
import
java.util.List
;
import
java.util.Set
;
...
...
@@ -39,8 +38,6 @@ public class Vertex extends Vector3d {
private
List
<
SerializablePair
<
Geometry
,
HashSet
<
LinearRing
>>>
adjacentRings
=
new
ArrayList
<>(
2
);
private
List
<
Vertex
>
neighbors
;
public
Vertex
(
double
x
,
double
y
,
double
z
)
{
super
(
x
,
y
,
z
);
}
...
...
@@ -49,21 +46,7 @@ public class Vertex extends Vector3d {
super
(
vec
);
}
public
void
addNeighbor
(
Vertex
vec
)
{
if
(
neighbors
==
null
)
{
neighbors
=
new
ArrayList
<>(
1
);
}
neighbors
.
add
(
vec
);
}
public
List
<
Vertex
>
getNeighbors
()
{
if
(
neighbors
==
null
)
{
return
Collections
.
emptyList
();
}
return
neighbors
;
}
private
Set
<
LinearRing
>
getAdjacentRingsWithoutNeightbor
(
Geometry
geom
)
{
private
Set
<
LinearRing
>
getAdjacentRingsWithoutNeighbor
(
Geometry
geom
)
{
for
(
SerializablePair
<
Geometry
,
HashSet
<
LinearRing
>>
adjacency
:
adjacentRings
)
{
if
(
adjacency
.
getValue0
()
==
geom
)
{
return
adjacency
.
getValue1
();
...
...
@@ -73,24 +56,7 @@ public class Vertex extends Vector3d {
}
public
Set
<
LinearRing
>
getAdjacentRings
(
Geometry
geom
)
{
if
(
adjacentRings
==
null
)
{
return
Collections
.
emptySet
();
}
for
(
SerializablePair
<
Geometry
,
HashSet
<
LinearRing
>>
adjacency
:
adjacentRings
)
{
if
(
adjacency
.
getValue0
()
==
geom
)
{
if
(
neighbors
==
null
||
neighbors
.
isEmpty
())
{
return
adjacency
.
getValue1
();
}
else
{
Set
<
LinearRing
>
linearRings
=
new
HashSet
<>();
linearRings
.
addAll
(
adjacency
.
getValue1
());
for
(
Vertex
neighbor
:
neighbors
)
{
linearRings
.
addAll
(
neighbor
.
getAdjacentRingsWithoutNeightbor
(
geom
));
}
return
linearRings
;
}
}
}
throw
new
IllegalStateException
(
"Requested adjacent rings with Geometry not containing this vertex"
);
return
getAdjacentRingsWithoutNeighbor
(
geom
);
}
void
addAdjacentRing
(
LinearRing
ring
,
Geometry
geom
)
{
...
...
@@ -128,24 +94,7 @@ public class Vertex extends Vector3d {
}
public
Set
<
Polygon
>
getAdjacentPolygons
(
Geometry
geom
)
{
if
(
adjacentRings
==
null
)
{
return
Collections
.
emptySet
();
}
for
(
SerializablePair
<
Geometry
,
HashSet
<
LinearRing
>>
adjecency
:
adjacentRings
)
{
if
(
adjecency
.
getValue0
()
==
geom
)
{
Set
<
Polygon
>
polygons
=
new
HashSet
<>();
for
(
LinearRing
lr
:
adjecency
.
getValue1
())
{
polygons
.
add
(
lr
.
getParent
());
}
if
(
neighbors
!=
null
&&
!
neighbors
.
isEmpty
())
{
for
(
Vertex
neighbor
:
neighbors
)
{
polygons
.
addAll
(
neighbor
.
getAdjacentPolygonsWithoutNeighbor
(
geom
));
}
}
return
polygons
;
}
}
throw
new
IllegalStateException
(
"Requested adjacent polygons with Geometry not containing this vertex"
);
return
getAdjacentPolygonsWithoutNeighbor
(
geom
);
}
@Override
...
...
@@ -182,34 +131,6 @@ public class Vertex extends Vector3d {
findAdjacentRingsForGeometry
(
geom
).
remove
(
lr
);
}
public
boolean
equalsWithNeighbors
(
Vertex
other
)
{
if
(
this
.
equals
(
other
))
{
return
true
;
}
for
(
Vertex
v
:
other
.
getNeighbors
())
{
if
(
this
.
equals
(
v
))
{
return
true
;
}
}
return
checkNeighbors
(
other
);
}
private
boolean
checkNeighbors
(
Vertex
other
)
{
if
(
neighbors
!=
null
)
{
for
(
Vertex
v
:
neighbors
)
{
if
(
v
.
equals
(
other
))
{
return
true
;
}
for
(
Vertex
otherNeighbor
:
other
.
getNeighbors
())
{
if
(
otherNeighbor
.
equals
(
v
))
{
return
true
;
}
}
}
}
return
false
;
}
/**
* Remove all adjacent rings from this vertex, ignoring geometry association
*/
...
...
CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/mapper/FeatureMapper.java
View file @
e763994c
...
...
@@ -67,10 +67,12 @@ import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import
de.hft.stuttgart.citydoctor2.datastructure.GeometryType
;
import
de.hft.stuttgart.citydoctor2.datastructure.GmlId
;
import
de.hft.stuttgart.citydoctor2.datastructure.LandObject
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinkedPolygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.Lod
;
import
de.hft.stuttgart.citydoctor2.datastructure.Opening
;
import
de.hft.stuttgart.citydoctor2.datastructure.OpeningType
;
import
de.hft.stuttgart.citydoctor2.datastructure.Polygon
;
import
de.hft.stuttgart.citydoctor2.datastructure.SurfaceFeatureType
;
import
de.hft.stuttgart.citydoctor2.datastructure.TransportationObject
;
import
de.hft.stuttgart.citydoctor2.datastructure.TransportationObject.TransportationType
;
...
...
@@ -154,30 +156,38 @@ public class FeatureMapper extends FeatureWalker {
}
private
void
updateEdgesAndVertices
(
CityObject
co
)
{
// searching for neighboring vertices, replacing them with one single vertex to
// avoid later problems with edges and manifold problems
for
(
Geometry
geom
:
co
.
getGeometries
())
{
geom
.
updateVertices
();
KDTree
tree
=
new
KDTree
();
for
(
Vertex
v
:
geom
.
getVertices
())
{
tree
.
add
(
v
);
}
for
(
Vertex
v
:
geom
.
getVertices
())
{
List
<
Vertex
>
nodesInRange
=
tree
.
getNodesInRange
(
v
,
neighborDistance
);
for
(
Vertex
neighbor
:
nodesInRange
)
{
if
(
neighbor
==
v
)
{
continue
;
}
v
.
addNeighbor
(
neighbor
);
for
(
Polygon
poly
:
geom
.
getPolygons
())
{
LinearRing
lr
=
poly
.
getExteriorRing
();
updateRing
(
tree
,
lr
);
for
(
LinearRing
innerRing
:
poly
.
getInnerRings
())
{
updateRing
(
tree
,
innerRing
);
}
}
if
(
config
.
useLowMemoryConsumption
())
{
// do not create edges and vertices
// vertices were already created because they were needed for the kd tree
// remove them again
geom
.
clearMetaInformation
();
if
(!
config
.
useLowMemoryConsumption
())
{
// no low memory consumption mode meaning create all meta information in geometry
geom
.
updateEdgesAndVertices
();
}
}
}
private
void
updateRing
(
KDTree
tree
,
LinearRing
lr
)
{
for
(
int
i
=
0
;
i
<
lr
.
getVertices
().
size
();
i
++)
{
Vertex
v
=
lr
.
getVertices
().
get
(
i
);
List
<
Vertex
>
nodesInRange
=
tree
.
getNodesInRange
(
v
,
neighborDistance
);
if
(
nodesInRange
.
isEmpty
())
{
tree
.
add
(
v
);
}
else
{
// create edges
geom
.
updateEdges
();
if
(
nodesInRange
.
size
()
!=
1
)
{
throw
new
IllegalStateException
(
"Found more than one vertex in range of "
+
v
+
" this should never happen here"
);
}
// replace other vertex with neighboring one
Vertex
original
=
nodesInRange
.
get
(
0
);
lr
.
setVertex
(
i
,
original
);
}
}
}
...
...
CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/GeometryTest.java
View file @
e763994c
...
...
@@ -23,13 +23,26 @@ import static org.junit.Assert.assertFalse;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
junit
.
Assert
.
assertNull
;
import
static
org
.
junit
.
Assert
.
assertSame
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
when
;
import
java.util.ArrayList
;
import
java.util.List
;
import
org.citygml4j.factory.GMLGeometryFactory
;
import
org.citygml4j.model.citygml.building.AbstractBoundarySurface
;
import
org.citygml4j.model.citygml.building.WallSurface
;
import
org.junit.Test
;
import
de.hft.stuttgart.citydoctor2.check.Check
;
import
de.hft.stuttgart.citydoctor2.check.CheckError
;
import
de.hft.stuttgart.citydoctor2.check.CheckId
;
import
de.hft.stuttgart.citydoctor2.check.CheckResult
;
import
de.hft.stuttgart.citydoctor2.check.ResultStatus
;
import
de.hft.stuttgart.citydoctor2.datastructure.LinearRing.LinearRingType
;
import
de.hft.stuttgart.citydoctor2.math.Vector3d
;
import
de.hft.stuttgart.citydoctor2.parser.ParserConfiguration
;
/**
...
...
@@ -42,24 +55,24 @@ public class GeometryTest {
@Test
public
void
testCreate
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD0
);
assertNotNull
(
geom
);
assertSame
(
GeometryType
.
SOLID
,
geom
.
getType
());
assertSame
(
Lod
.
LOD0
,
geom
.
getLod
());
}
@Test
public
void
testAddPolygon
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD0
);
Polygon
p
=
new
ConcretePolygon
();
geom
.
addPolygon
(
p
);
assertEquals
(
1
,
geom
.
getPolygons
().
size
());
assertSame
(
geom
,
p
.
getParent
());
assertSame
(
geom
.
getPolygons
().
get
(
0
),
p
);
}
@Test
public
void
testRemovePolygon
()
{
ParserConfiguration
config
=
new
ParserConfiguration
(
4
,
false
);
...
...
@@ -74,22 +87,22 @@ public class GeometryTest {
LinearRing
lr
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
p
.
setExteriorRing
(
lr
);
lr
.
addVertex
(
new
Vertex
(
0
,
0
,
0
));
geom
.
addPolygon
(
p
);
p
.
setPartOfSurface
(
bs
);
p
.
setPartOfInstallation
(
bi
);
geom
.
removePolygon
(
p
);
assertEquals
(
0
,
geom
.
getPolygons
().
size
());
bs
.
reCreateGeometries
(
new
GMLGeometryFactory
(),
config
);
assertNull
(
abs
.
getLod2MultiSurface
());
bi
.
reCreateGeometries
(
new
GMLGeometryFactory
(),
config
);
assertNull
(
gmlBi
.
getLod2Geometry
());
}
@Test
public
void
testReplacePolygon
()
{
ParserConfiguration
config
=
new
ParserConfiguration
(
4
,
false
);
...
...
@@ -108,28 +121,27 @@ public class GeometryTest {
p
.
setPartOfSurface
(
bs
);
p
.
setPartOfInstallation
(
bi
);
bs
.
addGeometry
(
geom2
);
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD2
);
Polygon
linkedPoly
=
new
LinkedPolygon
(
p
,
geom
);
geom
.
addPolygon
(
linkedPoly
);
ConcretePolygon
p2
=
new
ConcretePolygon
();
p2
.
setExteriorRing
(
lr
);
ConcretePolygon
p3
=
new
ConcretePolygon
();
p3
.
setExteriorRing
(
lr
);
geom
.
replacePolygon
(
linkedPoly
,
p2
,
p3
);
assertEquals
(
2
,
geom
.
getPolygons
().
size
());
assertEquals
(
2
,
geom2
.
getPolygons
().
size
());
bi
.
reCreateGeometries
(
new
GMLGeometryFactory
(),
config
);
assertNull
(
gmlBi
.
getLod2Geometry
());
assertNull
(
gmlBi
.
getLod3Geometry
());
assertNull
(
gmlBi
.
getLod4Geometry
());
bs
.
reCreateGeometries
(
new
GMLGeometryFactory
(),
config
);
assertNotNull
(
abs
.
getLod2MultiSurface
());
assertNotNull
(
abs
.
getLod2MultiSurface
().
getMultiSurface
());
...
...
@@ -139,4 +151,133 @@ public class GeometryTest {
assertNull
(
gmlBi
.
getLod2Geometry
());
}
@Test
public
void
testSetParent
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
MULTI_SURFACE
,
Lod
.
LOD2
);
CityObject
co
=
mock
(
CityObject
.
class
);
geom
.
setParent
(
co
);
assertEquals
(
co
,
geom
.
getParent
());
}
@Test
public
void
testUpdateEdges
()
{
Geometry
geom
=
GeometryTestUtils
.
createDummyGeometryWithInnerRingWithNeighboringPolygon
(
GeometryType
.
SOLID
,
Lod
.
LOD2
);
geom
.
clearMetaInformation
();
assertNull
(
geom
.
getEdges
());
geom
.
updateEdges
();
List
<
Edge
>
edges
=
geom
.
getEdges
();
assertNotNull
(
edges
);
assertFalse
(
edges
.
isEmpty
());
assertEquals
(
10
,
edges
.
size
());
}
@Test
public
void
testSetType
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
MULTI_SURFACE
,
Lod
.
LOD2
);
geom
.
setType
(
GeometryType
.
SOLID
);
assertEquals
(
GeometryType
.
SOLID
,
geom
.
getType
());
}
@Test
public
void
testGetCenter
()
{
Geometry
geom
=
GeometryTestUtils
.
createDummyGeometry
(
GeometryType
.
SOLID
);
Vector3d
center
=
geom
.
getCenter
();
assertEquals
(
427583.3025
,
center
.
getX
(),
0.000001
);
assertEquals
(
6003502.5725
,
center
.
getY
(),
0.000001
);
assertEquals
(
6.905
,
center
.
getZ
(),
0.000001
);
}
@Test
public
void
testContainsError
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
assertFalse
(
geom
.
containsError
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
));
geom
.
addCheckResult
(
new
CheckResult
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
,
ResultStatus
.
ERROR
,
mock
(
CheckError
.
class
)));
assertTrue
(
geom
.
containsError
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
));
ConcretePolygon
p
=
new
ConcretePolygon
();
p
.
setExteriorRing
(
mock
(
LinearRing
.
class
));
geom
.
addPolygon
(
p
);
assertFalse
(
geom
.
containsError
(
CheckId
.
C_GE_P_INNER_RINGS_NESTED
));
p
.
addCheckResult
(
new
CheckResult
(
CheckId
.
C_GE_P_INNER_RINGS_NESTED
,
ResultStatus
.
ERROR
,
mock
(
CheckError
.
class
)));
assertTrue
(
geom
.
containsError
(
CheckId
.
C_GE_P_INNER_RINGS_NESTED
));
}
@Test
public
void
testClearAllContainedCheckResults
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
assertFalse
(
geom
.
containsError
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
));
geom
.
addCheckResult
(
new
CheckResult
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
,
ResultStatus
.
ERROR
,
mock
(
CheckError
.
class
)));
assertTrue
(
geom
.
containsError
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
));
Polygon
p
=
mock
(
Polygon
.
class
);
geom
.
addPolygon
(
p
);
geom
.
clearAllContainedCheckResults
();
verify
(
p
).
clearAllContainedCheckResults
();
assertFalse
(
geom
.
containsError
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
));
}
@Test
public
void
testCollectContainedErrors
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
assertFalse
(
geom
.
containsError
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
));
geom
.
addCheckResult
(
new
CheckResult
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
,
ResultStatus
.
ERROR
,
mock
(
CheckError
.
class
)));
assertTrue
(
geom
.
containsError
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
));
Polygon
p
=
mock
(
Polygon
.
class
);
geom
.
addPolygon
(
p
);
List
<
CheckError
>
errors
=
new
ArrayList
<>();
geom
.
collectContainedErrors
(
errors
);
verify
(
p
).
collectContainedErrors
(
errors
);
assertEquals
(
1
,
errors
.
size
());
}
@Test
public
void
testContainsAnyError
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
assertFalse
(
geom
.
containsAnyError
());
geom
.
addCheckResult
(
new
CheckResult
(
CheckId
.
C_GE_P_HOLE_OUTSIDE
,
ResultStatus
.
ERROR
,
mock
(
CheckError
.
class
)));
assertTrue
(
geom
.
containsAnyError
());
Polygon
p
=
mock
(
Polygon
.
class
);
geom
.
addPolygon
(
p
);
geom
.
clearAllContainedCheckResults
();
assertFalse
(
geom
.
containsAnyError
());
verify
(
p
).
containsAnyError
();
when
(
p
.
containsAnyError
()).
thenReturn
(
true
);
assertTrue
(
geom
.
containsAnyError
());
}
@Test
public
void
testAccept
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
Check
c
=
mock
(
Check
.
class
);
geom
.
accept
(
c
);
when
(
c
.
canExecute
(
geom
)).
thenReturn
(
true
);
geom
.
accept
(
c
);
verify
(
c
).
check
(
geom
);
Polygon
p
=
mock
(
Polygon
.
class
);
geom
.
addPolygon
(
p
);
geom
.
accept
(
c
);
verify
(
p
).
accept
(
c
);
}
@Test
public
void
testGetVertices
()
{
Geometry
geom
=
GeometryTestUtils
.
createDummyGeometry
(
GeometryType
.
SOLID
);
List
<
Vertex
>
vertices
=
geom
.
getVertices
();
assertEquals
(
4
,
vertices
.
size
());
}
@Test
public
void
testGetEdgesAdjacentToVertex
()
{
Geometry
geom
=
GeometryTestUtils
.
createDummyGeometryWithInnerRingWithNeighboringPolygon
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
Polygon
polygon
=
geom
.
getPolygons
().
get
(
1
);
Vertex
vertex
=
polygon
.
getExteriorRing
().
getVertices
().
get
(
0
);
List
<
Edge
>
edges
=
geom
.
getEdgesAdjacentTo
(
vertex
);
assertEquals
(
2
,
edges
.
size
());
}
}
CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/GeometryTestUtils.java
View file @
e763994c
...
...
@@ -49,4 +49,45 @@ public class GeometryTestUtils {
return
geom
;
}
public
static
Geometry
createDummyGeometryWithInnerRingWithNeighboringPolygon
(
GeometryType
type
,
Lod
lod
)
{
Geometry
geom
=
new
Geometry
(
type
,
lod
);
ConcretePolygon
polygon
=
new
ConcretePolygon
();
geom
.
getPolygons
().
add
(
polygon
);
polygon
.
setParent
(
geom
);
LinearRing
lr
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
polygon
.
setExteriorRing
(
lr
);
Vertex
v1
=
new
Vertex
(
0
,
0
,
0
);
lr
.
getVertices
().
add
(
v1
);
Vertex
v2
=
new
Vertex
(
0
,
10
,
0
);
lr
.
getVertices
().
add
(
v2
);
Vertex
v3
=
new
Vertex
(
10
,
10
,
0
);
lr
.
getVertices
().
add
(
v3
);
Vertex
v4
=
new
Vertex
(
10
,
0
,
0
);
lr
.
getVertices
().
add
(
v4
);
lr
.
getVertices
().
add
(
v1
);
geom
.
updateEdgesAndVertices
();
LinearRing
interiorRing
=
new
LinearRing
(
LinearRingType
.
INTERIOR
);
polygon
.
addInteriorRing
(
interiorRing
);
Vertex
vi1
=
new
Vertex
(
1
,
1
,
0
);
Vertex
vi2
=
new
Vertex
(
9
,
1
,
0
);
Vertex
vi3
=
new
Vertex
(
9
,
9
,
0
);
Vertex
vi4
=
new
Vertex
(
1
,
9
,
0
);
interiorRing
.
getVertices
().
add
(
vi1
);
interiorRing
.
getVertices
().
add
(
vi2
);
interiorRing
.
getVertices
().
add
(
vi3
);
interiorRing
.
getVertices
().
add
(
vi4
);
interiorRing
.
getVertices
().
add
(
vi1
);
ConcretePolygon
polygon2
=
new
ConcretePolygon
();
geom
.
addPolygon
(
polygon2
);
LinearRing
lr2
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
polygon2
.
setExteriorRing
(
lr2
);
lr2
.
addVertex
(
v1
);
lr2
.
addVertex
(
v2
);
lr2
.
addVertex
(
new
Vertex
(
0
,
5
,
10
));
lr2
.
addVertex
(
v1
);
return
geom
;
}
}
CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/datastructure/VertexTest.java
View file @
e763994c
...
...
@@ -19,8 +19,6 @@
package
de.hft.stuttgart.citydoctor2.datastructure
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertSame
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
java.util.Set
;
...
...
@@ -46,37 +44,6 @@ public class VertexTest {
assertEquals
(
3
,
v
.
getZ
(),
0.0000001
);
}
@Test
public
void
testAddNeightbor
()
{
Vertex
v
=
new
Vertex
(
0
,
0
,
0
);
Vertex
neigh
=
new
Vertex
(
1
,
0
,
0
);
v
.
addNeighbor
(
neigh
);
assertSame
(
neigh
,
v
.
getNeighbors
().
get
(
0
));
Vertex
neigh2
=
new
Vertex
(
2
,
0
,
0
);
v
.
addNeighbor
(
neigh2
);
assertSame
(
neigh2
,
v
.
getNeighbors
().
get
(
1
));
}
@Test
public
void
testEqualsWidthNeighbor
()
{
Vertex
v
=
new
Vertex
(
0
,
0
,
0
);
Vertex
neigh
=
new
Vertex
(
1
,
0
,
0
);
v
.
addNeighbor
(
neigh
);
Vertex
other
=
new
Vertex
(
1
,
0
,
0
);
assertTrue
(
v
.
equalsWithNeighbors
(
other
));
Vertex
other2
=
new
Vertex
(
2
,
0
,
0
);
assertFalse
(
v
.
equalsWithNeighbors
(
other2
));
}
@Test
public
void
testNoNeighbors
()
{
Vertex
v
=
new
Vertex
(
1
,
2
,
3
);
assertTrue
(
v
.
getNeighbors
().
isEmpty
());
}
@Test
public
void
testGetAdjacentRings
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
...
...
@@ -95,46 +62,6 @@ public class VertexTest {
assertTrue
(
adjacentRings
.
contains
(
lr
));
}
@Test
public
void
testGetAdjacentRingsWithNeighbor
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
Polygon
p
=
new
ConcretePolygon
();
geom
.
addPolygon
(
p
);
LinearRing
lr
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
p
.
setExteriorRing
(
lr
);
Vertex
v0
=
new
Vertex
(
0
,
0
,
0
);
lr
.
addVertex
(
v0
);
Polygon
p2
=
new
ConcretePolygon
();
geom
.
addPolygon
(
p2
);
LinearRing
lr2
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
p2
.
setExteriorRing
(
lr2
);
Vertex
v1
=
new
Vertex
(
0
,
0.00000001
,
0
);
lr2
.
addVertex
(
v1
);
v0
.
addNeighbor
(
v1
);
v1
.
addNeighbor
(
v0
);
assertSame
(
v0
,
v1
.
getNeighbors
().
get
(
0
));
assertSame
(
v1
,
v0
.
getNeighbors
().
get
(
0
));
Set
<
LinearRing
>
adjacentRings
=
v0
.
getAdjacentRings
(
geom
);
assertEquals
(
2
,
adjacentRings
.
size
());
assertTrue
(
adjacentRings
.
contains
(
lr
));
assertTrue
(
adjacentRings
.
contains
(
lr2
));
adjacentRings
=
v1
.
getAdjacentRings
(
geom
);
assertEquals
(
2
,
adjacentRings
.
size
());
assertTrue
(
adjacentRings
.
contains
(
lr
));
assertTrue
(
adjacentRings
.
contains
(
lr2
));
}
@Test
(
expected
=
IllegalStateException
.
class
)
public
void
testGetAdjacentRingsGeometryNotContainingVertex
()
{
Geometry
geom
=
new
Geometry
(
GeometryType
.
SOLID
,
Lod
.
LOD1
);
...
...
CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/mapper/FeatureMapperTest.java
View file @
e763994c
...
...
@@ -19,12 +19,9 @@
package
de.hft.stuttgart.citydoctor2.mapper
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertNull
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
junit
.
Assert
.
fail
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.List
;
import
org.citygml4j.model.citygml.waterbody.WaterBody
;
import
org.citygml4j.model.gml.geometry.aggregates.MultiSurface
;
...
...
@@ -38,7 +35,6 @@ import org.junit.Test;
import
org.junit.rules.TemporaryFolder
;
import
de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel
;
import
de.hft.stuttgart.citydoctor2.datastructure.Edge
;
import
de.hft.stuttgart.citydoctor2.datastructure.Geometry
;
import
de.hft.stuttgart.citydoctor2.datastructure.Vertex
;
import
de.hft.stuttgart.citydoctor2.datastructure.WaterObject
;
...
...
@@ -79,24 +75,9 @@ public class FeatureMapperTest {
assertEquals
(
1
,
model
.
getWater
().
size
());
WaterObject
waterObject
=
model
.
getWater
().
get
(
0
);
Geometry
geometry
=
waterObject
.
getGeometries
().
get
(
0
);
assertEquals
(
2
,
geometry
.
getVertices
().
size
());
assertEquals
(
1
,
geometry
.
getVertices
().
size
());
Vertex
vertex
=
geometry
.
getVertices
().
get
(
0
);
assertEquals
(
1
,
vertex
.
getNeighbors
().
size
());
Vertex
neighbor
=
vertex
.
getNeighbors
().
get
(
0
);
if
(
vertex
.
equals
(
new
Vertex
(
0
,
0
,
0
)))
{
assertEquals
(
new
Vertex
(
0
,
0.00000001
,
0
),
neighbor
);
}
else
{
assertEquals
(
new
Vertex
(
0
,
0
,
0
),
neighbor
);
}
vertex
=
geometry
.
getVertices
().
get
(
1
);
assertEquals
(
1
,
vertex
.
getNeighbors
().
size
());
neighbor
=
vertex
.
getNeighbors
().
get
(
0
);
if
(
vertex
.
equals
(
new
Vertex
(
0
,
0
,
0
)))
{
assertEquals
(
new
Vertex
(
0
,
0.00000001
,
0
),
neighbor
);
}
else
{
assertEquals
(
new
Vertex
(
0
,
0
,
0
),
neighbor
);
}
assertEquals
(
new
Vertex
(
0
,
0
,
0
),
vertex
);
}
@Test
...
...
@@ -123,8 +104,13 @@ public class FeatureMapperTest {
Geometry
geometry
=
waterObject
.
getGeometries
().
get
(
0
);
assertEquals
(
2
,
geometry
.
getVertices
().
size
());
Vertex
vertex
=
geometry
.
getVertices
().
get
(
0
);
assertEquals
(
0
,
vertex
.
getNeighbors
().
size
());
if
(
vertex
.
equals
(
new
Vertex
(
0
,
0
,
0
)))
{
assertEquals
(
new
Vertex
(
0
,
0.00000002
,
0
),
geometry
.
getVertices
().
get
(
1
));
}
else
if
(
vertex
.
equals
(
new
Vertex
(
0
,
0.00000002
,
0
)))
{
assertEquals
(
new
Vertex
(
0
,
0
,
0
),
geometry
.
getVertices
().
get
(
1
));
}
else
{
fail
(
"Did not find both vertices again"
);
}
}
@Test
...
...
@@ -161,43 +147,8 @@ public class FeatureMapperTest {
assertEquals
(
1
,
model
.
getWater
().
size
());
WaterObject
waterObject
=
model
.
getWater
().
get
(
0
);
Geometry
geometry
=
waterObject
.
getGeometries
().
get
(
0
);
assertEquals
(
8
,
geometry
.
getVertices
().
size
());
List
<
Vertex
>
neighborVertices
=
new
ArrayList
<>();
for
(
Vertex
v
:
geometry
.
getVertices
())
{
if
(!
v
.
getNeighbors
().
isEmpty
())
{
assertEquals
(
1
,
v
.
getNeighbors
().
size
());
assertEquals
(
2
,
v
.
getAdjacentPolygons
(
geometry
).
size
());
assertEquals
(
2
,
v
.
getAdjacentRings
(
geometry
).
size
());
neighborVertices
.
add
(
v
);
}
}
assertEquals
(
2
,
neighborVertices
.
size
());
Vertex
v0
=
neighborVertices
.
get
(
0
);
Vertex
v1
=
neighborVertices
.
get
(
1
);
assertEquals
(
v0
.
getNeighbors
().
get
(
0
),
v1
);
assertEquals
(
v1
.
getNeighbors
().
get
(
0
),
v0
);
assertEquals
(
7
,
geometry
.
getVertices
().
size
());
assertEquals
(
8
,
geometry
.
getEdges
().
size
());
assertNull
(
geometry
.
getEdge
(
v0
,
v1
));
List
<
Vertex
>
ringVertices
=
geometry
.
getPolygons
().
get
(
0
).
getExteriorRing
().
getVertices
();
int
pos
=
-
1
;
for
(
int
i
=
0
;
i
<
ringVertices
.
size
();
i
++)
{
if
(
ringVertices
.
get
(
i
).
equals
(
v0
)
||
ringVertices
.
get
(
i
).
equals
(
v1
))
{
pos
=
i
;
break
;
}
}
assertTrue
(
pos
>
0
);
Vertex
prev
=
ringVertices
.
get
(
pos
-
1
);
Edge
e1
=
geometry
.
getEdge
(
v0
,
prev
);
Edge
e2
=
geometry
.
getEdge
(
v1
,
prev
);
Edge
e3
=
geometry
.
getEdge
(
prev
,
v0
);
Edge
e4
=
geometry
.
getEdge
(
prev
,
v1
);
assertEquals
(
e1
,
e2
);
assertEquals
(
e1
,
e3
);
assertEquals
(
e1
,
e4
);
assertEquals
(
e2
,
e3
);
assertEquals
(
e2
,
e4
);
assertEquals
(
e3
,
e4
);
}
private
Coord
createCoord
(
double
x
,
double
y
,
double
z
)
{
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/RingNotClosedCheck.java
View file @
e763994c
...
...
@@ -62,7 +62,7 @@ public class RingNotClosedCheck extends Check {
Vertex
last
=
lr
.
getVertices
().
get
(
lr
.
getVertices
().
size
()
-
1
);
CheckResult
cr
;
if
(!
first
.
equals
WithNeighbors
(
last
))
{
if
(!
first
.
equals
(
last
))
{
CheckError
err
=
new
RingNotClosedError
(
lr
);
cr
=
new
CheckResult
(
this
,
ResultStatus
.
ERROR
,
err
);
}
else
{
...
...
CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/geometry/TooFewPointsCheck.java
View file @
e763994c
...
...
@@ -63,25 +63,25 @@ public class TooFewPointsCheck extends Check {
public
void
check
(
LinearRing
lr
)
{
// put all vertices into a set, ignoring duplicates
Set
<
Vertex
>
vertices
=
new
HashSet
<>(
lr
.
getVertices
()
.
size
()
);
Set
<
Vertex
>
vertices
=
new
HashSet
<>(
lr
.
getVertices
());
// if the ring has two vertices which are neighbors (can be the same point)
// count them, as they don't count as a distinct point
int
numberOfContainingNeighbors
=
0
;
for
(
Vertex
v
:
lr
.
getVertices
())
{
boolean
added
=
vertices
.
add
(
v
);
if
(!
added
)
{
for
(
Vertex
neighbor
:
v
.
getNeighbors
())
{
if
(
vertices
.
contains
(
neighbor
))
{
numberOfContainingNeighbors
++;
}
}
}
}
//
int numberOfContainingNeighbors = 0;
//
//
for (Vertex v : lr.getVertices()) {
//
boolean added = vertices.add(v);
//
if (!added) {
//
for (Vertex neighbor : v.getNeighbors()) {
//
if (vertices.contains(neighbor)) {
//
numberOfContainingNeighbors++;
//
}
//
}
//
}
//
}
// 2 points or less = always error
if
(
vertices
.
size
()
-
numberOfContainingNeighbors
<
3
)
{
if
(
vertices
.
size
()
<
3
)
{
CheckError
err
=
new
RingTooFewPointsError
(
lr
);
CheckResult
cr
=
new
CheckResult
(
this
,
ResultStatus
.
ERROR
,
err
);
lr
.
addCheckResult
(
cr
);
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/ManifoldVertexCheckTest.java
View file @
e763994c
...
...
@@ -21,10 +21,6 @@ package de.hft.stuttgart.citydoctor2.checks.geometry;
import
static
de
.
hft
.
stuttgart
.
citydoctor2
.
checks
.
util
.
GeometryTestUtils
.
addPolygon
;
import
static
de
.
hft
.
stuttgart
.
citydoctor2
.
checks
.
util
.
GeometryTestUtils
.
createVertex
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
org.junit.Assert
;
import
org.junit.Test
;
...
...
@@ -62,10 +58,6 @@ public class ManifoldVertexCheckTest {
Vertex
v6
=
createVertex
(
14
,
15
,
3
,
geom
);
Vertex
v7
=
createVertex
(
14
,
16
,
3
,
geom
);
List
<
Vertex
>
vertices
=
new
ArrayList
<>();
Collections
.
addAll
(
vertices
,
v0
,
v1
,
v2
,
v3
,
v12
,
v13
,
v9
,
v10
,
v14
,
v11
,
v8
,
v5
,
v6
,
v7
);
geom
.
setVertices
(
vertices
);
addPolygon
(
geom
,
v0
,
v1
,
v2
,
v3
);
addPolygon
(
geom
,
v1
,
v6
,
v5
);
addPolygon
(
geom
,
v6
,
v7
,
v5
);
...
...
@@ -79,7 +71,8 @@ public class ManifoldVertexCheckTest {
addPolygon
(
geom
,
v12
,
v14
,
v11
,
v10
);
addPolygon
(
geom
,
v14
,
v2
,
v1
,
v11
);
addPolygon
(
geom
,
v13
,
v9
,
v0
,
v3
);
geom
.
updateEdges
();
geom
.
updateEdgesAndVertices
();
ManifoldVertexCheck
check
=
new
ManifoldVertexCheck
();
check
.
check
(
geom
);
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/geometry/NumPointsCheckTest.java
View file @
e763994c
...
...
@@ -40,11 +40,9 @@ import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration;
public
class
NumPointsCheckTest
{
@Test
public
void
testNumberOfPoints
WithNeighboringPoints
()
{
public
void
testNumberOfPoints
()
{
Vertex
v0
=
new
Vertex
(
0
,
0
,
0
);
Vertex
v1
=
new
Vertex
(
0
,
0.00000001
,
0
);
v0
.
addNeighbor
(
v1
);
v1
.
addNeighbor
(
v0
);
Vertex
v1
=
new
Vertex
(
0
,
0
,
0
);
Vertex
v2
=
new
Vertex
(
0
,
1
,
0
);
LinearRing
lr
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
...
...
@@ -64,17 +62,13 @@ public class NumPointsCheckTest {
}
@Test
public
void
testNumberOfPoints
WithNeighboringPoints
EnoughPoints
()
{
public
void
testNumberOfPointsEnoughPoints
()
{
Vertex
v0
=
new
Vertex
(
0
,
0
,
0
);
Vertex
v1
=
new
Vertex
(
0
,
0.00000001
,
0
);
v0
.
addNeighbor
(
v1
);
v1
.
addNeighbor
(
v0
);
Vertex
v2
=
new
Vertex
(
0
,
1
,
0
);
Vertex
v3
=
new
Vertex
(
0
,
2
,
0
);
LinearRing
lr
=
new
LinearRing
(
LinearRingType
.
EXTERIOR
);
lr
.
addVertex
(
v0
);
lr
.
addVertex
(
v1
);
lr
.
addVertex
(
v2
);
lr
.
addVertex
(
v3
);
lr
.
addVertex
(
v0
);
...
...
CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/checks/util/GeometryTestUtils.java
View file @
e763994c
...
...
@@ -52,7 +52,6 @@ public class GeometryTestUtils {
List
<
Vertex
>
vertices
=
new
ArrayList
<>();
Collections
.
addAll
(
vertices
,
v0
,
v1
,
v2
,
v3
,
v4
,
v5
,
v6
,
v7
);
geom
.
setVertices
(
vertices
);
addPolygon
(
geom
,
v0
,
v1
,
v2
,
v3
);
addPolygon
(
geom
,
v1
,
v5
,
v6
,
v2
);
...
...
@@ -60,7 +59,8 @@ public class GeometryTestUtils {
addPolygon
(
geom
,
v0
,
v3
,
v7
,
v4
);
addPolygon
(
geom
,
v3
,
v2
,
v6
,
v7
);
addPolygon
(
geom
,
v0
,
v4
,
v5
,
v1
);
geom
.
updateEdgesAndVertices
();
return
geom
;
}
...
...
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