Note

This page was generated from usage_05_custom_data_imports.ipynb.

Usage | 5. Custom data imports

This notebook explains how custom data sets can be imported into growbikenet to adapt to a specific situation, growing more realistically.

Parameters covered: import_files

We start every Usage notebook with the standard way of importing growbikenet:

[1]:
import growbikenet as gbn

Save and import street network

Here we work with Athens. When running growbikenet, it uses OSMnx to fetch street network data from OSM. OSMnx has data caching implemented, so when running it multiple times on the same city, it will load the cached json data. Unfortunately, loading json is slow. To speed up this process, we can first download and save the (undirected) street network locally, under Athens_street_network.gpkg, so we can load that file fast into growbikenet whenever we will use it (because gpkg files are loaded fast):

[2]:
import osmnx as ox
import networkx as nx
g = ox.graph_from_place("Municipality of Athens", network_type='drive')
g = nx.MultiGraph(ox.convert.to_digraph(g))
ox.io.save_graph_geopackage(g, "Athens_street_network.gpkg")

Now, we can use this local street network file with the "street_network" key inside the import_files dictionary parameter:

[3]:
edges_ranked = gbn.growbikenet("Municipality of Athens",
                               import_files={"street_network":"Athens_street_network.gpkg"},)
==============================================
RUNNING GROWBIKENET FOR CITY: Municipality of Athens
betweenness_centrality | auto | from scratch
----------------------------------------------╮
Importing network data : 100%|████████████████| 1/1 [00:01<00:00,  1.05s/network]
Creating seed points   : 100%|████████████████| 3/3 [00:00<00:00,  4.22step/s]
Triangulation          : 100%|████████████████| 1/1 [00:00<00:00, 1032.57step/s]
Routing                : 100%|████████████████| 3/3 [00:00<00:00, 10.61step/s]
Computing edge metrics : 100%|████████████████| 2/2 [00:00<00:00, 322.54step/s]
Removing edge overlaps : 100%|████████████████| 59/59 [00:00<00:00, 512.57edge/s]
Exporting data         : 100%|████████████████| 1/1 [00:00<00:00, 29.33step/s]
----------------------------------------------╯
Data exported to ./results/
----------------------------------------------
FINISHED IN 0:00:02
==============================================

This way, the street network file can also come from any other data provider than OSM, given that the data is in the same format, or it can be modified.

Limiting the street network definition

It could be possible that the city decides it will build protected bicycle infrastructure only on certain streets. Here we assume these are only the primary and secondary roads, implemented with a custom filter:

[4]:
custom_filter_streets = ['["highway"~"primary"]',
                         '["highway"~"secondary"]']
[5]:
g_limited = ox.graph_from_place("Municipality of Athens",
                        custom_filter=custom_filter_streets,
                        retain_all=False,) # fetch only the largest connected component
g_limited = nx.MultiGraph(ox.convert.to_digraph(g_limited))
ox.io.save_graph_geopackage(g_limited, "Athens_street_network_limited.gpkg")

This limitation selects a much smaller subnetwork (red) of the whole street network (white):

[6]:
ec = ["r" if data["highway"] == "primary" or data["highway"] == "secondary" else "w" for u, v, key, data in g.edges(keys=True, data=True)]
el = [1.5 if data["highway"] == "primary" or data["highway"] == "secondary" else 0.25 for u, v, key, data in g.edges(keys=True, data=True)]
ox.plot_graph(g, node_size=0, edge_linewidth=el, edge_color=ec);
../_images/usage_05_custom_data_imports_15_0.png

Because this limited network is so sparse, we need to set the snap distance for seed points to a high value before running growbikenet - otherwise the resulting network will be disconnected and highly incomplete (see more details on how to use the settings in Usage notebook 6):

[7]:
gbn.settings.seed_point_snap_distance = 1500
[8]:
edges_ranked_limited = gbn.growbikenet("Municipality of Athens",
                               import_files={"street_network":"Athens_street_network_limited.gpkg"},)
==============================================
RUNNING GROWBIKENET FOR CITY: Municipality of Athens
betweenness_centrality | auto | from scratch
----------------------------------------------╮
Importing network data : 100%|████████████████| 1/1 [00:00<00:00, 15.80network/s]
Creating seed points   : 100%|████████████████| 3/3 [00:00<00:00, 36.52step/s]
Quadrangulation        : 100%|████████████████| 1/1 [00:00<00:00, 571.35step/s]
Routing                : 100%|████████████████| 3/3 [00:00<00:00, 18.96step/s]
Computing edge metrics : 100%|████████████████| 2/2 [00:00<00:00, 130.94step/s]
Removing edge overlaps : 100%|████████████████| 103/103 [00:00<00:00, 471.81edge/s]
Exporting data         : 100%|████████████████| 1/1 [00:00<00:00, 28.80step/s]
----------------------------------------------╯
Data exported to ./results/
----------------------------------------------
FINISHED IN 0:00:01
==============================================

Let’s reset the snap distance to 'auto' now:

[9]:
gbn.settings.seed_point_snap_distance = 'auto'

Due to the limited street network, the resulting bicycle network is also much sparser (green) than the network grown on the full street network (orange):

[10]:
import folium
viz = edges_ranked.explore(tiles="CartoDB Positron",
                     style_kwds={"weight": 3, "color": "#f19730"},
                        name="Grown bike network (on full street network)")
viz = edges_ranked_limited.iloc[1:].explore(m=viz,
                     style_kwds={"weight": 3, "color": "#096a51"},
                        name="Grown bike network (on limited street network)")
folium.LayerControl().add_to(viz)
viz
[10]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Save and import bike network

Analogously, an existing bike network can be downloaded, saved, and imported. The download only needs some more preparation to set up the custom filter:

[11]:
custom_filter = ['["cycleway"~"track"]',
          '["highway"~"cycleway"]',
          '["highway"~"path"]["bicycle"~"designated"]',
          '["cycleway:right"~"track"]',
          '["cycleway:left"~"track"]',
          '["cycleway:both"~"track"]',
          '["cyclestreet"]',
          '["highway"~"living_street"]'
        ]
for custom_tag in ["cycleway", "bicycle", "cycleway:right", "cycleway:left", "cycleway:both", "cyclestreet"]:
    if custom_tag not in ox.settings.useful_tags_way:
        ox.settings.useful_tags_way.extend(custom_tag)
[12]:
g = ox.graph_from_place("Municipality of Athens",
                        custom_filter=custom_filter,
                        retain_all=True,) #fetch all connected components
g = nx.MultiGraph(ox.convert.to_digraph(g))
ox.io.save_graph_geopackage(g, "Athens_bike_network.gpkg")

Now it can be imported with the "bike_network" key inside the import_files dictionary parameter:

[13]:
edges_ranked = gbn.growbikenet("Municipality of Athens",
                               existing_network_spacing='auto',
                               seed_point_linking ='triangulate_delaunay',
                               import_files={"street_network":"Athens_street_network.gpkg",
                                             "bike_network":"Athens_bike_network.gpkg"},)
==============================================
RUNNING GROWBIKENET FOR CITY: Municipality of Athens
betweenness_centrality | auto | from existing bike network
----------------------------------------------╮
Importing network data : |                | 2/? [00:01<00:00,  1.01network/s]
Creating seed points   : 100%|████████████████| 4/4 [00:00<00:00,  4.80step/s]
Triangulation          : 100%|████████████████| 1/1 [00:00<00:00, 771.86step/s]
Routing                : 100%|████████████████| 3/3 [00:00<00:00,  8.42step/s]
Computing edge metrics : 100%|████████████████| 2/2 [00:00<00:00, 152.61step/s]
Removing edge overlaps : 100%|████████████████| 81/81 [00:00<00:00, 364.47edge/s]
Exporting data         : 100%|████████████████| 1/1 [00:00<00:00, 20.74step/s]
----------------------------------------------╯
Data exported to ./results/
----------------------------------------------
FINISHED IN 0:00:04
==============================================

[14]:
import folium
viz = edges_ranked.iloc[:1].explore(tiles="CartoDB Positron",
                     style_kwds={"weight": 2, "color": "#9999cc"},
                        name="Existing bike network")
viz = edges_ranked.iloc[1:].explore(m=viz,
                     style_kwds={"weight": 3, "color": "#096a51"},
                        name="Grown bike network")
folium.LayerControl().add_to(viz)
viz
[14]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Relaxing the bike network definition

The import of a custom bike network is useful to change the definition of the bike network. For example, the definition could be relaxed to also consider primary streets as protected bike infrastructure, assuming that the city plans to implement protected bike lanes along all primary streets:

[15]:
custom_filter_relaxed = custom_filter + ['["highway"~"primary"]']
[16]:
g = ox.graph_from_place("Municipality of Athens",
                        custom_filter=custom_filter_relaxed,
                        retain_all=True,) #fetch all connected components
g = nx.MultiGraph(ox.convert.to_digraph(g))
ox.io.save_graph_geopackage(g, "Athens_bike_network_relaxed.gpkg")
[17]:
edges_ranked_relaxed = gbn.growbikenet("Municipality of Athens",
                               existing_network_spacing='auto',
                               seed_point_linking ='triangulate_delaunay',
                               import_files={"street_network":"Athens_street_network.gpkg",
                                             "bike_network":"Athens_bike_network_relaxed.gpkg"},)
==============================================
RUNNING GROWBIKENET FOR CITY: Municipality of Athens
betweenness_centrality | auto | from existing bike network
----------------------------------------------╮
Importing network data : |                | 2/? [00:01<00:00,  1.00network/s]
Creating seed points   : 100%|████████████████| 4/4 [00:01<00:00,  3.07step/s]
Triangulation          : 100%|████████████████| 1/1 [00:00<00:00, 643.59step/s]
Routing                : 100%|████████████████| 3/3 [00:00<00:00,  6.22step/s]
Computing edge metrics : 100%|████████████████| 2/2 [00:00<00:00, 83.45step/s]
Removing edge overlaps : 100%|████████████████| 144/144 [00:00<00:00, 159.52edge/s]
Exporting data         : 100%|████████████████| 1/1 [00:00<00:00, 18.79step/s]
----------------------------------------------╯
Data exported to ./results/
----------------------------------------------
FINISHED IN 0:00:05
==============================================

This creates a different looking bicycle network:

[18]:
viz = edges_ranked_relaxed.iloc[:1].explore(tiles="CartoDB Positron",
                     style_kwds={"weight": 2, "color": "#9999cc"},
                        name="Existing bike network (relaxed)")
viz = edges_ranked_relaxed.iloc[1:].explore(m=viz,
                     style_kwds={"weight": 3, "color": "#096a51"},
                        name="Grown bike network")
folium.LayerControl().add_to(viz)
viz
[18]:
Make this Notebook Trusted to load map: File -> Trust Notebook

Import seed points

Usage notebook 1 explains how to import custom seed points.

Import city boundary

Rest to be written.