diff --git a/enrich-citygml-with-greenarea/.gitignore b/enrich-citygml-with-greenarea/.gitignore index f190e4bc19841cf1aa289fc09f5b260ea70f0b82..3181a0293e46b74d2b3c424589b9d1ae43e74610 100644 --- a/enrich-citygml-with-greenarea/.gitignore +++ b/enrich-citygml-with-greenarea/.gitignore @@ -2,6 +2,9 @@ # 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 +data/Marieke +data/*.gml + # User specific .sonarlint/ diff --git a/enrich-citygml-with-greenarea/data/Marieke/file_structure.txt b/enrich-citygml-with-greenarea/data/Marieke/file_structure.txt new file mode 100644 index 0000000000000000000000000000000000000000..d7e576c44fb494ee030f6468bea5bbe187ef0ed4 --- /dev/null +++ b/enrich-citygml-with-greenarea/data/Marieke/file_structure.txt @@ -0,0 +1,127 @@ +Name + . +├──  75Bäume +│ ├──  Baum_IST_Grombühl_alle.cpg +│ ├──  Baum_IST_Grombühl_alle.dbf +│ ├──  Baum_IST_Grombühl_alle.prj +│ ├──  Baum_IST_Grombühl_alle.sbn +│ ├──  Baum_IST_Grombühl_alle.sbx +│ ├──  Baum_IST_Grombühl_alle.shp +│ ├── 謹 Baum_IST_Grombühl_alle.shp.xml +│ ├──  Baum_IST_Grombühl_alle.shx +│ ├──  Grombuehl_Block_15-17.gml +│ ├──  trees_75_15.cpg +│ ├──  trees_75_15.dbf +│ ├──  trees_75_15.prj +│ ├──  trees_75_15.sbn +│ ├──  trees_75_15.sbx +│ ├──  trees_75_15.shp +│ ├── 謹 trees_75_15.shp.xml +│ └──  trees_75_15.shx +├──  250Bäume +│ ├──  Abstand5m +│ │ ├──  Grombuehl_Block_15-17.gml +│ │ ├──  trees_250_5.cpg +│ │ ├──  trees_250_5.dbf +│ │ ├──  trees_250_5.prj +│ │ ├──  trees_250_5.sbn +│ │ ├──  trees_250_5.sbx +│ │ ├──  trees_250_5.shp +│ │ ├── 謹 trees_250_5.shp.xml +│ │ └──  trees_250_5.shx +│ ├──  Abstand15m +│ │ ├──  49_801844__49_802502__9_948075__9_949534_15m_Block24 +│ │ │ ├──  Grombuehl_Block_24.gml +│ │ │ ├──  trees_250_15_kl.cpg +│ │ │ ├──  trees_250_15_kl.dbf +│ │ │ ├──  trees_250_15_kl.prj +│ │ │ ├──  trees_250_15_kl.shp +│ │ │ └──  trees_250_15_kl.shx +│ │ └──  49_802605__49_804087__9_94036__9_948417_15m_Block7-11_14-17 +│ │ ├──  Grombuehl_Block_7-11_14-17.gml +│ │ ├──  trees_150_15_gr.cpg +│ │ ├──  trees_150_15_gr.dbf +│ │ ├──  trees_150_15_gr.prj +│ │ ├──  trees_150_15_gr.sbn +│ │ ├──  trees_150_15_gr.sbx +│ │ ├──  trees_150_15_gr.shp +│ │ ├── 謹 trees_150_15_gr.shp.xml +│ │ └──  trees_150_15_gr.shx +│ ├──  Baum_IST_Grombühl_alle.cpg +│ ├──  Baum_IST_Grombühl_alle.dbf +│ ├──  Baum_IST_Grombühl_alle.prj +│ ├──  Baum_IST_Grombühl_alle.sbn +│ ├──  Baum_IST_Grombühl_alle.sbx +│ ├──  Baum_IST_Grombühl_alle.shp +│ ├── 謹 Baum_IST_Grombühl_alle.shp.xml +│ └──  Baum_IST_Grombühl_alle.shx +├──  500Bäume +│ ├──  Abstand10m +│ │ ├──  49_801844__49_802502__9_948075__9_949534_10m_block_24 +│ │ │ ├──  Grombuehl_Block_24.gml +│ │ │ ├──  trees_500_10_kl.cpg +│ │ │ ├──  trees_500_10_kl.dbf +│ │ │ ├──  trees_500_10_kl.prj +│ │ │ ├──  trees_500_10_kl.shp +│ │ │ └──  trees_500_10_kl.shx +│ │ └──  49_802605__49_804087__9_94036__9_948417_10m_block_7-11_14-17 +│ │ ├──  Grombuehl_Block_7-11_14-17.gml +│ │ ├──  trees_500_10_gr.cpg +│ │ ├──  trees_500_10_gr.dbf +│ │ ├──  trees_500_10_gr.prj +│ │ ├──  trees_500_10_gr.sbn +│ │ ├──  trees_500_10_gr.sbx +│ │ ├──  trees_500_10_gr.shp +│ │ ├── 謹 trees_500_10_gr.shp.xml +│ │ └──  trees_500_10_gr.shx +│ ├──  Baum_IST_Grombühl_alle.cpg +│ ├──  Baum_IST_Grombühl_alle.dbf +│ ├──  Baum_IST_Grombühl_alle.prj +│ ├──  Baum_IST_Grombühl_alle.sbn +│ ├──  Baum_IST_Grombühl_alle.sbx +│ ├──  Baum_IST_Grombühl_alle.shp +│ ├── 謹 Baum_IST_Grombühl_alle.shp.xml +│ └──  Baum_IST_Grombühl_alle.shx +├──  file_structure.txt +└──  ganzes_gebiet + ├──  Grombühl_BA_IST.gml + ├──  trees_75_15.cpg + ├──  trees_75_15.dbf + ├──  trees_75_15.prj + ├──  trees_75_15.sbn + ├──  trees_75_15.sbx + ├──  trees_75_15.shp + ├── 謹 trees_75_15.shp.xml + ├──  trees_75_15.shx + ├──  trees_250_5.cpg + ├──  trees_250_5.dbf + ├──  trees_250_5.prj + ├──  trees_250_5.sbn + ├──  trees_250_5.sbx + ├──  trees_250_5.shp + ├── 謹 trees_250_5.shp.xml + ├──  trees_250_5.shx + ├──  trees_250_15.cpg + ├──  trees_250_15.dbf + ├──  trees_250_15.prj + ├──  trees_250_15.sbn + ├──  trees_250_15.sbx + ├──  trees_250_15.shp + ├── 謹 trees_250_15.shp.xml + ├──  trees_250_15.shx + ├──  trees_500_10.cpg + ├──  trees_500_10.dbf + ├──  trees_500_10.prj + ├──  trees_500_10.sbn + ├──  trees_500_10.sbx + ├──  trees_500_10.shp + ├──  trees_500_10.shx + ├── 謹 trees_500_10.xml + ├──  trees_500_15.cpg + ├──  trees_500_15.dbf + ├──  trees_500_15.prj + ├──  trees_500_15.sbn + ├──  trees_500_15.sbx + ├──  trees_500_15.shp + ├── 謹 trees_500_15.shp.xml + └──  trees_500_15.shx 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 index c6007704c6f8c8b0783e5418c3d4f7a1ac25245a..3f1bddeadae5cb0add9e856ba3fd2e8f0b7e9696 100644 --- 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 @@ -7,7 +7,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; - import org.geotools.data.DataStore; import org.geotools.data.DataStoreFinder; import org.geotools.data.FeatureSource; @@ -17,13 +16,15 @@ import org.locationtech.jts.geom.Point; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; -public class TreeKatasterData { - + +public class TreeKatasterData +{ + public static final double TRUNK_PERCENTAGE = 0.2; public static final double CROWN_PERCENTAGE = 1 - TRUNK_PERCENTAGE; - + private List trees; - + public static TreeKatasterData parseTreeKatasterData(Path path) throws IOException { TreeKatasterData result = new TreeKatasterData(); Map readParameters = new HashMap<>(); @@ -75,7 +76,7 @@ public class TreeKatasterData { } return result; } - + public List getTrees() { if (trees == null) { trees = new ArrayList<>(); diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeUtils.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeUtils.java index ee549f92cce0e1e56797dddad54a62e20a2e2769..e9963229890d44d6d8325770c33c3f794c362dd3 100644 --- a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeUtils.java +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/citygml/green/osm/TreeUtils.java @@ -17,7 +17,9 @@ import org.xmlobjects.gml.model.geometry.aggregates.MultiSurface; import org.xmlobjects.gml.model.geometry.aggregates.MultiSurfaceProperty; import org.xmlobjects.gml.model.measures.Length; -public class TreeUtils { + +public class TreeUtils +{ private static final GeometryFactory FACTORY = new GeometryFactory(); @@ -36,10 +38,10 @@ public class TreeUtils { // generateTreesFromKataster(cityModel, katasterData2); // All kataster trees are taken, osm trees are removed -// filterDuplicateTreesFromOSM(osmData, katasterData); + // filterDuplicateTreesFromOSM(osmData, katasterData); // filterDuplicateTreesFromOSM(osmData, katasterData2); -// generateTreesFromOSM(cityModel, osmData, wktPolygon); + // generateTreesFromOSM(cityModel, osmData, wktPolygon); } diff --git a/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/scripts/PrepareDataForMarieke.java b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/scripts/PrepareDataForMarieke.java new file mode 100644 index 0000000000000000000000000000000000000000..8f739f89ea78f7b1f41dedd4716fce14315c9553 --- /dev/null +++ b/enrich-citygml-with-greenarea/src/main/java/de/hft/stuttgart/scripts/PrepareDataForMarieke.java @@ -0,0 +1,79 @@ +package de.hft.stuttgart.scripts; + +import java.io.File; +import java.io.IOException; +import org.citygml4j.xml.CityGMLContextException; +import org.citygml4j.xml.reader.CityGMLReadException; +import org.citygml4j.xml.writer.CityGMLWriteException; +import org.locationtech.jts.io.ParseException; +import de.hft.stuttgart.citygml.green.osm.GreenEnricher; +import jakarta.xml.bind.JAXBException; + + +public class PrepareDataForMarieke +{ + // Add Trees to GML files inside data/Marieke/... + public static void main(String[] whatever) throws Exception { + processWholeRegion(); + processSmallBlocks(); + processStatusQuo(); + } + + private static void processStatusQuo() throws IOException, CityGMLContextException, CityGMLReadException, + InterruptedException, CityGMLWriteException, JAXBException, ParseException { + File folder = new File("data/Marieke/GebietsausschnittStatusQuo/"); + File shapefile = new File("data/Marieke/GebietsausschnittStatusQuo/Baum_alle.shp"); + File[] gmls = folder.listFiles((dir, name) -> name.endsWith(".gml")); + for (File gml : gmls) { + System.err.println(">>> " + gml); + String extension = shapefile.getName().replaceAll("\\.shp", ""); + String[] args = new String[] { gml.toString(), // Input GML + shapefile.toString(), // Added trees, in Baumkatasterformat, + extension, // Output GML suffix + }; + GreenEnricher.main(args); + } + } + + private static void processSmallBlocks() throws IOException, CityGMLContextException, CityGMLReadException, + InterruptedException, CityGMLWriteException, JAXBException, ParseException { + File[] folders = { + new File("data/Marieke/75Bäume/"), + new File("data/Marieke/250Bäume/Abstand5m"), + new File("data/Marieke/250Bäume/Abstand15m/49_801844__49_802502__9_948075__9_949534_15m_Block24"), + new File("data/Marieke/250Bäume/Abstand15m/49_802605__49_804087__9_94036__9_948417_15m_Block7-11_14-17"), + new File("data/Marieke/500Bäume/Abstand10m/49_801844__49_802502__9_948075__9_949534_10m_block_24"), + new File("data/Marieke/500Bäume/Abstand10m/49_802605__49_804087__9_94036__9_948417_10m_block_7-11_14-17"), + }; + for (File folder : folders) { + System.err.println("### " + folder); + String gml = folder.listFiles((dir, name) -> name.endsWith(".gml"))[0].getName(); + File[] shapefiles = folder.listFiles((dir, name) -> name.endsWith(".shp")); + for (File shapefile : shapefiles) { + System.out.println(shapefile); + String extension = shapefile.getName().replaceAll("\\.shp", ""); + String[] args = new String[] { folder.toPath().resolve(gml).toString(), // Input GML + shapefile.toString(), // Added trees, in Baumkatasterformat, + extension, // Output GML suffix + }; + GreenEnricher.main(args); + } + } + } + + private static void processWholeRegion() throws IOException, CityGMLContextException, CityGMLReadException, + InterruptedException, CityGMLWriteException, JAXBException, ParseException { + File folder = new File("data/Marieke/ganzes_gebiet/"); + String gml = "Grombühl_BA_IST.gml"; + File[] shapefiles = folder.listFiles((dir, name) -> name.endsWith(".shp")); + for (File shapefile : shapefiles) { + System.err.println(">>> " + shapefile); + String extension = shapefile.getName().replaceAll("\\.shp", ""); + String[] args = new String[] { folder.toPath().resolve(gml).toString(), // Input GML + shapefile.toString(), // Added trees, in Baumkatasterformat, + extension, // Output GML suffix + }; + GreenEnricher.main(args); + } + } +} diff --git a/enrich-citygml-with-greenarea/src/test/java/de/hft/stuttgart/citygml/green/alkis/AlkisGreenEnricherTest.java b/enrich-citygml-with-greenarea/src/test/java/de/hft/stuttgart/citygml/green/alkis/AlkisGreenEnricherTest.java index a896e0d15bf86baf246a6b0a76969c139dded5a8..6cbac1769cf28c7bf03da491623dc0af3bb287ed 100644 --- a/enrich-citygml-with-greenarea/src/test/java/de/hft/stuttgart/citygml/green/alkis/AlkisGreenEnricherTest.java +++ b/enrich-citygml-with-greenarea/src/test/java/de/hft/stuttgart/citygml/green/alkis/AlkisGreenEnricherTest.java @@ -40,4 +40,4 @@ class AlkisGreenEnricherTest GreenEnricher.main(args); assertTrue(Files.exists(outputGML)); } -} +} \ No newline at end of file diff --git a/python_scripts/add_trees_to_open_street_map/add_trees.py b/python_scripts/add_trees_to_open_street_map/add_trees.py index 48c35a6c41106b6f79e0d846fa528e5d42ed2386..31e445dbba45cc0d07a97181ddf9c0f7e1ae2b62 100644 --- a/python_scripts/add_trees_to_open_street_map/add_trees.py +++ b/python_scripts/add_trees_to_open_street_map/add_trees.py @@ -8,7 +8,8 @@ Trees are exported in a CSV table, a PNG diagram and an HTML interactive map. """ import pickle from pathlib import Path -from collections import namedtuple +from collections import namedtuple, Counter +import re import folium import matplotlib.pyplot as plt @@ -30,7 +31,9 @@ from import_existing_trees import get_existing_forest # TODO: Write tests? # From RegionChooser, or https://transfer.hft-stuttgart.de/gitlab/circulargreensimcity/circulargreensimcity/-/wikis/Fallstudien/Gromb%C3%BChl -WKT = "POLYGON((9.947021 49.803063, 9.947011 49.800917, 9.955025 49.800810, 9.955110 49.803019, 9.947021 49.803063))" +# WKT = "POLYGON((9.947021 49.803063, 9.947011 49.800917, 9.955025 49.800810, 9.955110 49.803019, 9.947021 49.803063))" +# Grafenbühl +WKT = "POLYGON((9.147551 48.908059, 9.148635 48.907953, 9.149525 48.907819, 9.151177 48.907819, 9.151413 48.907840, 9.153226 48.908087, 9.153387 48.906705, 9.149160 48.906634, 9.148999 48.906620, 9.147551 48.908059))" # Replace with None if no existing tree should be imported EXISTING_TREES = 'existing_trees/Trees_ideal_2_20240227.shp' # EXISTING_TREES = 'existing_trees/baumkataster/Baum.shp' @@ -41,9 +44,15 @@ TREE_DISTANCE = 10 # [m] MIN_DISTANCE = TREE_DISTANCE * 0.5 # [m] # For display purposes only: GRID = 100 # [m] - -IGNORE_ROADS = set(['primary', 'unclassified', 'secondary', - 'secondary_link', 'trunk', 'trunk_link', 'primary_link']) +# If streets from OSM don't have known width, which one should be used? In [m] +# Leave empty to get default from similar streets, if available +# Set to -1 if you want to disable trees along the roads +DEFAULT_WIDTHS = { + 'unclassified': 0, +# 'residential': 8, +# 'motorway': -1, +# 'trunk': -1, +} SCRIPT_DIR = Path(__file__).resolve().parent @@ -51,17 +60,17 @@ OUTPUT_DIR = SCRIPT_DIR / 'output' Bounds = namedtuple("Bounds", "W S E N") -def load_region(wkt_polygon): +def load_region(wkt_polygon: str): region = wkt.loads(wkt_polygon) bounds = Bounds(*region.bounds) return region, bounds -def get_basename(bounds): +def get_basename(bounds: Bounds): return f'{bounds.S}__{bounds.N}__{bounds.W}__{bounds.E}_{TREE_DISTANCE}m'.replace('.', '_') -def get_osm_roads(bounds): +def get_osm_roads(bounds: Bounds): cache_dir = SCRIPT_DIR / 'cache' cache_dir.mkdir(exist_ok=True) @@ -88,7 +97,7 @@ def get_osm_roads(bounds): return ways -def set_plot(bounds, to_local_coordinates): +def set_plot(bounds: Bounds, to_local_coordinates): x_min, y_min = to_local_coordinates.transform(bounds.W, bounds.S) x_max, y_max = to_local_coordinates.transform(bounds.E, bounds.N) ax = plt.axes() @@ -103,16 +112,51 @@ def set_plot(bounds, to_local_coordinates): return ax -def place_trees(forest, ways, region, to_local, tree_distance, min_distance_2) -> Forest: +def get_width(way: overpy.Way) -> float: + width_str = way.tags.get("width", '0') + # NOTE: Some widths are written with units, so try to remove trailing [m] first. + width_str = re.sub(' ?m$', '', width_str) + return float(width_str) + + +def get_default_widths(ways: list) -> dict[str, float]: + """Check existing OSM highways, and extract the most common width for each type""" + width_counters: dict[str, Counter] = {} + for way in ways: + width = get_width(way) + way_type = way.tags.get("highway") + if width: + if way_type not in width_counters: + width_counters[way_type] = Counter() + width_counters[way_type][width] += 1 + return {w: c.most_common(1)[0][0] for w, c in width_counters.items()} + + +def place_trees(forest: Forest, ways: list, region: str, to_local, tree_distance: float, min_distance_2: float) -> Forest: local_region = transform(to_local.transform, region) + default_widths = {**get_default_widths(ways), **DEFAULT_WIDTHS} + + print(f"Default widths: {default_widths}") + for way in ways: - width = float(way.tags.get("width", 0)) - highway = way.tags.get("highway") - if highway in IGNORE_ROADS: - color = 'orange' + way_type = way.tags.get("highway") + width = get_width(way) + default_width = default_widths.get(way_type, 0) + if default_width < 0: + # Ignore this type of streets + continue + if width: + # Defined in OSM + color = 'blue' else: - color = 'gray' + if default_width: + # From OSM most common width + color = 'orange' + width = default_width + else: + # Unknown + color = 'gray' road_xy_s = [to_local.transform(node.lon, node.lat) for node in way.nodes] @@ -123,15 +167,13 @@ def place_trees(forest, ways, region, to_local, tree_distance, min_distance_2) - tree_path = road_as_polygon.exterior displayed_width = width else: - # NOTE: Could try to guess width depending on highway type. displayed_width = 1 road_xs, road_ys = zip(*road_xy_s) plt.plot(road_xs, road_ys, linewidth=displayed_width, c=color, alpha=0.8, zorder=-1) distances = np.arange(0, tree_path.length, tree_distance) - potential_trees = [tree_path.interpolate( - distance) for distance in distances] + potential_trees = [tree_path.interpolate(distance) for distance in distances] if tree_path.boundary: potential_trees += [tree_path.boundary.geoms[-1]] @@ -145,6 +187,7 @@ def place_trees(forest, ways, region, to_local, tree_distance, min_distance_2) - description='Tilia tomentosa', diameter=6, height=10, + trunk_diameter=0.5, source='add_trees.py' ) @@ -164,7 +207,7 @@ def plot_trees(bounds: Bounds, forest: Forest, tree_distance: float) -> None: print(" DONE!") -def export_map(bounds, forest, epsg_id) -> None: +def export_map(bounds: Bounds, forest: Forest, epsg_id: int) -> None: print("Exporting Map...") to_wgs84 = Transformer.from_crs(f"EPSG:{epsg_id}", "EPSG:4326", always_xy=True) interactive_map = folium.Map() @@ -197,7 +240,7 @@ def export_map(bounds, forest, epsg_id) -> None: print(" DONE!") -def export_csv(bounds, forest, wkt_polygon, tree_distance, min_distance, epsg_id) -> None: +def export_csv(bounds: Bounds, forest: Forest, wkt_polygon: str, tree_distance: float, min_distance: float, epsg_id: int) -> None: print("Exporting CSV...") with open(OUTPUT_DIR / f"{get_basename(bounds)}_trees.csv", "w") as csv: csv.write(f"# Fake trees for; {wkt_polygon}\n") @@ -212,7 +255,7 @@ def export_csv(bounds, forest, wkt_polygon, tree_distance, min_distance, epsg_id print(" DONE!") -def export_shapefile(bounds: Bounds, forest: Forest, epsg_id: str) -> None: +def export_shapefile(bounds: Bounds, forest: Forest, epsg_id: int) -> None: print("Exporting shapefile") data = [{ @@ -239,7 +282,7 @@ def export_shapefile(bounds: Bounds, forest: Forest, epsg_id: str) -> None: print(" DONE!") -def main(wkt_polygon, epsg_id, tree_distance, min_distance, import_tree_shp) -> None: +def main(wkt_polygon: str, epsg_id: int, tree_distance: float, min_distance: float, import_tree_shp) -> None: region, bounds = load_region(wkt_polygon) ways = get_osm_roads(bounds) diff --git a/python_scripts/add_trees_to_open_street_map/import_existing_trees.py b/python_scripts/add_trees_to_open_street_map/import_existing_trees.py index c45078378536f4ad2846676f124439dd82d5df94..19db832c5bcf97193e664ed27ebfd1deba13ddc0 100644 --- a/python_scripts/add_trees_to_open_street_map/import_existing_trees.py +++ b/python_scripts/add_trees_to_open_street_map/import_existing_trees.py @@ -5,18 +5,20 @@ from tree import Tree, Forest def get_existing_forest(shp_input): print(f"Importing {shp_input}") df = gpd.read_file(shp_input) - trees = [] + forest = Forest() for tree_row in df.itertuples(): point = tree_row.geometry - trees.append(Tree(point.x, point.y, + added = forest.add_tree_if_possible(0.1, point.x, point.y, description=tree_row.Bezeichnun, diameter=tree_row.Kronenbrei, type=tree_row.Baumart, trunk_diameter=tree_row.Stammumfan, height=tree_row.Baumhöhe, source=Path(shp_input).name - )) - return Forest(trees) + ) + if not added: + print(f"WARNING! tree seems to be too close to others! Is it a duplicate?\n\t{tree_row}") + return forest if __name__ == "__main__": print(repr(get_existing_forest('existing_trees/Trees_ideal_2_20240227.shp'))) diff --git a/python_scripts/add_trees_to_open_street_map/tree.py b/python_scripts/add_trees_to_open_street_map/tree.py index 8bc316277862643ba96e8d20a744aeff40dc5832..c2ac6fb2245c9f16dd29ef9684beef3a9772cd8f 100644 --- a/python_scripts/add_trees_to_open_street_map/tree.py +++ b/python_scripts/add_trees_to_open_street_map/tree.py @@ -10,8 +10,8 @@ class Tree: diameter: float height: float z: float = 0 - trunk_diameter: float = None - type: str = None + trunk_diameter: float|None = None + type: str|None = None description: str = '?' source: str = '?' color: str = 'green' @@ -39,11 +39,13 @@ class Forest(UserList): self.data = existing_trees - def add_tree_if_possible(self, min_distance_2, x, y, **kparams): + def add_tree_if_possible(self, min_distance_2, x, y, **kparams) -> bool: _nearest_tree, distance_2 = self.kd_tree.search_nn((x, y)) if distance_2 > min_distance_2: self.kd_tree.add((x, y)) self.append(Tree(x, y, **kparams)) + return True + return False @property def xs_ys_cs(self):