Usage | 6. Data imports for prioritizing growth

This notebook explains how custom data sets can be imported into GrowBikeNet to reprioritize growth, adapting to a local situation and growing more realistically.

Parameters covered: import_files['point_data'], import_files['trip_data']

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

import growbikenet as gbn

Re-prioritizing growth with point data

Here we work with Turin, Italy. By default GrowBikeNet orders edges by betweenness centrality, which is a proxy for expected flow of cyclists in the city. However, the city might have access to additional data sets that can inform the priority of some links. For example, in Turin, there is point data available on traffic crashes. It could be the city’s goal to prioritize bicycle network development in places where there are more traffic crashes reported. This is possible by loading the crash data via the import_files['point_data'] parameter.

First, let’s have a look at the crash data:

Hide code cell source

gbn.settings.import_path = '../../tests/test_data/'
import folium
import geopandas as gpd
growable_network = gpd.read_file(gbn.settings.import_path+"turin_growable_network.gpkg", layer="edges")
crashes = gpd.read_file(gbn.settings.import_path+"turin_crashes.gpkg")

viz = growable_network.explore(
    tiles="CartoDB Positron",
    style_kwds={"weight": 0.5, "color": "#999999"},
    tooltip = False,
    name="Existing street and bike network",
)
viz = crashes.explore(
    m=viz, 
    style_kwds={"color": "#ff0000"},
    name="Traffic crashes",
)
folium.LayerControl().add_to(viz)
viz
Make this Notebook Trusted to load map: File -> Trust Notebook

Caution

Cyclist crash reports and similar urban data are likely suffering from strong survivorship and data collection biases. For example, centrally located places that do not report any traffic crashes with cyclists are potentially so hostile to cyclists that nobody is cycling there in the first place. It could be more important to improve these places than those that report many crashes. Also, crashes with cyclists are notoriously underreported, making such data unreliable for data-driven planning.

Now we are going to compare Turin’s growth without and with this data set, working with local data imports, and ignoring Turin’s existing bike network for clearer demonstration of the re-prioritization feature. We also turn off rerouting first, as it would change the paths too much:

gbn.settings.reroute=False
edges_ordered = gbn.growbikenet(
    "Turin",
    import_files = {
        'growable_network': 'turin_growable_network.gpkg'
    },
    seed_point_linking = 'triangulate_delaunay',
)
edges_ordered_with_crashes = gbn.growbikenet(
    "Turin",
    import_files = {
        'growable_network': 'turin_growable_network.gpkg',
        'point_data': 'turin_crashes.gpkg',
    },
    seed_point_linking = 'triangulate_delaunay',
)

Hide code cell source

length_to_compare = 40000 # Show the first meters built
i_ordered = edges_ordered[edges_ordered.length_cumulative > length_to_compare].index[0]
i_ordered_with_crashes = edges_ordered_with_crashes[edges_ordered_with_crashes.length_cumulative > length_to_compare].index[0]

viz = edges_ordered_with_crashes.iloc[:i_ordered_with_crashes].explore(
    tiles="CartoDB Positron",
    style_kwds={"weight": 6, "color": "#f19730"},
    name="Grown bike network, "+str(int(length_to_compare/1000))+"km, re-prioritized by crashes"
)
viz = edges_ordered.iloc[:i_ordered].explore(
    m=viz, 
   
    style_kwds={"weight": 3, "color": "#096a51"},
    name="Grown bike network, "+str(int(length_to_compare/1000))+"km"
)
viz = crashes.explore(
    m=viz, 
    style_kwds={"color": "#ff0000"},
    name="Traffic crashes"
)
folium.LayerControl().add_to(viz)
viz
Make this Notebook Trusted to load map: File -> Trust Notebook

Observe how the first 40km of the re-prioritized network (orange) passes through more crash hot-spots than the network that is not informed by the crash data (green).

Re-prioritizing growth with trip data

Similarly, GrowBikeNet allows to re-prioritize growth using imported trip data via the import_files['trip_data'] parameter. The most common application is traffic flows that are often measured by cities, to prioritize network development where people are actually moving.

We have e-scooter trip data available for Turin. Trip data has to be available in a special format: csv file, with the columns o_lat, o_lon, d_lat, d_lon standing for origin and destination coordinates, and an optional num column encoding the number of trips between each origin and destination:

import pandas as pd
pd.read_csv(gbn.settings.import_path + "turin_trips.csv")
o_lat o_lon d_lat d_lon
0 45.076498 7.625700 45.083780 7.626436
1 45.042701 7.640026 45.045875 7.645144
2 45.057113 7.636342 45.060699 7.633862
3 45.096796 7.632997 45.079011 7.677199
4 45.094253 7.633431 45.096460 7.649023
... ... ... ... ...
617 45.107940 7.645396 45.097489 7.652072
618 45.084865 7.668145 45.087755 7.666159
619 45.070671 7.684370 45.072679 7.667050
620 45.097399 7.704756 45.091234 7.693056
621 45.094010 7.713072 45.104408 7.676748

622 rows × 4 columns

edges_ordered_with_trips = gbn.growbikenet(
    "Turin",
    import_files = {
        'growable_network': 'turin_growable_network.gpkg',
        'trip_data': 'turin_trips.csv',
    },
    seed_point_linking = 'triangulate_delaunay',
)

Comparing original network with trip-re-prioritized network:

Hide code cell source

i_ordered_with_trips = edges_ordered_with_trips[
    edges_ordered_with_trips.length_cumulative > length_to_compare
].index[0]
viz = edges_ordered_with_trips.iloc[:i_ordered_with_trips].explore(
    tiles="CartoDB Positron",
    style_kwds={"weight": 6, "color": "#f19730"},
    name="Grown bike network, "+str(int(length_to_compare/1000))+"km, re-prioritized by trips",
)
viz = edges_ordered.iloc[:i_ordered].explore(
    m=viz, 
    style_kwds={"weight": 3, "color": "#096a51"},
    name="Grown bike network, "+str(int(length_to_compare/1000))+"km",
)

folium.LayerControl().add_to(viz)
viz
Make this Notebook Trusted to load map: File -> Trust Notebook

Again there are clear differences visible.

Tuning point and trip data impact

If both point and trip data are loaded, GrowBikeNet uses both to re-prioritize growth, via the setting settings.import_data_trip_point_balance. This setting must be between 0 and 1, where 0 means no trip impact and full point impact, 1 means full trip impact and no point impact, and 0.5 means balanced impact of both. If only point or only trip data is imported, this setting is ignored.

Further, the impact of any imported data on re-prioritzation is controlled via the setting settings.import_data_impact. If set to 0, imported data has no impact. The default value of 9 means that edges in the network are treated up to around 9 times more important if they are close to many points or trips.