diff --git a/python_scripts/add_trees_to_open_street_map/.gitignore b/python_scripts/add_trees_to_open_street_map/.gitignore index bb0616adeb338f3eb3b31ced518c351e137c46c4..5e465967597634a36431f328ace95378a202ff27 100644 --- a/python_scripts/add_trees_to_open_street_map/.gitignore +++ b/python_scripts/add_trees_to_open_street_map/.gitignore @@ -1,4 +1 @@ -cache -*.html -*.csv -*.png \ No newline at end of file +cache \ 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 8442b290f672b558a9a19ee0b91cefcad731ac3d..551e22de85ee5532508dc1364eb99c743c775825 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 @@ -17,6 +17,8 @@ import overpy from pyproj import Transformer from shapely import LineString, geometry, wkt from shapely.ops import transform +import pandas as pd +import geopandas as gpd from tree import Forest from import_existing_trees import get_existing_forest @@ -47,11 +49,13 @@ IGNORE_ROADS = set(['primary', 'unclassified', 'secondary', SCRIPT_DIR = Path(__file__).resolve().parent +OUTPUT_DIR = SCRIPT_DIR / 'output' +Bounds = namedtuple("Bounds", "W S E N") def load_region(wkt_polygon): region = wkt.loads(wkt_polygon) - bounds = namedtuple("Bounds", "W S E N")(*region.bounds) + bounds = Bounds(*region.bounds) return region, bounds @@ -140,22 +144,23 @@ def place_trees(forest, ways, region, to_local, tree_distance, min_distance_2) - color='#DFFF00', type='Fake Tree', description='Tilia tomentosa', - diameter=6 + diameter=6, + source='add_trees.py' ) return forest -def plot_trees(bounds, forest, tree_distance) -> None: +def plot_trees(bounds: Bounds, forest: Forest, tree_distance: float) -> None: print("Exporting diagram...") - tree_xs, tree_ys, colors = forest.xs_ys_c6 + tree_xs, tree_ys, colors = forest.xs_ys_cs plt.scatter(tree_xs, tree_ys, s=2, c=colors) plt.grid(True) plt.title(f"{bounds}\nTree distance : {tree_distance} m") plt.gcf().set_size_inches(15, 10) plt.savefig( - SCRIPT_DIR / f"{get_basename(bounds)}.png", bbox_inches='tight', dpi=300) + OUTPUT_DIR / f"{get_basename(bounds)}.png", bbox_inches='tight', dpi=300) print(" DONE!") @@ -188,27 +193,49 @@ def export_map(bounds, forest, epsg_id) -> None: control=True ).add_to(interactive_map) - interactive_map.save(f"{get_basename(bounds)}_trees.html") + interactive_map.save(OUTPUT_DIR / f"{get_basename(bounds)}_trees.html") print(" DONE!") def export_csv(bounds, forest, wkt_polygon, tree_distance, min_distance, epsg_id) -> None: print("Exporting CSV...") - with open(SCRIPT_DIR / f"{get_basename(bounds)}_trees.csv", "w") as csv: + with open(OUTPUT_DIR / f"{get_basename(bounds)}_trees.csv", "w") as csv: csv.write(f"# Fake trees for; {wkt_polygon}\n") csv.write(f"# Tree distance along roads; {tree_distance}; [m]\n") csv.write(f"# Minimum allowed distance between trees; {min_distance}; [m]\n") csv.write(f"# EPSG; {epsg_id}\n") - csv.write("# X; Y; Type; Description; Radius\n") - csv.write("# [m]; [m]; [?]; [?]; [m]\n") + csv.write("# X; Y; Type; Description; Radius; Source\n") + csv.write("# [m]; [m]; [-]; [-]; [m]; [-]\n") for tree in forest: - csv.write(f"{tree.x};{tree.y};{tree.type};{tree.description};{tree.radius}\n") + csv.write(f"{tree.x};{tree.y};{tree.type};{tree.description};{tree.radius};{tree.source}\n") print(" DONE!") -def export_shapefile(bounds, forest, tree_distance, epsg_id): +def export_shapefile(bounds: Bounds, forest: Forest, tree_distance: float, epsg_id: str) -> None: print("Exporting shapefile") + + data = [{ + 'x': t.x, 'y': t.y, + 'Bezeichnun': t.description, 'Baumart': t.type, + 'Baumhöhe': t.height, 'Kronenbrei': t.diameter, + 'Quelle': t.source, + } + for t in forest] + + df = pd.DataFrame.from_dict(data) + gdf = gpd.GeoDataFrame( + df.drop(columns=['x', 'y']), + geometry=gpd.points_from_xy(df.x, df.y), crs=epsg_id + ) + + print(gdf) + + basename = get_basename(bounds) + shp_dir = OUTPUT_DIR / basename + shp_dir.mkdir(exist_ok=True) + gdf.to_file(shp_dir / f"trees.shp") + print(" DONE!") 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 883b7a1b20405d1fcb82c6f620a9a791ab11b39b..71342290cacadc1a6e1c87f158fcb9ebbf4392ae 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 @@ -1,3 +1,4 @@ +from pathlib import Path import geopandas as gpd from tree import Tree, Forest @@ -11,7 +12,8 @@ def get_existing_forest(shp_input): description=tree_row.Bezeichnun, diameter=tree_row.Kronenbrei, type=tree_row.Baumart, - trunk_diameter=tree_row.Stammumfan + trunk_diameter=tree_row.Stammumfan, + source=Path(shp_input).name )) return Forest(trees) diff --git a/python_scripts/add_trees_to_open_street_map/output/.gitignore b/python_scripts/add_trees_to_open_street_map/output/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..37c1c348800c3fd63c849b81ec3a71fc5407e7cb --- /dev/null +++ b/python_scripts/add_trees_to_open_street_map/output/.gitignore @@ -0,0 +1,9 @@ +*.png +*.html +*.csv +*.cpg +*.dbf +*.shp +*.prj +*.shx +*.geojson \ No newline at end of file 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 2dd775a77dc928b8327a290c9146b61fb141c2e5..412a0198c361d5d53b3d51c000f9bdeba840b6ce 100644 --- a/python_scripts/add_trees_to_open_street_map/tree.py +++ b/python_scripts/add_trees_to_open_street_map/tree.py @@ -13,6 +13,7 @@ class Tree: trunk_diameter: float = None type: str = None description: str = '?' + source: str = '?' color: str = 'green' def __len__(self): @@ -26,7 +27,7 @@ class Tree: return self.diameter / 2 def __str__(self): - return f"{self.type} ({self.description}), {self.radius or '?'} m (X={self.x:.1f}, Y={self.y:.1f})" + return f"{self.type} ({self.description}), {self.radius} m (X={self.x:.1f}, Y={self.y:.1f})" class Forest(UserList):