Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/development.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

edges_ordered = lbn.linkbikenet(
"Frederiksberg",
connection_strategy='closest_components',
connection_strategy='largest_to_closest',
import_files={
'city_boundary': 'boundaries/'+city_id+'.geojson',
'street_network': 'streetbike_networks/'+city_id+'.gpkg',
},
export_file_format = "gpkg",
)

print(edges_ordered['network_length'])
# print(edges_ordered['network_length'])
20 changes: 16 additions & 4 deletions linkbikenet/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
"""
LinkBikeNet is a Python package to find links between the disconnected
components in a city’s bicycle network.

Full documentation at: https://docs.bikenetkit.org/LinkBikeNet/

If you use LinkBikeNet, please cite: https://doi.org/10.1098/rsos.201130
"""

import contextlib
from importlib.metadata import PackageNotFoundError, version

from . import config
from . import functions
from linkbikenet.linkbikenet import linkbikenet
from . import constants
from . import linkbikenet
from . import osm
from . import settings
from . import functions

__author__ = "MS, MK"
__author_email__ = "email@domain.com"
__author__ = "LinkBikeNet contributors"
__author_email__ = "michael@szell.net"

with contextlib.suppress(PackageNotFoundError):
__version__ = version("linkbikenet")
35 changes: 0 additions & 35 deletions linkbikenet/config.py

This file was deleted.

33 changes: 20 additions & 13 deletions linkbikenet/functions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import config
from . import osm
from . import settings
from . import constants
import re
Expand Down Expand Up @@ -143,29 +143,36 @@ def resolve_crs_calculations(gdf, crs_projected=settings.crs_projected):
settings.crs_projected = crs_projected
return crs_projected

def map_edges_to_bike_infrastructure(g):
"""
map if edges in graph have bike infrastructure as specified in config.py

def assign_pbi_to_edges(g):
"""Assign a boolean pbi attribute to all edges in a graph depending if they
are considered protected bike infrastructure (pbi).

Parameters
----------
g :networkx.MultiDiGraph
simplified graph representing the street network
g : networkx.MultiDiGraph
Simplified graph representing the street network.

Returns
-------
g : networkx.MultiDiGraph
simplified graph representing the street network, with added binary edge attribute "pbi"
Simplified graph representing the street network, with added binary edge attribute "pbi".
"""

# add binary edge attribute "pbi" (protected bike infra: True/False)
for edge in g.edges(keys=True):
if g.edges[edge].get("cycleway") in config.cycleway_bike_infra or g.edges[edge].get("cycleway:right") in config.cycleway_right_bike_infra or g.edges[edge].get("cycleway:left") in config.cycleway_left_bike_infra or g.edges[edge].get("cycleway:both") in config.cycleway_both_bike_infra or g.edges[edge].get("highway") in config.highway_bike_infra or g.edges[edge].get("cyclestreet") or g.edges[edge].get("bicycle_road") or g.edges[edge].get("highway") in config.highway_bike_infra_extended and g.edges[edge].get("bicycle") in config.bicycle_bike_infra and g.edges[edge].get("access") != 'private':
g.edges[edge]["pbi"] = 1
else:
g.edges[edge]["pbi"] = 0
assigned_pbi = 0
for tag in osm.PBI_DICT:
if g.edges[edge].get(tag) in osm.PBI_DICT[tag]:
assigned_pbi = 1
if not assigned_pbi: # Check if the edge fulfills a subcriterion
for tag in osm.PBI_DICT_SUB:
subtag = list(osm.PBI_DICT_SUB[tag][1].keys())[0]
if g.edges[edge].get(tag) in osm.PBI_DICT_SUB[tag][0] and g.edges[edge].get(subtag) in osm.PBI_DICT_SUB[tag][1][subtag] and g.edges[edge].get("access") != 'private':
assigned_pbi = 1
g.edges[edge]["pbi"] = assigned_pbi

return g


def find_edges_to_drop(g):
"""
find parallel edges that have different pbi values, list the ones with pbi=0
Expand Down
16 changes: 10 additions & 6 deletions linkbikenet/linkbikenet.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
initialize_progress_bar,
import_network,
import_bike_network,
map_edges_to_bike_infrastructure,
assign_pbi_to_edges,
mark_joined_component,
resolve_crs_calculations,
shortest_path_components,
Expand Down Expand Up @@ -70,7 +70,8 @@ def linkbikenet(
File format for the data export, relevant if export_data set to True.
Default "geojson", also possible "gpkg". If exporting as geojson,
generates extra files for street network and city boundary. If
exporting as gkpg, these are added all in one file as extra layers.
exporting as gkpg, these are added all in one file as extra layers.
Existing files are overwritten.
import_files: dict, default {}
The following key:value entries can be set:

Expand Down Expand Up @@ -170,7 +171,7 @@ def linkbikenet(
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)
g = assign_pbi_to_edges(g)

progress_bar.update(1)

Expand Down Expand Up @@ -327,11 +328,14 @@ def linkbikenet(
city_boundary.to_file(settings.export_path['results'] + slugify(city_string) + "-linkbikenet-city_boundary.geojson", driver="GeoJSON", RFC7946="YES")
progress_bar.update(1)
elif export_file_format == "gpkg":
f = settings.export_path['results'] + export_data_filename
if os.path.exists(f):
os.remove(f) # mode="w" does not work for to_file with gpkg. It always appends. Therefore, existing file needs to be deleted.
progress_bar = initialize_progress_bar("Exporting data", 1, "file")
linked_components.to_file(settings.export_path['results'] + export_data_filename, driver="GPKG", layer="Identified links")
edges_pbi_gdf.to_file(settings.export_path['results'] + export_data_filename, driver="GPKG", layer="Existing bike network",
linked_components.to_file(f, driver="GPKG", layer="Identified links")
edges_pbi_gdf.to_file(f, driver="GPKG", layer="Existing bike network",
append=True)
city_boundary.to_file(settings.export_path['results'] + export_data_filename, driver="GPKG", layer="City boundary",
city_boundary.to_file(f, driver="GPKG", layer="City boundary",
append=True)
progress_bar.update(1)
progress_bar.close()
Expand Down
80 changes: 80 additions & 0 deletions linkbikenet/osm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Global constants for `growbikenet` related to OpenStreetMap (OSM). These are
meant for advanced users only who have a good understanding of OSM tags.

GROWABLE_NETWORK_CUSTOM_FILTER : list[str] or None
Custom filter for all infrastructure elements that are considered as
growable by `growbikenet`. By default, `growbikenet` uses a custom filter
to retrieve the combined drive and pbi (protected bicycle infrastructure)
network. To only consider the drive network, set
`GROWABLE_NETWORK_CUSTOM_FILTER` to None and `GROWABLE_NETWORK_TYPE` to
'drive'. However, doing so can lead to issues:
https://github.com/BikeNetKit/GrowBikeNet/issues/255.
By default, `GROWABLE_NETWORK_CUSTOM_FILTER` is a superset of
`PBI_CUSTOM_FILTER`, so it is best to keep them in sync.
GROWABLE_NETWORK_TYPE : {'drive', 'all', 'all_public', 'bike', 'drive_service', 'walk'}, default 'drive'
What type of street network to retrieve for the growable network if `GROWABLE_NETWORK_CUSTOM_FILTER` is None.
PBI_CUSTOM_FILTER : list[str]
Custom filter for protected bicycle infrastructure (pbi). Always keep in
sync with `PBI_DICT` and `PBI_DICT_SUB`. By default, `PBI_CUSTOM_FILTER` is
a subset of `GROWABLE_NETWORK_CUSTOM_FILTER`, so it is best to keep them in
sync.
PBI_DICT : dict
Dictionary for protected bicycle infrastructure (pbi), for single criteria.
Always keep in sync with `PBI_CUSTOM_FILTER`.
PBI_DICT_SUB : dict
Dictionary for protected bicycle infrastructure (pbi), with one
sub-criterion. For example, highway~path AND bicycle~designated. Always
adds a second criterion access!~private. Always keep in sync with
`PBI_CUSTOM_FILTER`.
"""

# Populate ox.settings.useful_tags_way to make application of custom filter possible
import osmnx as ox
for custom_tag in ["highway", "cycleway", "bicycle", "cycleway:right", "cycleway:left", "cycleway:both", "cyclestreet", "access", "area", "service", "motor_vehicle", "motorcar"]: # This list should contain all tags used in any custom filters
if custom_tag not in ox.settings.useful_tags_way:
ox.settings.useful_tags_way.extend(custom_tag)


GROWABLE_NETWORK_CUSTOM_FILTER = [ # adapted from https://github.com/gboeing/osmnx/blob/2fc39cb2792ff869881b99f724a2d97f0e958667/osmnx/_overpass.py#L77
# Car infra - Instead of excluding what we don't want, we only include what we want. Note: we are OK with alleys and driveways
'["highway"~"motorway|trunk|primary|secondary|tertiary|residential|motorway_link|trunk_link|primary_link|secondary_link|tertiary_link|service|unclassified"]["highway"!~"abandoned|construction|no|planned|platform|proposed|raceway|razed|rest_area|services"]["service"!~"emergency_access|parking|parking_aisle|private"]["area"!~"yes"]["access"!~"private"]["motor_vehicle"!~"no"]["motorcar"!~"no"]',
# Bike infra, copied from PBI_CUSTOM_FILTER
'["highway"~"cycleway|living_street"]',
'["highway"~"path|pedestrian|footway"]["bicycle"~"designated|yes|permissive"]["access"!~"private"]',
'["cyclestreet"~"yes"]',
'["bicycle_road"~"yes"]',
'["cycleway"~"track"]',
'["cycleway:right"~"track|opposite_track"]', # opposite_track is deprecated, but could still exist
'["cycleway:left"~"track|opposite_track"]', # opposite_track is deprecated, but could still exist
'["cycleway:both"~"track|opposite_track"]', # opposite_track is deprecated, but could still exist
]

GROWABLE_NETWORK_TYPE = 'drive'

PBI_CUSTOM_FILTER = [
'["highway"~"cycleway|living_street"]',
'["highway"~"path|pedestrian|footway"]["bicycle"~"designated|yes|permissive"]["access"!~"private"]',
'["cyclestreet"~"yes"]',
'["bicycle_road"~"yes"]',
'["cycleway"~"track"]',
'["cycleway:right"~"track|opposite_track"]', # opposite_track is deprecated, but could still exist
'["cycleway:left"~"track|opposite_track"]', # opposite_track is deprecated, but could still exist
'["cycleway:both"~"track|opposite_track"]', # opposite_track is deprecated, but could still exist
]

PBI_DICT = {
'highway': ['cycleway','living_street'],
'cyclestreet': ['yes'],
'bicycle_road': ['yes'],
'cycleway': ['track'],
'cycleway:right': ['track','opposite_track'],
'cycleway:left': ['track','opposite_track'],
'cycleway:both': ['track','opposite_track'],
}

PBI_DICT_SUB = {
'highway': [
['path','pedestrian','footway'],
{'bicycle': ['designated','yes','permissive']},
]
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified tests/test_data/frederiksberg_streetbike_network.gpkg
Binary file not shown.
Loading