diff --git a/enrich-citygml-with-greenarea/.gitignore b/enrich-citygml-with-greenarea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f190e4bc19841cf1aa289fc09f5b260ea70f0b82 --- /dev/null +++ b/enrich-citygml-with-greenarea/.gitignore @@ -0,0 +1,274 @@ + +# Created by https://www.gitignore.io/api/java,maven,macos,linux,eclipse,windows,netbeans,intellij +# Edit at https://www.gitignore.io/?templates=java,maven,macos,linux,eclipse,windows,netbeans,intellij + + +# User specific +.sonarlint/ +Servers/ + +### Eclipse ### + +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +### Eclipse Patch ### +# Eclipse Core +.project + +# JDT-specific (Eclipse Java Development Tools) +.classpath + +# Annotation Processing +.apt_generated + +.sts4-cache/ + +### Intellij ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### Intellij Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +.idea/sonarlint + +### Java ### +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Maven ### +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar + +### NetBeans ### +**/nbproject/private/ +**/nbproject/Makefile-*.mk +**/nbproject/Package-*.bash +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ + +### Windows ### +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# End of https://www.gitignore.io/api/java,maven,macos,linux,eclipse,windows,netbeans,intellij diff --git a/enrich-citygml-with-greenarea/pom.xml b/enrich-citygml-with-greenarea/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..344cc07e79d5a466a269f55134d5c554764639db --- /dev/null +++ b/enrich-citygml-with-greenarea/pom.xml @@ -0,0 +1,47 @@ + + 4.0.0 + de.hft.stuttgart + enrich-citygml-with-osm + 0.0.1-SNAPSHOT + + + UTF-8 + 17 + 17 + + + + + + org.junit.jupiter + junit-jupiter-api + 5.9.2 + test + + + + org.citygml4j + citygml4j-core + 3.0.0-rc.5 + + + + org.locationtech.proj4j + proj4j + 1.1.4 + + + + org.locationtech.jts + jts-core + 1.19.0 + + + + + org.citygml4j + citygml4j-xml + 3.0.0-rc.5 + + + \ No newline at end of file diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/GreenArea.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/GreenArea.java new file mode 100644 index 0000000000000000000000000000000000000000..91328115dc727bc7e0314f75dc06f46e916b0d71 --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/GreenArea.java @@ -0,0 +1,29 @@ +package de.hft.stuttgart.citygml.green.osm; + +import org.locationtech.jts.geom.Polygon; + +public class GreenArea { + + private Polygon area; + + public GreenArea() { + } + + public GreenArea(Polygon area) { + this.area = area; + } + + public void setArea(Polygon area) { + this.area = area; + } + + public Polygon getArea() { + return area; + } + + @Override + public String toString() { + return "GreenArea [area=" + area + "]"; + } + +} 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 new file mode 100644 index 0000000000000000000000000000000000000000..101ac9eca5f881474de38115d2a86af16dffbebe --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/GreenEnricher.java @@ -0,0 +1,721 @@ +package de.hft.stuttgart.citygml.green.osm; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URLEncoder; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +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.core.model.vegetation.PlantCover; +import org.citygml4j.core.model.vegetation.SolitaryVegetationObject; +import org.citygml4j.core.model.waterbody.WaterBody; +import org.citygml4j.core.visitor.ObjectWalker; +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.Geometry; +import org.locationtech.jts.geom.GeometryFactory; +import org.locationtech.jts.geom.LineString; +import org.locationtech.jts.geom.MultiPolygon; +import org.locationtech.jts.geom.Point; +import org.locationtech.jts.geom.Polygon; +import org.locationtech.proj4j.BasicCoordinateTransform; +import org.locationtech.proj4j.CRSFactory; +import org.locationtech.proj4j.CoordinateReferenceSystem; +import org.locationtech.proj4j.ProjCoordinate; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; +import org.xmlobjects.gml.model.geometry.DirectPosition; +import org.xmlobjects.gml.model.geometry.DirectPositionList; +import org.xmlobjects.gml.model.geometry.Envelope; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; +import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; +import org.xmlobjects.gml.model.geometry.primitives.AbstractRingProperty; +import org.xmlobjects.gml.model.geometry.primitives.LinearRing; +import org.xmlobjects.gml.model.geometry.primitives.SurfaceProperty; + +public class GreenEnricher { + + private static final int BOUNDING_BOX_INCREASE_IN_M = 100; + + // in degrees + // add around 30m ( 1" = 30m -> 0.00277 = 900m) + private static final double BOUNDING_BOX_INCREASE = 0.00277; + + private static final String OSM_STRING = """ + [bbox:{{bbox}}]; + ( + nwr["leisure"="garden"]; + nwr["landuse"="forest"]; + nwr["natural"="tree"]; + nwr["leisure"="park"]; + nwr["natural"="tree_row"]; + nwr["natural"="wood"]; + nwr["landuse"="grass"]; + nwr["landuse"="orchard"]; + nwr["natural"="scrub"]; + nwr["landuse"="meadow"]; + nwr["landuse"="farmland"]; + nwr["landuse"="allotments"]; + nwr["natural"="water"]; + nwr["water"="river"]; + nwr["type"="waterway"]; + nwr["waterway"="stream"]; + nwr["water"="pond"]; + ); + out geom;"""; + + private static final URI OVERPASS_API_URI = URI.create("http://www.overpass-api.de/api/interpreter"); + private static final CRSFactory CRS_FACTORY = new CRSFactory(); + private static CoordinateReferenceSystem sourceCRS; + private static CoordinateReferenceSystem targetCRS = CRS_FACTORY.createFromName("EPSG:4326"); + private static BasicCoordinateTransform transform; + private static BasicCoordinateTransform backTransform; + public static GeometryFactory geomFactory = new GeometryFactory(); + + public static void main(String[] args) throws IOException, CityGMLContextException, CityGMLReadException, + InterruptedException, ParserConfigurationException, SAXException, CityGMLWriteException { + 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); + transform = new BasicCoordinateTransform(sourceCRS, targetCRS); + backTransform = new BasicCoordinateTransform(targetCRS, sourceCRS); + + OsmData osmData = new OsmData(); + String boundingBoxString = extractAndConvertBoundingBox(cityModel, epsgCode, osmData); +// HttpResponse 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")); + + System.out.println("Parsing OSM response"); + parseOsmResponse(osmResponse, osmData); + createBoundingBox(cityModel, osmData); + +// List newGreenAreas = new ArrayList<>(); + + System.out.println("Fit data in bounding box"); + fitToBoundingBox(osmData); + + System.out.println("Filter intersecting areas"); + List greenAreas = osmData.getGreenAreas(); + removeDuplicateAreas(greenAreas); + + convertGreenAreasToCityGML(cityModel, greenAreas); + convertWaterAreasToCityGML(cityModel, osmData); + + for (Waterway waterWay : osmData.getWaterways()) { + } + + for (TreePoint tp : osmData.getTreePoints()) { + double trunkRadius = 0.2; + double trunkHeight = 1; + Coordinate coordinate = tp.getPoint().getCoordinate(); + if (Double.isNaN(coordinate.z)) { + coordinate.z = 0; + } + MultiSurface generatedTree = TreeGenerator.generateTree(coordinate, trunkRadius, trunkHeight, 5, 5, 10); + SolitaryVegetationObject cover = new SolitaryVegetationObject(); + cover.setId(UUID.randomUUID().toString()); + cover.setLod2MultiSurface(new MultiSurfaceProperty(generatedTree)); + cityModel.getCityObjectMembers().add(new AbstractCityObjectProperty(cover)); + } +// +// for (TreeRow tr : osmData.getTreeRows()) { +// System.out.println(tr); +// } + + clampToGround(cityModel); + + String inputString = inFile.getFileName().toString(); + String inputPathWithoutFileEnding = inputString.substring(0, inputString.lastIndexOf('.')); + Path outputPath = Paths.get(inputPathWithoutFileEnding + "_with_greens.gml"); + System.out.println("Writing output file."); + writeCityGML(cityModel, outputPath); + System.out.println("Done"); + + } + + private static void convertWaterAreasToCityGML(CityModel cityModel, OsmData osmData) { + for (WaterArea waterArea : osmData.getWaterAreas()) { + WaterBody wb = new WaterBody(); + org.xmlobjects.gml.model.geometry.primitives.Polygon poly = convertToCityGmlPoly(waterArea.getArea()); + if (poly == null) { + System.out.println("Skipping WaterBody " + waterArea.getArea()); + continue; + } + MultiSurface ms = new MultiSurface(); + wb.setId(UUID.randomUUID().toString()); + ms.getSurfaceMember().add(new SurfaceProperty(poly)); + wb.setLod0MultiSurface(new MultiSurfaceProperty(ms)); + cityModel.getCityObjectMembers().add(new AbstractCityObjectProperty(wb)); + } + } + + private static void convertGreenAreasToCityGML(CityModel cityModel, List greenAreas) { + for (GreenArea ga : greenAreas) { + org.xmlobjects.gml.model.geometry.primitives.Polygon poly = convertToCityGmlPoly(ga.getArea()); + if (poly == null) { + System.out.println("Skipping " + ga.getArea()); + continue; + } + PlantCover cover = new PlantCover(); + MultiSurface ms = new MultiSurface(); + cover.setId(UUID.randomUUID().toString()); + ms.getSurfaceMember().add(new SurfaceProperty(poly)); + cover.setLod2MultiSurface(new MultiSurfaceProperty(ms)); + cityModel.getCityObjectMembers().add(new AbstractCityObjectProperty(cover)); + } + } + + private static void fitToBoundingBox(OsmData osmData) { + List greenAreas = osmData.getGreenAreas(); + List newGreenAreas = new ArrayList<>(); + for (GreenArea greenArea : greenAreas) { + Polygon area = greenArea.getArea(); + Geometry intersection = area.intersection(osmData.getBoundingBox()); + if (intersection instanceof MultiPolygon multi) { + Polygon poly1 = (Polygon) multi.getGeometryN(0); + greenArea.setArea(poly1); + for (int k = 1; k < multi.getNumGeometries(); k++) { + GreenArea newGreenArea = new GreenArea(); + newGreenArea.setArea((Polygon) multi.getGeometryN(k)); + newGreenAreas.add(newGreenArea); + } + } else { + greenArea.setArea((Polygon) intersection); + } + } + greenAreas.addAll(newGreenAreas); + + clipWaterAreasToBoundingBox(osmData); + + for (Iterator iterator = osmData.getTreePoints().iterator(); iterator.hasNext();) { + TreePoint tp = iterator.next(); + if (!osmData.getBoundingBox().contains(tp.getPoint())) { + iterator.remove(); + } + } + + } + + private static void clipWaterAreasToBoundingBox(OsmData osmData) { + List newWaterAreas = new ArrayList<>(); + for (WaterArea waterArea : osmData.getWaterAreas()) { + Polygon area = waterArea.getArea(); + Geometry intersection = area.intersection(osmData.getBoundingBox()); + if (intersection instanceof MultiPolygon multi) { + Polygon poly1 = (Polygon) multi.getGeometryN(0); + waterArea.setArea(poly1); + for (int k = 1; k < multi.getNumGeometries(); k++) { + WaterArea newWaterArea = new WaterArea(); + newWaterArea.setArea((Polygon) multi.getGeometryN(k)); + newWaterAreas.add(newWaterArea); + } + } else { + waterArea.setArea((Polygon) intersection); + } + } + } + + private static void removeDuplicateAreas(List greenAreas) { + for (int i = 0; i < greenAreas.size(); i++) { + for (int j = i + 1; j < greenAreas.size(); j++) { + GreenArea area1 = greenAreas.get(i); + GreenArea area2 = greenAreas.get(j); + if (area1.getArea().intersects(area2.getArea())) { + Geometry difference = area1.getArea().difference(area2.getArea()); + System.out.println(difference); + if (difference instanceof MultiPolygon) { + MultiPolygon multi = (MultiPolygon) difference; + Polygon poly1 = (Polygon) multi.getGeometryN(0); + area1.setArea(poly1); + for (int k = 1; k < multi.getNumGeometries(); k++) { + GreenArea newGreenArea = new GreenArea(); + newGreenArea.setArea((Polygon) multi.getGeometryN(k)); + greenAreas.add(newGreenArea); + } + } else { + area1.setArea((Polygon) difference); + } + + } + } + } + } + + private static void createBoundingBox(CityModel cityModel, OsmData osmData) { + // TODO Auto-generated method stub + + } + +// private static MultiSurface generateTree(Point p) { +// double radiusTrunk = 0.2; +// +// MultiSurface result = new MultiSurface(); +// +// // trunk +// Coordinate center = p.getCoordinate(); +// List coords = new ArrayList<>(); +// coords.add(new Coordinate(center.x, center.y + radiusTrunk)); +// coords.add(new Coordinate(center.x + radiusTrunk / 2, center.y + Math.cos(0.5) * radiusTrunk)); +// } + + private static void clampToGround(CityModel cityModel) { + for (AbstractCityObjectProperty afp : cityModel.getCityObjectMembers()) { + AbstractCityObject af = afp.getObject(); + if (af instanceof Building b) { + Envelope envelope = b.computeEnvelope(); + Double lowestZ = envelope.getLowerCorner().getValue().get(2); + b.accept(new ObjectWalker() { + @Override + public void visit(LinearRing linearRing) { + List values = linearRing.getControlPoints().getPosList().getValue(); + for (int i = 2; i < values.size(); i = i + 3) { + values.set(i, values.get(i) - lowestZ); + } + } + }); + } + } + } + + public static org.xmlobjects.gml.model.geometry.primitives.Polygon convertToCityGmlPoly(Polygon area) { + if (area.getExteriorRing().getCoordinates().length == 0) { + return null; + } + org.xmlobjects.gml.model.geometry.primitives.Polygon result = new org.xmlobjects.gml.model.geometry.primitives.Polygon(); + LinearRing lr = new LinearRing(); + result.setExterior(new AbstractRingProperty(lr)); + DirectPositionList posList = new DirectPositionList(); + lr.getControlPoints().setPosList(posList); + List values = posList.getValue(); + for (Coordinate coord : area.getExteriorRing().getCoordinates()) { + values.add(coord.x); + values.add(coord.y); + if (Double.isNaN(coord.z)) { + coord.z = 0; + } + values.add(coord.z); + } + + for (int i = 0; i < area.getNumInteriorRing(); i++) { + LinearRing innerRing = new LinearRing(); + result.getInterior().add(new AbstractRingProperty(innerRing)); + DirectPositionList innerPosList = new DirectPositionList(); + innerRing.getControlPoints().setPosList(innerPosList); + List innerValues = innerPosList.getValue(); + Coordinate[] coordinates = area.getInteriorRingN(i).getCoordinates(); + for (Coordinate coord : coordinates) { + innerValues.add(coord.x); + innerValues.add(coord.y); + if (Double.isNaN(coord.z)) { + coord.z = 0; + } + innerValues.add(coord.z); + } + } + + return result; + } + + private static HttpResponse getOsmData(String boundingBoxString) throws IOException, InterruptedException { + String data = OSM_STRING.replace("{{bbox}}", boundingBoxString); + String body = URLEncoder.encode("data", StandardCharsets.UTF_8) + "=" + + URLEncoder.encode(data, StandardCharsets.UTF_8); + + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder().uri(OVERPASS_API_URI) + .POST(HttpRequest.BodyPublishers.ofString(body)).build(); + System.out.println("Retrieving OSM data of bounding box: " + boundingBoxString); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() != 200) { + System.err.println("Failed to retrieve OSM data. Return code: " + response.statusCode()); + System.exit(1); + } + return response; + } + + 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); + } + } + + private static OsmData parseOsmResponse(String osmResponse, OsmData osmData) + throws ParserConfigurationException, SAXException, IOException { + Document document = createDomFromOsmResponse(osmResponse); + + // root element + Node osmItem = document.getChildNodes().item(0); + NodeList childNodes = osmItem.getChildNodes(); + + Set usedIds = new HashSet<>(); + + for (int i = 0; i < childNodes.getLength(); i++) { + Node node = childNodes.item(i); + if (parseTreeIfPossible(osmData.getTreePoints(), node, usedIds)) { + continue; + } + if (parseGreenAreasIfPossible(osmData, node, usedIds)) { + continue; + } + parseGreenRelationsIfPossible(osmData, node, usedIds); + } + + return osmData; + } + + private static boolean parseGreenRelationsIfPossible(OsmData data, Node node, Set usedIds) { + if (!"relation".equals(node.getNodeName())) { + return false; + } + String nodeValue = node.getAttributes().getNamedItem("id").getNodeValue(); + System.out.println("Relation: " + nodeValue); + NodeList childNodes = node.getChildNodes(); + List> innerRings = new ArrayList<>(); + List outerRing = new ArrayList<>(); + boolean water = false; + for (int i = 0; i < childNodes.getLength(); i++) { + Node boundsOrMember = childNodes.item(i); + if (!"member".equals(boundsOrMember.getNodeName())) { + if ("tag".equals(boundsOrMember.getNodeName())) { + if ("water".equals(boundsOrMember.getAttributes().getNamedItem("v").getNodeValue())) { + water = true; + } + } + continue; + } + Node member = boundsOrMember; + String role = member.getAttributes().getNamedItem("role").getNodeValue(); + if (!"way".equals(member.getAttributes().getNamedItem("type").getNodeValue())) { + continue; + } + String referenceId = member.getAttributes().getNamedItem("ref").getNodeValue(); + if (usedIds.contains(referenceId)) { + System.out.println("Relation contains way that is already used " + referenceId); + continue; + } + List ringCoords; + if ("inner".equals(role)) { + ringCoords = new ArrayList<>(); + innerRings.add(ringCoords); + } else if ("outer".equals(role)) { + // outer ring + outerRing.clear(); + ringCoords = outerRing; + } else if ("main_stream".equals(role)) { + ringCoords = outerRing; + } else if ("side_stream".equals(role)) { + ringCoords = outerRing; + } else { + System.out.println("Unknown role: " + role); + // probably waterway + continue; + } + NodeList memberNodes = member.getChildNodes(); + for (int memberNodesIndex = 0; memberNodesIndex < memberNodes.getLength(); memberNodesIndex++) { + Node nd = memberNodes.item(memberNodesIndex); + if ("nd".equals(nd.getNodeName())) { + double lat = Double.parseDouble(nd.getAttributes().getNamedItem("lat").getNodeValue()); + double lon = Double.parseDouble(nd.getAttributes().getNamedItem("lon").getNodeValue()); + ProjCoordinate converted = convertCoordinatesFrom84(lon, lat); + ringCoords.add(new Coordinate(converted.x, converted.y)); + } + } + } + + if (water) { + org.locationtech.jts.geom.LinearRing outerLinearRing = geomFactory + .createLinearRing(outerRing.toArray(new Coordinate[outerRing.size()])); + // create the inner rings + List innerLinearRings = new ArrayList<>(); + for (List innerRing : innerRings) { + org.locationtech.jts.geom.LinearRing innerLinearRing = geomFactory + .createLinearRing(innerRing.toArray(new Coordinate[innerRing.size()])); + innerLinearRings.add(innerLinearRing); + } + + Polygon polygon = geomFactory.createPolygon(outerLinearRing, + innerLinearRings.toArray(new org.locationtech.jts.geom.LinearRing[innerLinearRings.size()])); + + data.getWaterAreas().add(new WaterArea(polygon)); + } + +// validateRing(outerRing); + // create the outer ring +// org.locationtech.jts.geom.LinearRing outerLinearRing = geomFactory +// .createLinearRing(outerRing.toArray(new Coordinate[outerRing.size()])); +// +// // create the inner rings +// List innerLinearRings = new ArrayList<>(); +// for (List innerRing : innerRings) { +// org.locationtech.jts.geom.LinearRing innerLinearRing = geomFactory +// .createLinearRing(innerRing.toArray(new Coordinate[innerRing.size()])); +// innerLinearRings.add(innerLinearRing); +// } +// +// if (outerRing.isEmpty()) { +// return false; +// } +// +// // create the polygon +// Polygon polygon = geomFactory.createPolygon(outerLinearRing, +// innerLinearRings.toArray(new org.locationtech.jts.geom.LinearRing[innerLinearRings.size()])); +// +// data.getGreenAreas().add(new GreenArea(polygon)); + return true; + + } + + private static void validateRing(List outerRing) { + if (outerRing.isEmpty()) { + return; + } + if (!outerRing.get(0).equals(outerRing.get(outerRing.size() - 1))) { + // close ring + outerRing.add(outerRing.get(0)); + } + } + + private static boolean parseGreenAreasIfPossible(OsmData data, Node node, Set usedIds) { + if (!"way".equals(node.getNodeName())) { + return false; + } + + boolean water = false; + String id = node.getAttributes().getNamedItem("id").getNodeValue(); + if (usedIds.contains(id)) { + System.out.println("Already used way id " + id); + return true; + } + + usedIds.add(id); + NodeList childNodes = node.getChildNodes(); + List coordinates = new ArrayList<>(); + boolean line = false; + for (int i = 0; i < childNodes.getLength(); i++) { + Node child = childNodes.item(i); + if ("nd".equals(child.getNodeName())) { + double lat = Double.parseDouble(getTagValue(child, "lat")); + double lon = Double.parseDouble(getTagValue(child, "lon")); + ProjCoordinate converted = convertCoordinatesFrom84(lon, lat); + coordinates.add(new Coordinate(converted.x, converted.y)); + } else if ("tag".equals(child.getNodeName())) { +// if (existTagWithValue(child, "k", "natural") && existTagWithValue(child, "v", "tree_row")) { +// line = true; +// } + if ((existTagWithValue(child, "k", "natural") && existTagWithValue(child, "v", "water")) + || existTagWithValue(child, "k", "waterway")) { + water = true; + } + } + } + + if (!coordinates.get(0).equals(coordinates.get(coordinates.size() - 1))) { + // assume line if start and end coordinate do not match? + line = true; + } + + if (water) { + if (line) { + LineString lineString = geomFactory + .createLineString(coordinates.toArray(new Coordinate[coordinates.size()])); + data.getWaterways().add(new Waterway(lineString)); + } else { + Polygon polygon = geomFactory.createPolygon(coordinates.toArray(new Coordinate[coordinates.size()])); + data.getWaterAreas().add(new WaterArea(polygon)); + } + } else { + if (line) { + LineString lineString = geomFactory + .createLineString(coordinates.toArray(new Coordinate[coordinates.size()])); + data.getTreeRows().add(new TreeRow(lineString)); + } else { + Polygon polygon = geomFactory.createPolygon(coordinates.toArray(new Coordinate[coordinates.size()])); + data.getGreenAreas().add(new GreenArea(polygon)); + } + } + + return true; + } + + private static boolean existTagWithValue(Node node, String key, String value) { + return value.equals(getTagValue(node, key)); + } + + private static String getTagValue(Node node, String tagName) { + Node namedItem = node.getAttributes().getNamedItem(tagName); + if (namedItem == null) { + return null; + } + return namedItem.getNodeValue(); + } + + private static boolean parseTreeIfPossible(List treePoints, Node node, Set usedIds) { + if (!"node".equals(node.getNodeName())) { + return false; + } + String id = node.getAttributes().getNamedItem("id").getNodeValue(); + if (usedIds.contains(id)) { + System.out.println("Already used node id " + id); + return true; + } + usedIds.add(id); + double lat = Double.parseDouble(node.getAttributes().getNamedItem("lat").getNodeValue()); + double lon = Double.parseDouble(node.getAttributes().getNamedItem("lon").getNodeValue()); + ProjCoordinate converted = convertCoordinatesFrom84(lon, lat); + Point point = geomFactory.createPoint(new Coordinate(converted.x, converted.y)); + treePoints.add(new TreePoint(point)); + return true; + } + + private static Document createDomFromOsmResponse(String body) + throws ParserConfigurationException, SAXException, IOException { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + // to be compliant, completely disable DOCTYPE declaration: + factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // or completely disable external entities declarations: + factory.setFeature("http://xml.org/sax/features/external-general-entities", false); + factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + // or prohibit the use of all protocols by external entities: + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); + DocumentBuilder builder = factory.newDocumentBuilder(); + ByteArrayInputStream input = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8)); + return builder.parse(input); + } + + 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 String extractEpsgCode(CityModel cityModel) { + Envelope envelope = cityModel.getBoundedBy().getEnvelope(); + String srsName = envelope.getSrsName(); + return parseEpsg(srsName); + } + + private static String parseEpsg(String srsName) { + int utmIndex = srsName.indexOf("UTM"); + if (utmIndex > 0) { + return "EPSG:258" + srsName.substring(utmIndex + 3, utmIndex + 5); + } + Pattern p = Pattern.compile("EPSG:+([0-9]+)"); + Matcher m = p.matcher(srsName); + if (m.find()) { + return "EPSG:" + m.group(1); + } + return srsName; + } + + private static String extractAndConvertBoundingBox(CityModel cityModel, String epsgCode, OsmData osmData) { + Envelope calculatedEnvelope = cityModel.computeEnvelope(); + + DirectPosition lowerCorner = calculatedEnvelope.getLowerCorner(); + ProjCoordinate p1 = new ProjCoordinate(lowerCorner.getValue().get(0), lowerCorner.getValue().get(1)); + ProjCoordinate lowerCornerProjected = new ProjCoordinate(); + transform.transform(p1, lowerCornerProjected); + + DirectPosition upperCorner = calculatedEnvelope.getUpperCorner(); + p1.x = upperCorner.getValue().get(0); + p1.y = upperCorner.getValue().get(1); + ProjCoordinate upperCornerProjected = new ProjCoordinate(); + transform.transform(p1, upperCornerProjected); + + lowerCornerProjected.x -= BOUNDING_BOX_INCREASE; + lowerCornerProjected.y -= BOUNDING_BOX_INCREASE; + upperCornerProjected.x += BOUNDING_BOX_INCREASE; + upperCornerProjected.y += BOUNDING_BOX_INCREASE; + + List bboxCoordinates = new ArrayList<>(); + bboxCoordinates.add(new Coordinate(lowerCorner.getValue().get(0) - BOUNDING_BOX_INCREASE_IN_M, + lowerCorner.getValue().get(1) - BOUNDING_BOX_INCREASE_IN_M)); + bboxCoordinates.add(new Coordinate(lowerCorner.getValue().get(0) - BOUNDING_BOX_INCREASE_IN_M, + upperCorner.getValue().get(1) + BOUNDING_BOX_INCREASE_IN_M)); + bboxCoordinates.add(new Coordinate(upperCorner.getValue().get(0) + BOUNDING_BOX_INCREASE_IN_M, + upperCorner.getValue().get(1) + BOUNDING_BOX_INCREASE_IN_M)); + bboxCoordinates.add(new Coordinate(upperCorner.getValue().get(0) + BOUNDING_BOX_INCREASE_IN_M, + lowerCorner.getValue().get(1) - BOUNDING_BOX_INCREASE_IN_M)); + bboxCoordinates.add(new Coordinate(lowerCorner.getValue().get(0) - BOUNDING_BOX_INCREASE_IN_M, + lowerCorner.getValue().get(1) - BOUNDING_BOX_INCREASE_IN_M)); + + Polygon poly = geomFactory.createPolygon(bboxCoordinates.toArray(new Coordinate[bboxCoordinates.size()])); + osmData.setBoundingBox(poly); + + return lowerCornerProjected.y + "," + lowerCornerProjected.x + "," + upperCornerProjected.y + "," + + upperCornerProjected.x; + } + + private static ProjCoordinate convertCoordinatesFrom84(double x, double y) { + ProjCoordinate p1 = new ProjCoordinate(x, y); + ProjCoordinate result = new ProjCoordinate(); + backTransform.transform(p1, result); + return result; + } + + private static ProjCoordinate convertCoordinatesTo84(DirectPosition point) { + double x = point.getValue().get(0); + double y = point.getValue().get(1); + return convertCoordinatesTo84(x, y); + } + + private static ProjCoordinate convertCoordinatesTo84(double x, double y) { + ProjCoordinate p1 = new ProjCoordinate(x, y); + ProjCoordinate result = new ProjCoordinate(); + transform.transform(p1, result); + return result; + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/OsmData.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/OsmData.java new file mode 100644 index 0000000000000000000000000000000000000000..0ae87234c69238e0d6964678d8793b9f2cffd6a3 --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/OsmData.java @@ -0,0 +1,46 @@ +package de.hft.stuttgart.citygml.green.osm; + +import java.util.ArrayList; +import java.util.List; + +import org.locationtech.jts.geom.Polygon; + +public class OsmData { + + private Polygon boundingBox; + + private List treePoints = new ArrayList<>(); + private List greenAreas = new ArrayList<>(); + private List treeRows = new ArrayList<>(); + private List waterways = new ArrayList<>(); + private List waterAreas = new ArrayList<>(); + + public void setBoundingBox(Polygon boundingBox) { + this.boundingBox = boundingBox; + } + + public List getWaterAreas() { + return waterAreas; + } + + public List getWaterways() { + return waterways; + } + + public Polygon getBoundingBox() { + return boundingBox; + } + + public List getGreenAreas() { + return greenAreas; + } + + public List getTreePoints() { + return treePoints; + } + + public List getTreeRows() { + return treeRows; + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeGenerator.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..198b9afa5d195c2e260a058ccd482a9482fad2c4 --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeGenerator.java @@ -0,0 +1,147 @@ +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.geomFactory.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.geomFactory.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.geomFactory.createPolygon(coordArray); + var convertedPoly = GreenEnricher.convertToCityGmlPoly(polygon); + ms.getSurfaceMember().add(new SurfaceProperty(convertedPoly)); + } + + return ms; + } + + private static Polygon convertPolygon(List trunkWestCoordinateList) { + return GreenEnricher.geomFactory + .createPolygon(trunkWestCoordinateList.toArray(new Coordinate[trunkWestCoordinateList.size()])); + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreePoint.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreePoint.java new file mode 100644 index 0000000000000000000000000000000000000000..6460f8288fd97155b27f5b1bf1ddb1ecebf94bee --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreePoint.java @@ -0,0 +1,29 @@ +package de.hft.stuttgart.citygml.green.osm; + +import org.locationtech.jts.geom.Point; + +public class TreePoint { + + private Point point; + + public TreePoint() { + } + + public TreePoint(Point point) { + this.point = point; + } + + public void setPoint(Point point) { + this.point = point; + } + + public Point getPoint() { + return point; + } + + @Override + public String toString() { + return "TreePoint [point=" + point + "]"; + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeRow.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeRow.java new file mode 100644 index 0000000000000000000000000000000000000000..d8715731294329d820056b752b05686c8b4de1eb --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeRow.java @@ -0,0 +1,29 @@ +package de.hft.stuttgart.citygml.green.osm; + +import org.locationtech.jts.geom.LineString; + +public class TreeRow { + + private LineString line; + + public TreeRow() { + } + + public TreeRow(LineString line) { + this.line = line; + } + + public LineString getLine() { + return line; + } + + public void setLine(LineString line) { + this.line = line; + } + + @Override + public String toString() { + return "TreeRow [line=" + line + "]"; + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/WaterArea.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/WaterArea.java new file mode 100644 index 0000000000000000000000000000000000000000..70d03ddb23b8d557463bc52c84a7fe9b0c24f5ff --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/WaterArea.java @@ -0,0 +1,29 @@ +package de.hft.stuttgart.citygml.green.osm; + +import org.locationtech.jts.geom.Polygon; + +public class WaterArea { + + private Polygon area; + + public WaterArea() { + } + + public WaterArea(Polygon area) { + this.area = area; + } + + public void setArea(Polygon area) { + this.area = area; + } + + public Polygon getArea() { + return area; + } + + @Override + public String toString() { + return "WaterArea [area=" + area + "]"; + } + +} diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/Waterway.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/Waterway.java new file mode 100644 index 0000000000000000000000000000000000000000..ba38af9a41ef8c9c2d32f008b3425acf5530334f --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/Waterway.java @@ -0,0 +1,30 @@ +package de.hft.stuttgart.citygml.green.osm; + +import org.locationtech.jts.geom.LineString; + +public class Waterway { + + private LineString line; + + public Waterway() { + } + + public Waterway(LineString line) { + super(); + this.line = line; + } + + public void setLine(LineString line) { + this.line = line; + } + + public LineString getLine() { + return line; + } + + @Override + public String toString() { + return "Waterway [line=" + line + "]"; + } + +}