/*- * 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 . */ package de.hft.stuttgart.citydoctor2.datastructure; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; import java.util.Set; 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.math.Triangle3d; import de.hft.stuttgart.citydoctor2.math.Vector3d; import de.hft.stuttgart.citydoctor2.tesselation.TesselatedPolygon; import de.hft.stuttgart.citydoctor2.utils.BoundingBoxCalculator; import de.hft.stuttgart.citydoctor2.utils.SerializablePair; /** * Representation of a geometry containing the polygons and edges * * @author Matthias Betz * */ public class Geometry extends GmlElement { private static final long serialVersionUID = 2539031030917731575L; private static Random r = new Random(); private GeometryType type; private Lod lod; private CityObject parent; private List polygons = new ArrayList<>(); private List edges; private Map, Edge> edgeMap; private List vertices; public Geometry(GeometryType type, Lod lod) { this.lod = lod; this.type = type; } void setParent(CityObject parent) { this.parent = parent; } public CityObject getParent() { return parent; } public void setType(GeometryType type) { this.type = type; } public GeometryType getType() { return type; } public Lod getLod() { return lod; } public List getPolygons() { return polygons; } public void addPolygon(Polygon cdPoly) { polygons.add(cdPoly); cdPoly.setParent(this); } public void updateEdges() { edges = new ArrayList<>(); edgeMap = new HashMap<>(); Map duplicacyMap = new HashMap<>(); for (Polygon p : polygons) { LinearRing extRing = p.getExteriorRing(); createEdgesFromRing(duplicacyMap, p, extRing); for (LinearRing inner : p.getInnerRings()) { createEdgesFromRing(duplicacyMap, p, inner); } } } void anonymize() { setGmlId(GmlId.generateId()); for (Polygon p : polygons) { p.anonymize(); } double minX = Double.MAX_VALUE; double minY = Double.MAX_VALUE; double minZ = Double.MAX_VALUE; for (Vertex v : vertices) { if (v.getX() < minX) { minX = v.getX(); } if (v.getY() < minY) { minY = v.getY(); } if (v.getZ() < minZ) { minZ = v.getZ(); } } double varX = r.nextDouble() * 10; double varY = r.nextDouble() * 10; double varZ = r.nextDouble() * 10; for (Vertex v : vertices) { v.setX(v.getX() - minX + varX); v.setY(v.getY() - minY + varY); v.setZ(v.getZ() - minZ + varZ); } } private void createEdgesFromRing(Map duplicacyMap, Polygon p, LinearRing ring) { // only go to size -1 as ring should be closed for (int i = 0; i < ring.getVertices().size() - 1; i++) { Vertex v0 = ring.getVertices().get(i + 0); Vertex v1 = ring.getVertices().get(i + 1); Edge tempEdge = new Edge(v0, v1); Edge e = duplicacyMap.get(tempEdge); if (e == null) { // no duplicate found, enter edges 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); // } e = tempEdge; edges.add(e); edgeMap.put(new SerializablePair<>(v0, v1), e); edgeMap.put(new SerializablePair<>(v1, v0), e); } else { e.addHalfEdge(v0, v1); } e.addAdjacentPolygon(p); e.addAdjacentRing(ring); } } public Vector3d getCenter() { return calculateBoundingBox().getCenter(); } /** * This will calculate two points where every other point in the geometry is * between those two. If the geometry does not contain points or is null, an * empty array is returned. This is considered an axis aligned bounding box. * Only the exterior rings of the polygons is used as inner rings should be * within the exterior or they are faulty. * * @return an array of two points. The first contains the lowest coordinates, * the second contains the highest coordinates */ public BoundingBox calculateBoundingBox() { return BoundingBoxCalculator.calculateBoundingBox(polygons); } public double calculateVolume() { double sum = 0; Vector3d center = calculateBoundingBox().getCenter(); for (Polygon p : polygons) { TesselatedPolygon tesselatedPolygon = p.tesselate(); for (Triangle3d t : tesselatedPolygon.getTriangles()) { Vector3d p1 = t.getP1().minus(center); Vector3d p2 = t.getP2().minus(center); Vector3d p3 = t.getP3().minus(center); sum += det(p1.getCoordinates(), p2.getCoordinates(), p3.getCoordinates()); } } return sum / 6d; } private double det(double[] a, double[] b, double[] c) { return -c[0] * b[1] * a[2] + b[0] * c[1] * a[2] + c[0] * a[1] * b[2] - a[0] * c[1] * b[2] - b[0] * a[1] * c[2] + a[0] * b[1] * c[2]; } @Override public boolean containsError(CheckId checkIdentifier) { boolean hasError = super.containsError(checkIdentifier); if (hasError) { return true; } for (Polygon p : polygons) { if (p.containsError(checkIdentifier)) { return true; } } return false; } @Override public void clearAllContainedCheckResults() { super.clearCheckResults(); for (Polygon p : polygons) { p.clearAllContainedCheckResults(); } } @Override public void collectContainedErrors(List errors) { super.collectContainedErrors(errors); for (Polygon p : polygons) { p.collectContainedErrors(errors); } } @Override public boolean containsAnyError() { boolean hasError = super.containsAnyError(); if (hasError) { return true; } for (Polygon p : polygons) { if (p.containsAnyError()) { return true; } } return false; } @Override public void accept(Check c) { super.accept(c); if (c.canExecute(this)) { c.check(this); } for (Polygon p : polygons) { p.accept(c); } } @Override public String toString() { return "Geometry [type=" + type + ", lod=" + lod + ", id=" + getGmlId() + "]"; } public List getVertices() { return vertices; } public List getEdges() { return edges; } public List getEdgesAdjacentTo(Vertex v) { List adjacentEdges = new ArrayList<>(); for (Edge e : edges) { if (e.getFrom() == v || e.getTo() == v) { adjacentEdges.add(e); } } return adjacentEdges; } public Edge getEdge(Vertex v1, Vertex v2) { Edge edge = edgeMap.get(new SerializablePair<>(v1, v2)); 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; } public void updateEdgesAndVertices() { updateVertices(); updateEdges(); } public void updateVertices() { Set vertexSet = new HashSet<>(); for (Polygon p : getPolygons()) { updateRing(vertexSet, p.getExteriorRing()); for (LinearRing inner : p.getInnerRings()) { updateRing(vertexSet, inner); } } vertices = new ArrayList<>(vertexSet); } private void updateRing(Set vertexSet, LinearRing ring) { for (Vertex v : ring.getVertices()) { if (vertexSet.add(v)) { // new vertex, clear adjacent rings in case new rings have been added v.clearAdjacentRings(this); } // add ring to adjacent rings of vertex v.addAdjacentRing(ring, this); } } public void removePolygon(Polygon p) { p.removeRings(); polygons.remove(p); if (p.isLink()) { Polygon original = p.getOriginal(); original.getParent().polygons.remove(original); } else if (p.isLinkedTo()) { Polygon link = p.getLinkedFromPolygon(); link.getParent().polygons.remove(link); } } public void replacePolygon(Polygon p, ConcretePolygon... polygons) { removePolygon(p); if (p.isLink()) { Geometry geom2 = p.getOriginal().getParent(); for (ConcretePolygon newPoly : polygons) { geom2.addPolygon(newPoly); addPolygon(new LinkedPolygon(newPoly, this)); } } else if (p.isLinkedTo()) { Geometry geom2 = p.getLinkedFromPolygon().getParent(); for (ConcretePolygon newPoly : polygons) { addPolygon(newPoly); geom2.addPolygon(new LinkedPolygon(newPoly, this)); } } else { for (Polygon newPoly : polygons) { addPolygon(newPoly); } } } public List getEdgesAdjacentTo(Polygon p) { List list = new ArrayList<>(); for (Edge e : edges) { if (e.getAdjacentPolygons().contains(p)) { list.add(e); } } return list; } public boolean containsPolygon(Polygon other) { for (Polygon p : polygons) { if (p.getGmlId().equals(other.getGmlId())) { return true; } } return false; } @Override public Geometry copy() { return (Geometry) super.copy(); } @Override public void prepareForChecking() { updateEdgesAndVertices(); } @Override public void clearMetaInformation() { if (vertices != null) { for (Vertex v : vertices) { v.clearAdjacentRings(); } vertices = null; } edges = null; edgeMap = null; } }