diff --git a/enrich-citygml-with-greenarea/pom.xml b/enrich-citygml-with-greenarea/pom.xml index 344cc07e79d5a466a269f55134d5c554764639db..995be0a3753bfb186e257f471715f4135a2e0a6a 100644 --- a/enrich-citygml-with-greenarea/pom.xml +++ b/enrich-citygml-with-greenarea/pom.xml @@ -10,7 +10,27 @@ <maven.compiler.target>17</maven.compiler.target> </properties> + <repositories> + <repository> + <id>osgeo</id> + <name>OSGeo Release Repository</name> + <url>https://repo.osgeo.org/repository/release/</url> + <snapshots> + <enabled>false</enabled> + </snapshots> + <releases> + <enabled>true</enabled> + </releases> + </repository> + </repositories> + <dependencies> + <!-- https://mvnrepository.com/artifact/org.geotools/gt-shapefile --> + <dependency> + <groupId>org.geotools</groupId> + <artifactId>gt-shapefile</artifactId> + <version>24.5</version> + </dependency> <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --> <dependency> <groupId>org.junit.jupiter</groupId> diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/kataster/StandardTreeFinder.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/kataster/StandardTreeFinder.java new file mode 100644 index 0000000000000000000000000000000000000000..d3f5032bf896519df12e906f79a03846754fa13c --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/kataster/StandardTreeFinder.java @@ -0,0 +1,98 @@ +package de.hft.stuttgart.citygml.green.kataster; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.IntSummaryStatistics; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +import org.geotools.data.DataStore; +import org.geotools.data.DataStoreFinder; +import org.geotools.data.FeatureSource; +import org.geotools.feature.FeatureCollection; +import org.geotools.feature.FeatureIterator; +import org.opengis.feature.simple.SimpleFeature; +import org.opengis.feature.simple.SimpleFeatureType; + +public class StandardTreeFinder { + + public static void main(String[] args) throws IOException { + + File f = new File("data/Baum.shp"); + + Map<String, AtomicInteger> treeTypes = new HashMap<>(); + List<Integer> ages = new ArrayList<>(); + List<Integer> treeHeights = new ArrayList<>(); + List<Float> crownWidths = new ArrayList<>(); + List<Integer> trunkCircs = new ArrayList<>(); + + Map<String, Object> map = new HashMap<>(); + map.put("url", f.toURI().toURL()); + map.put("charset", StandardCharsets.UTF_8); + + DataStore dataStore = DataStoreFinder.getDataStore(map); + String typeName = dataStore.getTypeNames()[0]; + + FeatureSource<SimpleFeatureType, SimpleFeature> source = + dataStore.getFeatureSource(typeName); + + int featureCount = 0; + FeatureCollection<SimpleFeatureType, SimpleFeature> collection = source.getFeatures(); + try (FeatureIterator<SimpleFeature> features = collection.features()) { + while (features.hasNext()) { + featureCount++; + SimpleFeature feature = features.next(); + String type = feature.getAttribute("Baumart").toString(); + treeTypes.compute(type, (k, v) -> { + if (v == null) { + v = new AtomicInteger(0); + } + v.incrementAndGet(); + return v; + }); + Object ageObject = feature.getAttribute("Alter am S"); + if (ageObject != null) { + ages.add(Integer.parseInt(ageObject.toString())); + } + Object treeHeight = feature.getAttribute("Baumhöhe"); + if (treeHeight != null) { + treeHeights.add(Integer.parseInt(treeHeight.toString())); + } + Object crownWidth = feature.getAttribute("Kronenbrei"); + if (crownWidth != null) { + crownWidths.add(Float.parseFloat(crownWidth.toString())); + } + Object trunkCirc = feature.getAttribute("Stammumfan"); + if (trunkCirc != null) { + trunkCircs.add(Integer.parseInt(trunkCirc.toString())); + } + } + } + + System.out.println("Number of trees: " + featureCount); + + for (Entry<String, AtomicInteger> e : treeTypes.entrySet()) { + System.out.println(e.getKey() + ": " + e.getValue()); + } + + System.out.println(); + IntSummaryStatistics treeHeightsStatistics = treeHeights.stream().collect(Collectors.summarizingInt(Integer::intValue)); + System.out.println("TreeHeightAverage: " + treeHeightsStatistics.getAverage()); + + IntSummaryStatistics agesStatistics = ages.stream().collect(Collectors.summarizingInt(Integer::intValue)); + System.out.println("AverageAge: " + agesStatistics); + + Double averageCrown = crownWidths.stream().collect(Collectors.averagingDouble(Float::doubleValue)); + System.out.println("AverageCrownWidth: " + averageCrown); + + Double averageTrunkCirc = trunkCircs.stream().collect(Collectors.averagingInt(Integer::intValue)); + System.out.println("AverageTrunkCirc: " + averageTrunkCirc); + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/GreenEnricher.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/GreenEnricher.java index 101ac9eca5f881474de38115d2a86af16dffbebe..b6346294a8105d6dde7c7da09ae5c7a232d94fda 100644 --- a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/GreenEnricher.java +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/GreenEnricher.java @@ -112,6 +112,8 @@ public class GreenEnricher { System.out.println("Reading CityGML file"); Path inFile = Paths.get(args[0]); CityModel cityModel = readCityGml(inFile); + + String epsgCode = extractEpsgCode(cityModel); sourceCRS = CRS_FACTORY.createFromName(epsgCode); @@ -123,7 +125,7 @@ public class GreenEnricher { // HttpResponse<String> response = getOsmData(boundingBoxString); // Files.write(Path.of("osm_response.xml"), response.body().getBytes(StandardCharsets.UTF_8)); // String osmResponse = response.body(); - String osmResponse = Files.readString(Paths.get("osm_response.xml")); + String osmResponse = Files.readString(Paths.get("data", "osm_response.xml")); System.out.println("Parsing OSM response"); parseOsmResponse(osmResponse, osmData); @@ -146,12 +148,12 @@ public class GreenEnricher { for (TreePoint tp : osmData.getTreePoints()) { double trunkRadius = 0.2; - double trunkHeight = 1; + double trunkHeight = 3; Coordinate coordinate = tp.getPoint().getCoordinate(); if (Double.isNaN(coordinate.z)) { coordinate.z = 0; } - MultiSurface generatedTree = TreeGenerator.generateTree(coordinate, trunkRadius, trunkHeight, 5, 5, 10); + MultiSurface generatedTree = TreeGenerator.generateTree(coordinate, trunkRadius, trunkHeight, 3, 3, 10); SolitaryVegetationObject cover = new SolitaryVegetationObject(); cover.setId(UUID.randomUUID().toString()); cover.setLod2MultiSurface(new MultiSurfaceProperty(generatedTree)); @@ -166,7 +168,7 @@ public class GreenEnricher { String inputString = inFile.getFileName().toString(); String inputPathWithoutFileEnding = inputString.substring(0, inputString.lastIndexOf('.')); - Path outputPath = Paths.get(inputPathWithoutFileEnding + "_with_greens.gml"); + Path outputPath = Paths.get("data", inputPathWithoutFileEnding + "_with_greens.gml"); System.out.println("Writing output file."); writeCityGML(cityModel, outputPath); System.out.println("Done"); diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/Tree.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/Tree.java new file mode 100644 index 0000000000000000000000000000000000000000..ba8b68ff93f0645bf8771d8747c619e6d7acc974 --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/Tree.java @@ -0,0 +1,53 @@ +package de.hft.stuttgart.citygml.green.osm; + +import org.locationtech.jts.geom.Coordinate; + +public class Tree { + + private Coordinate point; + private double trunkHeight; + private double trunkRadius; + private double crownRadius; + private double crownHeight; + + public Coordinate getPoint() { + return point; + } + + public void setPoint(Coordinate point) { + this.point = point; + } + + public double getTrunkHeight() { + return trunkHeight; + } + + public void setTrunkHeight(double trunkHeight) { + this.trunkHeight = trunkHeight; + } + + public double getTrunkRadius() { + return trunkRadius; + } + + public void setTrunkRadius(double trunkRadius) { + this.trunkRadius = trunkRadius; + } + + public double getCrownRadius() { + return crownRadius; + } + + public void setCrownRadius(double crownRadius) { + this.crownRadius = crownRadius; + } + + public double getCrownHeight() { + return crownHeight; + } + + public void setCrownHeight(double crownHeight) { + this.crownHeight = crownHeight; + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeKatasterData.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeKatasterData.java new file mode 100644 index 0000000000000000000000000000000000000000..fac9652025825d78b5d7160873596976cb21d811 --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeKatasterData.java @@ -0,0 +1,17 @@ +package de.hft.stuttgart.citygml.green.osm; + +import java.util.ArrayList; +import java.util.List; + +public class TreeKatasterData { + + private List<Tree> trees; + + public List<Tree> getTrees() { + if (trees == null) { + trees = new ArrayList<>(); + } + return trees; + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/yoc/analysis/YearOfConstructionAnalyzer.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/yoc/analysis/YearOfConstructionAnalyzer.java new file mode 100644 index 0000000000000000000000000000000000000000..8f11b335c7a7d8ba39a7d107d406d02eeb8e5af3 --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/yoc/analysis/YearOfConstructionAnalyzer.java @@ -0,0 +1,113 @@ +package de.hft.stuttgart.citygml.yoc.analysis; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.atomic.AtomicInteger; + +import org.citygml4j.core.model.CityGMLVersion; +import org.citygml4j.core.model.building.Building; +import org.citygml4j.core.model.core.AbstractCityObject; +import org.citygml4j.core.model.core.AbstractCityObjectProperty; +import org.citygml4j.core.model.core.AbstractFeature; +import org.citygml4j.core.model.core.CityModel; +import org.citygml4j.xml.CityGMLContext; +import org.citygml4j.xml.CityGMLContextException; +import org.citygml4j.xml.module.citygml.CoreModule; +import org.citygml4j.xml.reader.CityGMLInputFactory; +import org.citygml4j.xml.reader.CityGMLReadException; +import org.citygml4j.xml.reader.CityGMLReader; +import org.citygml4j.xml.writer.CityGMLOutputFactory; +import org.citygml4j.xml.writer.CityGMLWriteException; +import org.citygml4j.xml.writer.CityGMLWriter; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.Point; +import org.xmlobjects.gml.model.geometry.DirectPosition; + +public class YearOfConstructionAnalyzer { + + public static void main(String[] args) throws CityGMLContextException, CityGMLReadException, CityGMLWriteException { + Path inputFile = Paths.get("data", "Grombühl_v3.gml"); + CityModel cityModel = readCityGml(inputFile); + List<AbstractCityObjectProperty> cityObjectMembers = cityModel.getCityObjectMembers(); + Map<String, AtomicInteger> missingYocUsageCount = new HashMap<>(); + + GeometryFactory factory = new GeometryFactory(); + Map<Point, Building> centersMap = new HashMap<>(); + + for (AbstractCityObjectProperty acop : cityObjectMembers) { + AbstractCityObject abstractCityObject = acop.getObject(); + if (abstractCityObject instanceof Building b) { + DirectPosition center = b.computeEnvelope().getCenter(); + Point point = factory.createPoint(new Coordinate(center.getValue().get(0), center.getValue().get(1))); + centersMap.put(point, b); + } + } + + for (AbstractCityObjectProperty acop : cityObjectMembers) { + AbstractCityObject abstractCityObject = acop.getObject(); + if (abstractCityObject instanceof Building b && b.getDateOfConstruction() == null) { + String function = b.getFunctions().get(0).getValue(); + if ("31001_1000".equals(function)) { + DirectPosition center = b.computeEnvelope().getCenter(); + double lowestDistance = Double.MAX_VALUE; + Point point = factory.createPoint(new Coordinate(center.getValue().get(0), center.getValue().get(1))); + Building nearestBuilding = null; + for (Entry<Point, Building> e : centersMap.entrySet()) { + if (e.getValue() == b) { + continue; + } + if (e.getKey().distance(point) < lowestDistance && e.getValue().getDateOfConstruction() != null) { + lowestDistance = e.getKey().distance(point); + nearestBuilding = e.getValue(); + } + } + + if (nearestBuilding == null) { + throw new IllegalStateException(); + } + System.out.println("Assigning " + b.getId() + " yoc of: " + nearestBuilding.getDateOfConstruction()); + b.setDateOfConstruction(nearestBuilding.getDateOfConstruction()); + } + missingYocUsageCount.compute(function, (k, v) -> { + if (v == null) { + v = new AtomicInteger(0); + } + v.getAndIncrement(); + return v; + }); + } + } + writeCityGML(cityModel, Paths.get("out.gml")); + } + + private static CityModel readCityGml(Path inFile) throws CityGMLContextException, CityGMLReadException { + CityGMLContext context = CityGMLContext.newInstance(); + CityGMLInputFactory in = context.createCityGMLInputFactory(); + try (CityGMLReader reader = in.createCityGMLReader(inFile)) { + AbstractFeature feature = reader.next(); + if (feature instanceof CityModel cm) { + return cm; + } + } + throw new IllegalStateException("CityGML does not contain a CityModel feature type"); + } + + private static void writeCityGML(CityModel cityModel, Path outputPath) + throws CityGMLWriteException, CityGMLContextException { + CityGMLContext context = CityGMLContext.newInstance(); + CityGMLVersion version = CityGMLVersion.v2_0; + CityGMLOutputFactory out = context.createCityGMLOutputFactory(version); + + try (CityGMLWriter writer = out.createCityGMLWriter(outputPath, StandardCharsets.UTF_8.name())) { + writer.withIndent(" ").withDefaultSchemaLocations().withDefaultPrefixes() + .withDefaultNamespace(CoreModule.of(version).getNamespaceURI()).write(cityModel); + } + } + +}