package de.hft.stuttgart.citygml.green.osm; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Polygon; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; import org.xmlobjects.gml.model.geometry.primitives.SurfaceProperty; public class TreeGenerator { private static final double TRUNK_INCREASE_FACTOR = 1.2; public static MultiSurface generateTree(Coordinate point, double trunkRadius, double trunkHeight, double crownXRadius, double crownYRadius, double crownHeight) { List trunkWestCoordinateList = new ArrayList<>(); trunkWestCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y - trunkRadius, 0)); double drawTrunkHeight = trunkHeight * TRUNK_INCREASE_FACTOR; trunkWestCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y - trunkRadius, drawTrunkHeight)); trunkWestCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y + trunkRadius, drawTrunkHeight)); trunkWestCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y + trunkRadius, 0)); trunkWestCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y - trunkRadius, 0)); Polygon trunkWestPolygon = convertPolygon(trunkWestCoordinateList); var trunkWestConvertedPolygon = GreenEnricher.convertToCityGmlPoly(trunkWestPolygon); MultiSurface ms = new MultiSurface(); ms.getSurfaceMember().add(new SurfaceProperty(trunkWestConvertedPolygon)); List trunkEastCoordinateList = new ArrayList<>(); trunkEastCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y - trunkRadius, 0)); trunkEastCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y + trunkRadius, 0)); trunkEastCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y + trunkRadius, drawTrunkHeight)); trunkEastCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y - trunkRadius, drawTrunkHeight)); trunkEastCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y - trunkRadius, 0)); Polygon trunkEastPolygon = convertPolygon(trunkEastCoordinateList); var trunkEastConvertedPolygon = GreenEnricher.convertToCityGmlPoly(trunkEastPolygon); ms.getSurfaceMember().add(new SurfaceProperty(trunkEastConvertedPolygon)); List trunkSouthCoordinateList = new ArrayList<>(); trunkSouthCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y - trunkRadius, 0)); trunkSouthCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y - trunkRadius, 0)); trunkSouthCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y - trunkRadius, drawTrunkHeight)); trunkSouthCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y - trunkRadius, drawTrunkHeight)); trunkSouthCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y - trunkRadius, 0)); Polygon trunkSouthPolygon = convertPolygon(trunkSouthCoordinateList); var trunkSouthConvertedPolygon = GreenEnricher.convertToCityGmlPoly(trunkSouthPolygon); ms.getSurfaceMember().add(new SurfaceProperty(trunkSouthConvertedPolygon)); List trunkNorthCoordinateList = new ArrayList<>(); trunkNorthCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y + trunkRadius, 0)); trunkNorthCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y + trunkRadius, drawTrunkHeight)); trunkNorthCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y + trunkRadius, drawTrunkHeight)); trunkNorthCoordinateList.add(new Coordinate(point.x + trunkRadius, point.y + trunkRadius, 0)); trunkNorthCoordinateList.add(new Coordinate(point.x - trunkRadius, point.y + trunkRadius, 0)); Polygon trunkNorthPolygon = convertPolygon(trunkNorthCoordinateList); var trunkNorthConvertedPolygon = GreenEnricher.convertToCityGmlPoly(trunkNorthPolygon); ms.getSurfaceMember().add(new SurfaceProperty(trunkNorthConvertedPolygon)); /*- * Spheroid formula: * x = a * sin(thetha) * cos(phi) * y = b * sin(thetha) * sin(phi) * z = c * cos(thetha) * * 0 <= theta <= pi * 0 <= phi < 2 pi */ // create crown // divide by 2 to get radius, height is diameter double c = crownHeight / 2D; double a = crownXRadius; double b = crownYRadius; int numberOfThetaRings = 5; int numberOfPhiRings = 10; double thetaDelta = Math.PI / (numberOfThetaRings - 1); double phiDelta = Math.PI * 2 / numberOfPhiRings; List> ringCoordinates = new ArrayList<>(); // top point triangles Coordinate topPoint = new Coordinate(point.x, point.y, point.z + c + trunkHeight + c); ringCoordinates.add(Collections.singletonList(topPoint)); for (int i = 1; i < numberOfThetaRings - 1; i++) { double theta = i * thetaDelta; List coords = new ArrayList<>(); ringCoordinates.add(coords); for (int j = 0; j < numberOfPhiRings; j++) { double phi = j * phiDelta; double x = a * Math.sin(theta) * Math.cos(phi) + point.x; double y = b * Math.sin(theta) * Math.sin(phi) + point.y; double z = c * Math.cos(theta) + point.z + trunkHeight + c; Coordinate p1 = new Coordinate(x, y, z); coords.add(p1); } } Coordinate bottomPoint = new Coordinate(point.x, point.y, point.z + trunkHeight); ringCoordinates.add(Collections.singletonList(bottomPoint)); List secondRing = ringCoordinates.get(1); for (int i = 0; i < secondRing.size(); i++) { Coordinate[] coordArray = new Coordinate[] { topPoint, secondRing.get(i), secondRing.get((i + 1) % secondRing.size()), topPoint }; Polygon polygon = GreenEnricher.GEOM_FACTORY.createPolygon(coordArray); var convertedPoly = GreenEnricher.convertToCityGmlPoly(polygon); ms.getSurfaceMember().add(new SurfaceProperty(convertedPoly)); } for (int i = 1; i < ringCoordinates.size() - 2; i++) { List topRing = ringCoordinates.get(i); List bottomRing = ringCoordinates.get(i + 1); for (int j = 0; j < topRing.size(); j++) { int nextRingIndex = (j + 1) % topRing.size(); Coordinate p1 = topRing.get(j); Coordinate p2 = topRing.get(nextRingIndex); Coordinate p3 = bottomRing.get(j); Coordinate p4 = bottomRing.get(nextRingIndex); Coordinate[] coordArray = new Coordinate[] { p1, p3, p4, p2, p1 }; Polygon polygon = GreenEnricher.GEOM_FACTORY.createPolygon(coordArray); var convertedPoly = GreenEnricher.convertToCityGmlPoly(polygon); ms.getSurfaceMember().add(new SurfaceProperty(convertedPoly)); } } // bottom point triangles List bottomRing = ringCoordinates.get(ringCoordinates.size() - 2); for (int i = 0; i < bottomRing.size(); i++) { int nextRingIndex = (i + 1) % bottomRing.size(); Coordinate p1 = bottomRing.get(i); Coordinate p2 = bottomRing.get(nextRingIndex); Coordinate[] coordArray = new Coordinate[] { p2, p1, bottomPoint, p2 }; Polygon polygon = GreenEnricher.GEOM_FACTORY.createPolygon(coordArray); var convertedPoly = GreenEnricher.convertToCityGmlPoly(polygon); ms.getSurfaceMember().add(new SurfaceProperty(convertedPoly)); } return ms; } private static Polygon convertPolygon(List trunkWestCoordinateList) { return GreenEnricher.GEOM_FACTORY .createPolygon(trunkWestCoordinateList.toArray(new Coordinate[trunkWestCoordinateList.size()])); } }