Commit 43660f7f authored by Matthias Betz's avatar Matthias Betz
Browse files

Adding default check for solids

switching proj4j to newer version not on maven
fixing an issue of polygon association when replacing a polygon in a geometry
parent 5cab90a1
Pipeline #4347 passed with stage
in 2 minutes and 33 seconds
......@@ -20,13 +20,74 @@ package de.hft.stuttgart.citydoctor2.edge;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
public class CDPolygonNs extends PolygonNs {
private List<List<HalfEdge>> innerHalfEdges = new ArrayList<>();
public static CDPolygonNs of(Polygon p, Map<Vertex, Coordinate3d> pointMap) {
List<List<Coordinate3d>> loopCoordinates = new ArrayList<>();
List<Coordinate3d> edgeExtRing = createCoordinatesFromRing(pointMap, p.getExteriorRing().getVertices());
loopCoordinates.add(edgeExtRing);
for (LinearRing innerRing : p.getInnerRings()) {
List<Coordinate3d> edgeInnerRing = createCoordinatesFromRing(pointMap, innerRing.getVertices());
loopCoordinates.add(edgeInnerRing);
}
List<List<HalfEdge>> halfEdges = new ArrayList<>();
for (List<Coordinate3d> ringCoordinates : loopCoordinates) {
List<HalfEdge> currHeList = createHalfEdgesFromCoordinates(ringCoordinates);
halfEdges.add(currHeList);
}
return new CDPolygonNs(halfEdges, p);
}
private static List<HalfEdge> createHalfEdgesFromCoordinates(List<Coordinate3d> ringCoordinates) {
List<HalfEdge> currHeList = new ArrayList<>();
HalfEdge prevHalfEdge = null;
for (int currCoordIndex = 1; currCoordIndex < ringCoordinates.size(); currCoordIndex++) {
int prevCoordIndex = currCoordIndex - 1;
Coordinate3d currCoord = ringCoordinates.get(currCoordIndex);
Coordinate3d prevCoord = ringCoordinates.get(prevCoordIndex);
HalfEdge e = new HalfEdge(prevCoord, currCoord);
if (prevHalfEdge != null) {
prevHalfEdge.setNext(e);
}
currHeList.add(e);
prevHalfEdge = e;
}
if (prevHalfEdge == null) {
throw new IllegalStateException("No half edges were created");
}
Coordinate3d start = ringCoordinates.get(0);
Coordinate3d end = ringCoordinates.get(ringCoordinates.size() - 1);
HalfEdge e = new HalfEdge(end, start);
prevHalfEdge.setNext(e);
e.setNext(currHeList.get(0));
currHeList.add(e);
return currHeList;
}
private static List<Coordinate3d> createCoordinatesFromRing(Map<Vertex, Coordinate3d> pointMap,
List<Vertex> vertices) {
List<Coordinate3d> edgeRing = new ArrayList<>();
for (int i = 0; i < vertices.size() - 1; i++) {
Vertex v = vertices.get(i);
Coordinate3d c = pointMap.computeIfAbsent(v, key -> new Coordinate3d(key.getX(), key.getY(), key.getZ()));
edgeRing.add(c);
}
return edgeRing;
}
public CDPolygonNs(List<List<HalfEdge>> halfEdges) {
super(halfEdges.get(0));
public CDPolygonNs(List<List<HalfEdge>> halfEdges, Polygon original) {
super(halfEdges.get(0), original);
for (int i = 1; i < halfEdges.size(); i++) {
List<HalfEdge> loopEdges = halfEdges.get(i);
......
......@@ -22,15 +22,23 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
public class EdgePolygon extends BaseEntity {
private Polygon original;
private List<HalfEdge> halfEdges;
public EdgePolygon(List<HalfEdge> halfEdges) {
public EdgePolygon(List<HalfEdge> halfEdges, Polygon original) {
for (HalfEdge he : halfEdges) {
addChild(he);
}
this.halfEdges = halfEdges;
this.original = original;
}
public Polygon getOriginal() {
return original;
}
public GmPlane getPlane() {
......
......@@ -434,7 +434,7 @@ public class IntersectPlanarPolygons {
HalfEdge pStartHE = he;
HalfEdge pPartnerHE = pStartHE.getPartner();
boolean bothPolysShareThisEdge = false;
while (pStartHE != pPartnerHE && false == bothPolysShareThisEdge) {
while (pStartHE != pPartnerHE && !bothPolysShareThisEdge) {
if (pPartnerHE.getPolygon() == p1 || pPartnerHE.getPolygon() == p2) {
bothPolysShareThisEdge = true;
}
......
......@@ -24,7 +24,6 @@ import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
......@@ -37,63 +36,20 @@ public class MeshSurface {
Map<Vertex, Coordinate3d> pointMap = new IdentityHashMap<>();
for (Polygon p : geom.getPolygons()) {
List<List<Coordinate3d>> loopCoordinates = new ArrayList<>();
List<Coordinate3d> edgeExtRing = createCoordinatesFromRing(pointMap, p.getExteriorRing().getVertices());
loopCoordinates.add(edgeExtRing);
for (LinearRing innerRing : p.getInnerRings()) {
List<Coordinate3d> edgeInnerRing = createCoordinatesFromRing(pointMap, innerRing.getVertices());
loopCoordinates.add(edgeInnerRing);
}
List<List<HalfEdge>> halfEdges = new ArrayList<>();
for (List<Coordinate3d> ringCoordinates : loopCoordinates) {
List<HalfEdge> currHeList = createHalfEdgesFromCoordinates(ringCoordinates);
halfEdges.add(currHeList);
}
CDPolygonNs newPolygon = new CDPolygonNs(halfEdges);
polygonList.add(newPolygon);
CDPolygonNs poly = CDPolygonNs.of(p, pointMap);
polygonList.add(poly);
// HalfEdge firstHalfEdge = poly.getFirstHalfEdge();
// System.out.println();
// System.out.println(firstHalfEdge.getStart());
// System.out.println(firstHalfEdge.getEnd());
// HalfEdge next = firstHalfEdge.getNext();
// while (next != firstHalfEdge) {
// System.out.println(next.getEnd());
// next = next.getNext();
// }
}
return new MeshSurface(polygonList);
}
private static List<Coordinate3d> createCoordinatesFromRing(Map<Vertex, Coordinate3d> pointMap,
List<Vertex> vertices) {
List<Coordinate3d> edgeRing = new ArrayList<>();
for (int i = 0; i < vertices.size() - 1; i++) {
Vertex v = vertices.get(i);
Coordinate3d c = pointMap.computeIfAbsent(v, key -> new Coordinate3d(key.getX(), key.getY(), key.getZ()));
edgeRing.add(c);
}
return edgeRing;
}
private static List<HalfEdge> createHalfEdgesFromCoordinates(List<Coordinate3d> ringCoordinates) {
List<HalfEdge> currHeList = new ArrayList<>();
HalfEdge prevHalfEdge = null;
for (int currCoordIndex = 1; currCoordIndex < ringCoordinates.size(); currCoordIndex++) {
int prevCoordIndex = currCoordIndex - 1;
Coordinate3d currCoord = ringCoordinates.get(currCoordIndex);
Coordinate3d prevCoord = ringCoordinates.get(prevCoordIndex);
HalfEdge e = new HalfEdge(prevCoord, currCoord);
if (prevHalfEdge != null) {
prevHalfEdge.setNext(e);
}
currHeList.add(e);
prevHalfEdge = e;
}
if (prevHalfEdge == null) {
throw new IllegalStateException("No half edges were created");
}
Coordinate3d start = ringCoordinates.get(0);
Coordinate3d end = ringCoordinates.get(ringCoordinates.size() - 1);
HalfEdge e = new HalfEdge(end, start);
prevHalfEdge.setNext(e);
e.setNext(currHeList.get(0));
currHeList.add(e);
return currHeList;
}
public MeshSurface(List<CDPolygonNs> polygons) {
this.polygons = polygons;
......
......@@ -20,10 +20,12 @@ package de.hft.stuttgart.citydoctor2.edge;
import java.util.List;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
public class PolygonNs extends EdgePolygon {
public PolygonNs(List<HalfEdge> list) {
super(list);
public PolygonNs(List<HalfEdge> list, Polygon original) {
super(list, original);
}
}
......
......@@ -51,5 +51,11 @@ public class PolygonPolygonIntersection {
public EdgePolygon getPolygon2() {
return p2;
}
@Override
public String toString() {
return "PolygonPolygonIntersection [p1=" + p1 + ", p2=" + p2 + ", polyLine=" + polyLine + ", type=" + type
+ "]";
}
}
......
/*-
* Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart
*
* This file is part of CityDoctor2.
*
* CityDoctor2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CityDoctor2 is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CityDoctor2. If not, see <https://www.gnu.org/licenses/>.
*/
package de.hft.stuttgart.citydoctor2.edge;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.junit.Test;
import de.hft.stuttgart.citydoctor2.datastructure.ConcretePolygon;
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.LinearRing.LinearRingType;
import de.hft.stuttgart.citydoctor2.datastructure.Lod;
import de.hft.stuttgart.citydoctor2.datastructure.Vertex;
public class IntersectionErrorsTest {
@Test
public void testWrongIntersection() {
Geometry geom = new Geometry(GeometryType.SOLID, Lod.LOD1);
ConcretePolygon p1 = new ConcretePolygon();
LinearRing ext1 = new LinearRing(LinearRingType.EXTERIOR);
p1.setExteriorRing(ext1);
/*
Vertex [x=3515051.7, y=5405988.05, z=246.67]
Vertex [x=3515047.52, y=5405985.1, z=246.67]
Vertex [x=3515049.03, y=5405978.98, z=246.67]
Vertex [x=3515054.29, y=5405983.65, z=246.67]
Vertex [x=3515051.7, y=5405988.05, z=246.67]
*/
Vertex v1 = new Vertex(3515047.52, 5405985.1, 246.67);
Vertex v2 = new Vertex(3515049.03, 5405978.98, 246.67);
Vertex v3 = new Vertex(3515054.29, 5405983.65, 246.67);
Vertex v4 = new Vertex(3515051.7, 5405988.05, 246.67);
ext1.addVertex(v1);
ext1.addVertex(v2);
ext1.addVertex(v3);
ext1.addVertex(v4);
ext1.addVertex(v1);
ConcretePolygon p2 = new ConcretePolygon();
LinearRing ext2 = new LinearRing(LinearRingType.EXTERIOR);
p2.setExteriorRing(ext2);
/*
Vertex [x=3515049.03, y=5405978.98, z=237.903218]
Vertex [x=3515054.29, y=5405983.65, z=237.903218]
Vertex [x=3515054.29, y=5405983.65, z=246.67]
Vertex [x=3515049.03, y=5405978.98, z=246.67]
Vertex [x=3515049.03, y=5405978.98, z=237.903218]
*/
Vertex v5 = new Vertex(3515049.03, 5405978.98, 237.903218);
Vertex v6 = new Vertex(3515054.29, 5405983.65, 237.903218);
Vertex v7 = v3;
Vertex v8 = v2;
ext2.addVertex(v5);
ext2.addVertex(v6);
ext2.addVertex(v7);
ext2.addVertex(v8);
ext2.addVertex(v5);
geom.addPolygon(p1);
geom.addPolygon(p2);
geom.prepareForChecking();
System.out.println();
for (Vertex v : p1.getExteriorRing().getVertices()) {
System.out.println(v);
}
System.out.println();
for (Vertex v : p2.getExteriorRing().getVertices()) {
System.out.println(v);
}
MeshSurface meshSurface = MeshSurface.of(geom);
List<CDPolygonNs> polygons = meshSurface.getPolygons();
assertEquals(2, polygons.size());
CDPolygonNs edgePoly1 = meshSurface.getPolygons().get(0);
CDPolygonNs edgePoly2 = meshSurface.getPolygons().get(1);
List<PolygonPolygonIntersection> intersectPolygons = IntersectPlanarPolygons.intersectPolygons(edgePoly1, edgePoly2, 0.000001, 0.001);
assertTrue(intersectPolygons.isEmpty());
}
}
......@@ -58,7 +58,7 @@
<artifactId>snakeyaml</artifactId>
</dependency>
<dependency>
<groupId>org.osgeo</groupId>
<groupId>org.locationtech.proj4j</groupId>
<artifactId>proj4j</artifactId>
</dependency>
</dependencies>
......
......@@ -19,6 +19,7 @@
package de.hft.stuttgart.citydoctor2.check;
import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError;
import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError;
import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError;
import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError;
import de.hft.stuttgart.citydoctor2.check.error.MultipleConnectedComponentsError;
......@@ -187,6 +188,10 @@ public interface HealingMethod {
default boolean visit(SurfaceUnfragmentedError err, ModificationListener l) {
return false;
}
default boolean visit(AttributeMissingError err, ModificationListener l) {
return false;
}
public HealingMethod createNew();
......
......@@ -371,4 +371,20 @@ public class CityDoctorModel {
}
buildings.set(index, (Building) nextFeature);
}
public void addCityObject(CityObject co) {
if (co instanceof Building) {
buildings.add((Building) co);
} else if (co instanceof BridgeObject) {
bridges.add((BridgeObject) co);
} else if (co instanceof TransportationObject) {
roads.add((TransportationObject) co);
} else if (co instanceof Vegetation) {
vegetation.add((Vegetation) co);
} else if (co instanceof LandObject) {
land.add((LandObject) co);
} else if (co instanceof WaterObject) {
water.add((WaterObject) co);
}
}
}
......@@ -53,6 +53,11 @@ public class ConcretePolygon extends Polygon {
*
* @see de.hft.stuttgart.citydoctor2.datastructure.Polygon#calculateNormal()
*/
@Override
public Vector3d calculateNormalNormalized() {
return exterior.calculateNormalNormalized();
}
@Override
public Vector3d calculateNormal() {
return exterior.calculateNormal();
......
......@@ -89,6 +89,16 @@ public class Geometry extends GmlElement {
public void addPolygon(Polygon cdPoly) {
polygons.add(cdPoly);
cdPoly.setParent(this);
// set part of boundary surface or building installation or both
if (parent instanceof BoundarySurface) {
BoundarySurface bs = (BoundarySurface) parent;
cdPoly.setPartOfSurface(bs);
if (bs.getParent() instanceof BuildingInstallation) {
cdPoly.setPartOfInstallation((BuildingInstallation) bs.getParent());
}
} else if (parent instanceof BuildingInstallation) {
cdPoly.setPartOfInstallation((BuildingInstallation) parent);
}
}
public void updateEdges() {
......
......@@ -59,7 +59,7 @@ public class LinearRing extends GmlElement {
// project to 2d ring
List<Vector2d> projectedRing = new ArrayList<>();
Vector2d point;
Vector3d normal = calculateNormal();
Vector3d normal = calculateNormalNormalized();
double x = Math.abs(normal.getX());
double y = Math.abs(normal.getY());
......@@ -135,7 +135,7 @@ public class LinearRing extends GmlElement {
*
* @return the normal as a normalized vector
*/
public Vector3d calculateNormal() {
public Vector3d calculateNormalNormalized() {
double[] coords = new double[3];
for (int i = 0; i < vertices.size() - 1; i++) {
Vertex current = vertices.get(i + 0);
......@@ -161,6 +161,38 @@ public class LinearRing extends GmlElement {
v.normalize();
return v;
}
/**
* Calculates the normal vector of the ring. Method by Newell. If the Newell
* method would return a (0, 0, 0) vector a cross product is formed from the
* first 3 vertices. If there are no 3 vertices available (1, 0, 0) is returned.
*
* @return the normal vector
*/
public Vector3d calculateNormal() {
double[] coords = new double[3];
for (int i = 0; i < vertices.size() - 1; i++) {
Vertex current = vertices.get(i + 0);
Vertex next = vertices.get(i + 1);
coords[0] += (current.getZ() + next.getZ()) * (current.getY() - next.getY());
coords[1] += (current.getX() + next.getX()) * (current.getZ() - next.getZ());
coords[2] += (current.getY() + next.getY()) * (current.getX() - next.getX());
}
if (coords[0] == 0 && coords[1] == 0 && coords[2] == 0) {
// no valid normal vector found
if (vertices.size() < 3) {
// no three points, return x-axis
return new Vector3d(1, 0, 0);
}
Vertex v1 = vertices.get(0);
Vertex v2 = vertices.get(1);
Vertex v3 = vertices.get(2);
return calculateNormalWithCross(v1, v2, v3);
}
return new Vector3d(coords);
}
private Vector3d calculateNormalWithCross(Vertex v1, Vertex v2, Vertex v3) {
Vector3d dir1 = v2.minus(v1);
......
......@@ -132,6 +132,11 @@ public class LinkedPolygon extends Polygon {
poly.clearCheckResults();
}
@Override
public Vector3d calculateNormalNormalized() {
return poly.calculateNormalNormalized();
}
@Override
public Vector3d calculateNormal() {
return poly.calculateNormal();
......
......@@ -33,6 +33,7 @@ public abstract class Polygon extends GmlElement {
private static final long serialVersionUID = -613942946364706513L;
public abstract Vector3d calculateNormalNormalized();
public abstract Vector3d calculateNormal();
public abstract TesselatedPolygon tesselate();
......
......@@ -39,8 +39,8 @@ import org.citygml4j.model.gml.geometry.primitives.PosOrPointPropertyOrPointRep;
import org.citygml4j.model.gml.geometry.primitives.SolidProperty;
import org.citygml4j.model.gml.geometry.primitives.SurfaceProperty;
import org.citygml4j.util.walker.GeometryWalker;
import org.osgeo.proj4j.BasicCoordinateTransform;
import org.osgeo.proj4j.ProjCoordinate;
import org.locationtech.proj4j.BasicCoordinateTransform;
import org.locationtech.proj4j.ProjCoordinate;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingInstallation;
......@@ -171,7 +171,9 @@ public class GeometryMapper extends GeometryWalker {
ConcretePolygon cdPoly) {
if (gmlPoly.isSetId()) {
cdPoly.setGmlId(new GmlId(gmlPoly.getId()));
polygons.put(gmlPoly.getId(), cdPoly);
if (polygons.put(gmlPoly.getId(), cdPoly) != null) {
logger.warn("Found already existing polygon ID {}, duplicate IDs are not valid", gmlPoly.getId());
}
}
}
......
......@@ -38,7 +38,7 @@ public class Polygon2d {
private List<Ring2d> innerRings;
public static Polygon2d withProjection(Polygon poly) {
Vector3d normal = poly.calculateNormal();
Vector3d normal = poly.calculateNormalNormalized();
int[] axis = getProjectionAxis(normal);
return projectTo2D(poly, axis);
}
......
......@@ -33,7 +33,7 @@ public class PolygonUtils {
}
public static Matrix3x3d calculateRotationMatrix(Polygon p) {
Vector3d normal = p.calculateNormal();
Vector3d normal = p.calculateNormalNormalized();
Vector3d zAxis = new Vector3d(0d, 0d, 1d);
Quaternion rot = Quaternion.fromToRotation(normal, zAxis);