From b5e8eb0778c82e0cebd16b79661c9109223e6850 Mon Sep 17 00:00:00 2001 From: Manuel-Knepper Date: Fri, 11 Sep 2026 11:52:04 +0200 Subject: [PATCH 1/4] import functions individually in linkbikenet.py rather than all at once --- linkbikenet/linkbikenet.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/linkbikenet/linkbikenet.py b/linkbikenet/linkbikenet.py index d6433a4..a48a9a3 100644 --- a/linkbikenet/linkbikenet.py +++ b/linkbikenet/linkbikenet.py @@ -1,11 +1,28 @@ # imports +from . import settings import os import osmnx as ox import networkx as nx import pandas as pd +import geopandas as gpd from collections import defaultdict -from linkbikenet.functions import * +from linkbikenet.functions import ( + import_network, + import_bike_network, + resolve_crs_calculations, + map_edges_to_bike_infrastructure, + find_edges_to_drop, + graph_edges_to_gdf, + pair_between_largest_components, + pair_between_largest_and_closest_components, + pair_between_closest_components, + get_correct_edgetuples, + create_gdf_with_geoms, + slugify, + calculate_network_statistics, + mark_joined_component + ) def linkbikenet( city_query, From 10585bea6f171f69dd6914f8c64710e735c09e94 Mon Sep 17 00:00:00 2001 From: Manuel-Knepper Date: Fri, 11 Sep 2026 12:56:27 +0200 Subject: [PATCH 2/4] add typecheck for city_id --- linkbikenet/linkbikenet.py | 4 ++++ linkbikenet/settings.py | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/linkbikenet/linkbikenet.py b/linkbikenet/linkbikenet.py index a48a9a3..5881016 100644 --- a/linkbikenet/linkbikenet.py +++ b/linkbikenet/linkbikenet.py @@ -6,6 +6,8 @@ import pandas as pd import geopandas as gpd from collections import defaultdict +from tqdm.auto import tqdm +import time from linkbikenet.functions import ( import_network, @@ -73,6 +75,8 @@ def linkbikenet( raise TypeError("connection_strategy must be 'largest_to_second', 'largest_to_closest' or 'closest_components'") if type(export_data) is not bool: raise TypeError("export_data must be a boolean") + if type(city_id) is not str: + raise TypeError("city_id must be a string") if export_file_format != "geojson" and export_file_format != "gpkg": raise ValueError("export_file_format must be 'geojson' or 'gpkg'") if type(import_files) is not dict: diff --git a/linkbikenet/settings.py b/linkbikenet/settings.py index 2376ec0..ab05ec6 100644 --- a/linkbikenet/settings.py +++ b/linkbikenet/settings.py @@ -2,4 +2,6 @@ import_path = "./" export_path = "./results/" -crs_projected = 'auto' \ No newline at end of file +crs_projected = 'auto' +_PROGRESS_BAR_DESC_LENGTH = 24 +_PROGRESS_BAR_LENGTH = 23 \ No newline at end of file From 834cc5aeca46ce63169e09be5f8bb1332bfde013 Mon Sep 17 00:00:00 2001 From: Manuel-Knepper Date: Fri, 11 Sep 2026 13:25:52 +0200 Subject: [PATCH 3/4] add tqdm progress bar --- linkbikenet/functions.py | 12 ++++++++++ linkbikenet/linkbikenet.py | 45 +++++++++++++++++++++++++++++--------- linkbikenet/settings.py | 3 ++- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/linkbikenet/functions.py b/linkbikenet/functions.py index d584b22..cb78b97 100644 --- a/linkbikenet/functions.py +++ b/linkbikenet/functions.py @@ -7,6 +7,18 @@ import numpy as np from scipy.spatial import cKDTree from shapely.geometry import LineString +from tqdm.auto import tqdm + +def initialize_progress_bar(desc_string, total=1, unit="step"): + """Initialize tqdm progress bar. + """ + return tqdm( + desc=("{:<"+str(settings._PROGRESS_BAR_DESC_LENGTH)+"}").format(desc_string), + total=total, + unit=unit, + bar_format='{l_bar}{bar:'+str(settings._PROGRESS_BAR_LENGTH-7)+'}{r_bar}', + disable=settings.silent, + ) def import_network(street_network): """Import and project a street network from gpkg file diff --git a/linkbikenet/linkbikenet.py b/linkbikenet/linkbikenet.py index 5881016..a68dc0c 100644 --- a/linkbikenet/linkbikenet.py +++ b/linkbikenet/linkbikenet.py @@ -10,6 +10,7 @@ import time from linkbikenet.functions import ( + initialize_progress_bar, import_network, import_bike_network, resolve_crs_calculations, @@ -68,6 +69,8 @@ def linkbikenet( gdf: geopandas.GeoDataFrame geodataframe with the proposed links, ordered after strategy chosen """ + + starttime = time.time() # check if user input is valid if type(city_query) != str: raise TypeError("city_name must be a string") @@ -75,8 +78,9 @@ def linkbikenet( raise TypeError("connection_strategy must be 'largest_to_second', 'largest_to_closest' or 'closest_components'") if type(export_data) is not bool: raise TypeError("export_data must be a boolean") - if type(city_id) is not str: - raise TypeError("city_id must be a string") + if city_id is not None: + if type(city_id) is not str: + raise TypeError("city_id must be a string") if export_file_format != "geojson" and export_file_format != "gpkg": raise ValueError("export_file_format must be 'geojson' or 'gpkg'") if type(import_files) is not dict: @@ -92,7 +96,7 @@ def linkbikenet( city_boundary = ox.geocoder.geocode_to_gdf(city_query) if import_files['bike_network'] is not None: - print("Importing bike network..") + progress_bar = initialize_progress_bar("Importing bike network data", 1, "network") h = import_bike_network(import_files['bike_network']) nodes_h = ox.graph_to_gdfs(h, nodes=True, edges=False, node_geometry=True) proj_crs = resolve_crs_calculations(nodes_h, settings.crs_projected) @@ -100,12 +104,12 @@ def linkbikenet( h = nx.Graph(h) if import_files['street_network'] is not None: - print("Importing street network..") + progress_bar = initialize_progress_bar("Importing bike network data", 1, "network") g = import_network(import_files['street_network']) else: ### downloading and preprocessing data from OSM - print("Downloading OSM data..") + progress_bar = initialize_progress_bar("Downloading OSM data", 1, "network") ox.settings.useful_tags_way = ["highway", "cycleway", "cycleway:right", "cycleway:left", "cycleway:both", "cyclestreet"] @@ -114,7 +118,10 @@ def linkbikenet( g = ox.graph_from_place( city_query, network_type='all_public', simplify=False, retain_all=True ) + progress_bar.update(1) + progress_bar.close() + progress_bar = initialize_progress_bar("Processing network", 2) g = ox.simplify_graph( g, edge_attrs_differ=['cycleway', 'highway', 'cycleway:right', 'cycleway:left', 'cycleway:both'], @@ -125,11 +132,16 @@ def linkbikenet( proj_crs = resolve_crs_calculations(nodes_g, settings.crs_projected) g = ox.project_graph(g, to_crs=proj_crs) + progress_bar.update(1) + # check which edges have existing bicycle infrastructure and assign "pbi = 1" to them, all other edges get "pbi = 0". g = map_edges_to_bike_infrastructure(g) + progress_bar.update(1) + progress_bar.close() + # finding parallel edges and dropping them - print("Dropping parallel edges..") + #print("Dropping parallel edges..") edges_to_drop = find_edges_to_drop(g) g.remove_edges_from(edges_to_drop) @@ -163,8 +175,9 @@ def linkbikenet( closest_pairs = [] step = 1 + progress_bar = initialize_progress_bar("Postprocess data", 5) # check which strategy was chosen and execute the corresponding algorithm - print("Calculating links...") + #print("Calculating links...") if connection_strategy == "largest_to_second": for i in range(to_iterate): wcc = [H.subgraph(c).copy() for c in sorted(nx.connected_components(H), key=lambda c: sum( @@ -233,7 +246,7 @@ def linkbikenet( main_component.update(component_u) H[pair[0]][pair[1]]["lcc_step"] = step step += 1 - + progress_bar.update(1) # find paths between node pairs so we can generate geometries paths = [] for pair in closest_pairs: @@ -245,6 +258,7 @@ def linkbikenet( H.remove_edges_from(closest_pairs) edges_pbi_gdf = graph_edges_to_gdf(H) + progress_bar.update(1) edges_gdf = graph_edges_to_gdf(G) df = pd.DataFrame() @@ -252,6 +266,7 @@ def linkbikenet( df['edge_list'] = df.nodelist.apply(lambda x: get_correct_edgetuples(edges_gdf, x)) gdf = create_gdf_with_geoms(df, edges_gdf) + progress_bar.update(1) # reset Graph if import_files['bike_network'] is not None: @@ -260,11 +275,12 @@ def linkbikenet( H = G.edge_subgraph(edges).copy() # calculating connectivity metrics - print("Calculating connectivity metrics...") + #print("Calculating connectivity metrics...") network_lengths = [] lcc_lengths = [] edge_lengths = gdf['geometry'].length initial_network_length, initial_lcc_length = calculate_network_statistics(H) + progress_bar.update(1) for i in range(len(gdf)): H.add_edge(closest_pairs[i][0], closest_pairs[i][1], length=edge_lengths[i]) @@ -300,6 +316,8 @@ def linkbikenet( gdf['lcc_gain'] = gdf['lcc_length'].diff().fillna(0) gdf['ordering'] = gdf.index + progress_bar.update(1) + progress_bar.close() #edges_pbi_gdf = edges_gdf[edges_gdf["pbi"] == 1] @@ -323,18 +341,25 @@ def linkbikenet( if export_data: ### save data - print("Saving data..") + #print("Saving data..") edges_pbi_gdf.drop(["osmid"], axis=1, inplace=True) city_boundary.to_crs(epsg=4326, inplace=True) if export_file_format == "geojson": + progress_bar = initialize_progress_bar("Exporting data", 3, "file") gdf.to_file(settings.export_path + export_data_filename, driver="GeoJSON", RFC7946="YES") + progress_bar.update(1) edges_pbi_gdf.to_file(settings.export_path + slugify(city_string) + "-linkbikenet-" + connection_strategy + "-existing_bike_network.geojson", driver="GeoJSON", RFC7946="YES") + progress_bar.update(1) city_boundary.to_file(settings.export_path + slugify(city_string) + "-city_boundary.geojson", driver="GeoJSON", RFC7946="YES") + progress_bar.update(1) elif export_file_format == "gpkg": + progress_bar = initialize_progress_bar("Exporting data", 1, "file") gdf.to_file(settings.export_path + export_data_filename, driver="GPKG", layer="Identified links") edges_pbi_gdf.to_file(settings.export_path + export_data_filename, driver="GPKG", layer="Existing bike network", append=True) city_boundary.to_file(settings.export_path + export_data_filename, driver="GPKG", layer="City boundary", append=True) + progress_bar.update(1) + progress_bar.close() return gdf \ No newline at end of file diff --git a/linkbikenet/settings.py b/linkbikenet/settings.py index ab05ec6..5ad127a 100644 --- a/linkbikenet/settings.py +++ b/linkbikenet/settings.py @@ -4,4 +4,5 @@ export_path = "./results/" crs_projected = 'auto' _PROGRESS_BAR_DESC_LENGTH = 24 -_PROGRESS_BAR_LENGTH = 23 \ No newline at end of file +_PROGRESS_BAR_LENGTH = 23 +silent = False \ No newline at end of file From 3b69d34ad531d488fb431093775e54a8d95dbda8 Mon Sep 17 00:00:00 2001 From: Manuel-Knepper Date: Fri, 11 Sep 2026 13:26:28 +0200 Subject: [PATCH 4/4] removing old print statements --- linkbikenet/linkbikenet.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/linkbikenet/linkbikenet.py b/linkbikenet/linkbikenet.py index a68dc0c..ca9de60 100644 --- a/linkbikenet/linkbikenet.py +++ b/linkbikenet/linkbikenet.py @@ -141,7 +141,6 @@ def linkbikenet( progress_bar.close() # finding parallel edges and dropping them - #print("Dropping parallel edges..") edges_to_drop = find_edges_to_drop(g) g.remove_edges_from(edges_to_drop) @@ -177,7 +176,6 @@ def linkbikenet( progress_bar = initialize_progress_bar("Postprocess data", 5) # check which strategy was chosen and execute the corresponding algorithm - #print("Calculating links...") if connection_strategy == "largest_to_second": for i in range(to_iterate): wcc = [H.subgraph(c).copy() for c in sorted(nx.connected_components(H), key=lambda c: sum( @@ -275,7 +273,6 @@ def linkbikenet( H = G.edge_subgraph(edges).copy() # calculating connectivity metrics - #print("Calculating connectivity metrics...") network_lengths = [] lcc_lengths = [] edge_lengths = gdf['geometry'].length @@ -341,7 +338,6 @@ def linkbikenet( if export_data: ### save data - #print("Saving data..") edges_pbi_gdf.drop(["osmid"], axis=1, inplace=True) city_boundary.to_crs(epsg=4326, inplace=True) if export_file_format == "geojson":