# !pip install --upgrade cityseer
London Amenities
Install and update cityseer
if necessary.
Data and preparation
This examples calculates landuse accessibility to pubs and restaurants for London
For additional information on network preparation, see the corresponding notebook on graph cleaning.
This example will make use of OSM data downloaded from the OSM API for a 5000m radius around London Soho.
The landuse amenity data is taken from OpenStreetMap
via osmnx
.
from cityseer.tools import graphs, io
# download from OSM
= -0.13396079424572427, 51.51371088849723
lng, lat buffer = 5000
= 3500
plot_buffer # creates a WGS shapely polygon
= io.buffered_point_poly(lng, lat, buffer)
poly_wgs, _ = io.buffered_point_poly(lng, lat, buffer, projected=True)
poly_utm, _ = poly_utm.centroid.buffer(plot_buffer).bounds plot_bbox
Prepare the amenities GeoDataFrame.
# prepare data layer
from cityseer.metrics import layers
from osmnx import features
= features.features_from_place(
data_gdf "london", tags={"amenity": ["pub", "restaurant"]}
)= data_gdf.to_crs(
data_gdf 32630
# to match EPSG code for UTM zone 30N per network data
) = data_gdf.loc["node"]
data_gdf = data_gdf.reset_index(level=0, drop=True)
data_gdf = data_gdf.index.astype(str)
data_gdf.index # convert to the same UTM CRS as the network
= data_gdf[["amenity", "geometry"]]
data_gdf print(data_gdf.head())
amenity geometry
0 pub POINT (694282.233 5720372.951)
1 restaurant POINT (694353.653 5720508.222)
2 pub POINT (694189.282 5720228.905)
3 pub POINT (695475.171 5721895.793)
4 pub POINT (709733.415 5720007.168)
Fetch and simplify the street network. The street network will be decomposed for a higher resolution analysis.
# generate OSM graph from polygon
= io.osm_graph_from_poly(poly_wgs, simplify=True, iron_edges=True)
G_utm # decompose for higher resolution analysis
= graphs.nx_decompose(G_utm, 25)
G_decomp # prepare the data structures
= io.network_structure_from_nx(
nodes_gdf, _edges_gdf, network_structure =32629
G_decomp, crs )
INFO:cityseer.tools.io:Converting networkX graph from EPSG code 4326 to EPSG code 32630.
INFO:cityseer.tools.io:Processing node x, y coordinates.
100%|██████████| 158974/158974 [00:00<00:00, 448816.60it/s]
INFO:cityseer.tools.io:Processing edge geom coordinates, if present.
100%|██████████| 174746/174746 [00:00<00:00, 958047.36it/s]
INFO:cityseer.tools.graphs:Generating interpolated edge geometries.
100%|██████████| 174746/174746 [00:02<00:00, 69522.98it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 158974/158974 [00:20<00:00, 7661.52it/s]
INFO:cityseer.tools.graphs:Removing dangling nodes.
100%|██████████| 51042/51042 [00:00<00:00, 281125.34it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 44125/44125 [00:01<00:00, 41301.46it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 40426/40426 [00:00<00:00, 100086.85it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 40426/40426 [00:11<00:00, 3640.98it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 28513/28513 [00:00<00:00, 159276.17it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 43571/43571 [00:03<00:00, 11934.13it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 40139/40139 [00:00<00:00, 691922.69it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 28135/28135 [00:11<00:00, 2433.05it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 41835/41835 [00:00<00:00, 220327.91it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 29831/29831 [00:00<00:00, 100799.57it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 29831/29831 [00:05<00:00, 5392.59it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 25797/25797 [00:00<00:00, 59691.08it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 37634/37634 [00:01<00:00, 20032.03it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 24356/24356 [00:00<00:00, 132270.32it/s]
INFO:cityseer.tools.graphs:Ironing edges.
100%|██████████| 34781/34781 [00:10<00:00, 3355.07it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 1.
100%|██████████| 34781/34781 [00:00<00:00, 189489.49it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 34779/34779 [00:00<00:00, 719880.47it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 23771/23771 [00:06<00:00, 3438.18it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 34993/34993 [00:00<00:00, 55171.39it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 23985/23985 [00:00<00:00, 109008.42it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 23985/23985 [00:02<00:00, 10589.26it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 23634/23634 [00:00<00:00, 429373.54it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 34563/34563 [00:00<00:00, 117906.15it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 23568/23568 [00:00<00:00, 474414.40it/s]
INFO:cityseer.tools.graphs:Ironing edges.
100%|██████████| 34323/34323 [00:10<00:00, 3345.77it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 1.
100%|██████████| 34323/34323 [00:00<00:00, 193568.69it/s]
INFO:cityseer.tools.graphs:Decomposing graph to maximum edge lengths of 25.
100%|██████████| 34322/34322 [00:16<00:00, 2081.67it/s]
INFO:cityseer.tools.io:Preparing node and edge arrays from networkX graph.
100%|██████████| 90342/90342 [00:01<00:00, 83076.16it/s]
100%|██████████| 90342/90342 [00:13<00:00, 6772.80it/s]
# compute pub accessibility
= [100, 200, 400, 800]
distances = layers.compute_accessibilities(
nodes_gdf, pubs_data_gdf
data_gdf,="amenity",
landuse_column_label=["pub", "restaurant"],
accessibility_keys=nodes_gdf,
nodes_gdf=network_structure,
network_structure=distances,
distances )
INFO:cityseer.metrics.layers:Computing land-use accessibility for: pub, restaurant
100%|██████████| 90342/90342 [01:08<00:00, 1324.37it/s]
The output GeoDataFrame will contain new columns for each landuse and distance combination. For each of these the following will have been computed:
- A count of reachable instances of a given landuse (within the specified distance thresholds)
- Similarly, a distance-weighted count of reachable locations
- The distance to the nearest instance of a given landuse. Where there are no reachable landuses for a given node within the specified distance threshold, then a
np.inf
value will be returned.
import matplotlib.pyplot as plt
import numpy as np
from cityseer import rustalgos
from cityseer.tools import plot
= "#111"
bg_colour = "#ddd"
text_colour = 5
font_size = rustalgos.betas_from_distances(distances)
betas = rustalgos.avg_distances_for_betas(betas)
avg_dists for d, b, avg_d in zip(distances, betas, avg_dists):
= plt.subplots(2, 1, figsize=(8, 8), dpi=200, facecolor=bg_colour)
fig, axes
fig.suptitle(f"Accessibilities at avg. walking tolerance: {avg_d:.2f}m and max tolerance of {d}m",
=text_colour,
color=7,
fontsize
)
plot.plot_scatter(0],
axes[
network_structure.node_xs,
network_structure.node_ys,f"cc_pub_{d}_wt"],
nodes_gdf[=plot_bbox,
bbox_extents="viridis",
cmap_key=0.2,
s_min=2,
s_max=bg_colour,
face_colour
)0].set_title(
axes["Number of pubs within walking tolerance.",
=text_colour,
color=font_size,
fontsize
)
plot.plot_scatter(1],
axes[
network_structure.node_xs,
network_structure.node_ys,f"cc_restaurant_{d}_wt"],
nodes_gdf[=plot_bbox,
bbox_extents="plasma",
cmap_key=0.2,
s_min=2,
s_max=bg_colour,
face_colour
)1].set_title(
axes["Number of restaurants within walking tolerance.",
=text_colour,
color=font_size,
fontsize
)
fig.tight_layout() plt.show()
= plt.subplots(2, 1, figsize=(8, 8), dpi=200, facecolor=bg_colour)
fig, axes
fig.suptitle(f"Distances to nearest instance of specified landuse",
=text_colour,
color=7,
fontsize
)= nodes_gdf[f"cc_pub_nearest_max_{max(distances)}"]
dist_data = max(distances)
dist_data[np.isinf(dist_data)]
plot.plot_scatter(0],
axes[
network_structure.node_xs,
network_structure.node_ys,
dist_data,=plot_bbox,
bbox_extents="inferno_r",
cmap_key=0.2,
s_min=0.21,
s_max=bg_colour,
face_colour
)0].set_title("Distance to nearest pub.", color=text_colour, fontsize=font_size)
axes[= nodes_gdf[f"cc_restaurant_nearest_max_{max(distances)}"]
dist_data = max(distances)
dist_data[np.isinf(dist_data)]
plot.plot_scatter(1],
axes[
network_structure.node_xs,
network_structure.node_ys,
dist_data,=plot_bbox,
bbox_extents="viridis_r",
cmap_key=0.2,
s_min=0.21,
s_max=bg_colour,
face_colour
)1].set_title(
axes["Distance to nearest restaurant.", color=text_colour, fontsize=font_size
)
/var/folders/bs/sq3p18cs4dn1ws4kpmfyk_3c0000gn/T/ipykernel_66498/4173652336.py:8: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
dist_data[np.isinf(dist_data)] = max(distances)
/var/folders/bs/sq3p18cs4dn1ws4kpmfyk_3c0000gn/T/ipykernel_66498/4173652336.py:22: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
dist_data[np.isinf(dist_data)] = max(distances)
Text(0.5, 1.0, 'Distance to nearest restaurant.')