from cityseer.metrics import layers
from cityseer.tools import graphs, io
import matplotlib.pyplot as plt
from osmnx import features
Landuse accessibility from OSM data
Calculate landuse accessibilities from OSM data.
This examples calculates landuse accessibility to pubs and restaurants for London using OpenStreetMap data.
To start, follow the same approach as shown in the network examples to create the network.
= -0.13396079424572427, 51.51371088849723
lng, lat buffer = 1500
= io.buffered_point_poly(lng, lat, buffer)
poly_wgs, epsg_code = io.osm_graph_from_poly(poly_wgs)
G = graphs.nx_to_dual(G)
G_dual = io.network_structure_from_nx(G_dual) nodes_gdf, _edges_gdf, network_structure
WARNING:cityseer.tools.io:Merging node 12450391665 into 25544116 due to identical x, y coords.
WARNING:cityseer.tools.io:Unable to parse level info: -`;-4
WARNING:cityseer.tools.io:Unable to parse level info: -`;-4
WARNING:cityseer.tools.io:Unable to parse level info: -`;-4
INFO:cityseer.tools.graphs:Generating interpolated edge geometries.
100%|██████████| 24093/24093 [00:00<00:00, 51290.61it/s]
INFO:cityseer.tools.io:Converting networkX graph to CRS code 32630.
INFO:cityseer.tools.io:Processing node x, y coordinates.
100%|██████████| 22376/22376 [00:00<00:00, 1132845.84it/s]
INFO:cityseer.tools.io:Processing edge geom coordinates, if present.
100%|██████████| 24093/24093 [00:01<00:00, 16990.13it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 22376/22376 [00:00<00:00, 23375.08it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 12155/12155 [00:00<00:00, 1573899.89it/s]
100%|██████████| 12155/12155 [00:01<00:00, 10470.36it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 10438/10438 [00:00<00:00, 64395.92it/s]
100%|██████████| 7386/7386 [00:00<00:00, 311266.70it/s]
INFO:cityseer.tools.graphs:Removing dangling nodes.
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 9004/9004 [00:00<00:00, 992940.87it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 6652/6652 [00:00<00:00, 1432705.67it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 5270/5270 [00:00<00:00, 167212.46it/s]
INFO:cityseer.tools.graphs:Squashing opposing nodes
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 6661/6661 [00:00<00:00, 162107.52it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 6612/6612 [00:00<00:00, 1768895.14it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 5270/5270 [00:00<00:00, 38747.78it/s]
INFO:cityseer.tools.graphs:Squashing opposing nodes
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 6668/6668 [00:00<00:00, 315822.02it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 6658/6658 [00:00<00:00, 1637581.42it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 5270/5270 [00:00<00:00, 152669.73it/s]
INFO:cityseer.tools.graphs:Squashing opposing nodes
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 6659/6659 [00:00<00:00, 65366.97it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 6659/6659 [00:00<00:00, 1380616.43it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 5270/5270 [00:00<00:00, 95310.29it/s]
INFO:cityseer.tools.graphs:Squashing opposing nodes
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 6660/6660 [00:00<00:00, 65674.52it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 5270/5270 [00:00<00:00, 60563.67it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 5270/5270 [00:00<00:00, 7267.67it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 6608/6608 [00:00<00:00, 264152.73it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 5230/5230 [00:00<00:00, 456614.35it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 5158/5158 [00:00<00:00, 58115.11it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 5158/5158 [00:06<00:00, 759.84it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 6059/6059 [00:00<00:00, 72499.40it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 4798/4798 [00:00<00:00, 501002.55it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 4762/4762 [00:00<00:00, 24404.47it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 4762/4762 [00:01<00:00, 3349.55it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 5851/5851 [00:00<00:00, 219079.73it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 4708/4708 [00:00<00:00, 1004925.36it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 4694/4694 [00:00<00:00, 60766.94it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 4694/4694 [00:02<00:00, 2234.60it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 5710/5710 [00:00<00:00, 325705.84it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 4613/4613 [00:00<00:00, 805643.09it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 4598/4598 [00:00<00:00, 60209.14it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 5687/5687 [00:00<00:00, 1604210.56it/s]
INFO:cityseer.tools.graphs:Snapping gapped endings.
100%|██████████| 4598/4598 [00:00<00:00, 18916.69it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 5888/5888 [00:00<00:00, 1774524.82it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 4598/4598 [00:00<00:00, 16279.57it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 6767/6767 [00:00<00:00, 352796.80it/s]
INFO:cityseer.tools.graphs:Removing dangling nodes.
100%|██████████| 5088/5088 [00:00<00:00, 713733.07it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 4288/4288 [00:00<00:00, 32345.17it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 4811/4811 [00:00<00:00, 1686767.24it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 3133/3133 [00:00<00:00, 11938.20it/s]
INFO:cityseer.tools.graphs:Squashing opposing nodes
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 4851/4851 [00:00<00:00, 100570.25it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 3133/3133 [00:00<00:00, 62204.75it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 3133/3133 [00:10<00:00, 298.19it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 3876/3876 [00:00<00:00, 46089.68it/s]
INFO:cityseer.tools.util:Creating edges STR tree.
100%|██████████| 3680/3680 [00:00<00:00, 1693552.64it/s]
INFO:cityseer.tools.graphs:Splitting opposing edges.
100%|██████████| 2473/2473 [00:00<00:00, 10257.75it/s]
INFO:cityseer.tools.graphs:Squashing opposing nodes
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 3690/3690 [00:00<00:00, 403277.44it/s]
INFO:cityseer.tools.util:Creating nodes STR tree
100%|██████████| 2473/2473 [00:00<00:00, 59565.93it/s]
INFO:cityseer.tools.graphs:Consolidating nodes.
100%|██████████| 2473/2473 [00:06<00:00, 392.17it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 25.
100%|██████████| 3165/3165 [00:00<00:00, 59942.17it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 2028/2028 [00:00<00:00, 105268.97it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 50.
100%|██████████| 2930/2930 [00:00<00:00, 150827.95it/s]
INFO:cityseer.tools.graphs:Ironing edges.
100%|██████████| 2911/2911 [00:00<00:00, 23159.06it/s]
INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 1.
100%|██████████| 2898/2898 [00:00<00:00, 355547.23it/s]
INFO:cityseer.tools.graphs:Removing dangling nodes.
100%|██████████| 1887/1887 [00:00<00:00, 901133.06it/s]
INFO:cityseer.tools.graphs:Removing filler nodes.
100%|██████████| 1869/1869 [00:00<00:00, 320135.34it/s]
INFO:cityseer.tools.graphs:Converting graph to dual.
INFO:cityseer.tools.graphs:Preparing dual nodes
100%|██████████| 2845/2845 [00:00<00:00, 44313.05it/s]
INFO:cityseer.tools.graphs:Preparing dual edges (splitting and welding geoms)
100%|██████████| 2845/2845 [00:04<00:00, 682.84it/s]
INFO:cityseer.tools.io:Preparing node and edge arrays from networkX graph.
100%|██████████| 2845/2845 [00:00<00:00, 19500.36it/s]
100%|██████████| 2845/2845 [00:00<00:00, 4317.47it/s]
Prepare the amenities GeoDataFrame by downloading the data from OpenStreetMap. The osmnx
features_from_polygon
works well for this purpose. In this instance, we are specifically targeting features that are labelled as an amenity
type of either pub
or restaurant
. You can use the same idea to extract other features or land use types.
It is important to convert the derivative GeoDataFrame to the same CRS as the network.
= features.features_from_polygon(
data_gdf ={"amenity": ["pub", "restaurant"]}
poly_wgs, tags
)= data_gdf.to_crs(nodes_gdf.crs)
data_gdf data_gdf
geometry | addr:city | addr:country | addr:housenumber | addr:postcode | addr:street | amenity | check_date | contact:email | contact:phone | ... | access:conditional | note:access | name:gsw | note:name:en | note:name:zh | website:en | serving_system:buffet | building:use | floor | type | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
element | id | |||||||||||||||||||||
node | 21593236 | POINT (699869.619 5711280.678) | London | GB | 12 | WC2A 3HP | Gate Street | pub | 2023-04-18 | info@theshiptavern.co.uk | +44 20 7405 1992 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
25471087 | POINT (699749.16 5710787.29) | London | NaN | 39 | NaN | Bow Street | pub | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
25475914 | POINT (699606.322 5711699.239) | NaN | NaN | NaN | NaN | NaN | pub | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
25746185 | POINT (700308.36 5710758.42) | London | NaN | NaN | WC2R 3LD | NaN | pub | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
26699558 | POINT (699071.215 5711168.671) | NaN | NaN | NaN | NaN | NaN | pub | 2024-09-10 | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
way | 995954627 | POLYGON ((699270.101 5710540.727, 699277.571 5... | London | NaN | 24 | WC2H 0HX | Charing Cross Road | restaurant | 2024-10-07 | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1016903732 | POLYGON ((699039.571 5710804.095, 699036.453 5... | NaN | NaN | NaN | NaN | NaN | restaurant | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
1060462032 | POLYGON ((700144.125 5710144.723, 700142.327 5... | London | NaN | NaN | SE1 8XX | Belvedere Road | restaurant | NaN | NaN | NaN | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | retail | NaN | NaN | |
1072559143 | POLYGON ((699383.906 5710631.863, 699382.376 5... | London | NaN | 17 | WC2E 9AX | Garrick Street | restaurant | NaN | NaN | +44 20 3675 0930 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | |
1194233515 | POLYGON ((700109.233 5711375.71, 700111.379 57... | London | NaN | 283-288 | WC1V 7HP | High Holborn | pub | 2023-08-03 | NaN | +44 20 7242 5669 | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | Ground | NaN |
1431 rows × 253 columns
Some preparatory data cleaning is typically necessary. This example extracts the particular rows and columns of interest for the subsequent steps of analysis.
# extract nodes
= data_gdf.loc["node"]
data_gdf # reset index
= data_gdf.reset_index(level=0, drop=True)
data_gdf # extract relevant columns
= data_gdf[["amenity", "geometry"]]
data_gdf data_gdf.head()
amenity | geometry | |
---|---|---|
0 | pub | POINT (699869.619 5711280.678) |
1 | pub | POINT (699749.16 5710787.29) |
2 | pub | POINT (699606.322 5711699.239) |
3 | pub | POINT (700308.36 5710758.42) |
4 | pub | POINT (699071.215 5711168.671) |
Once the land use and network data has been prepared, use the layers.compute_accessibilities
method to compute accessibilities to land uses. The landuse_column_label
and the target accessibility keys should correspond to the data in the input GeoDataFrame.
# compute pub accessibility
= [100, 200, 400, 800]
distances = layers.compute_accessibilities(
nodes_gdf, 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
INFO:cityseer.metrics.layers:Assigning data to network.
100%|██████████| 701/701 [00:00<00:00, 122370.96it/s]
100%|██████████| 2845/2845 [00:01<00:00, 2793.31it/s]
INFO:cityseer.config:Metrics computed for:
INFO:cityseer.config:Distance: 100m, Beta: 0.04, Walking Time: 1.25 minutes.
INFO:cityseer.config:Distance: 200m, Beta: 0.02, Walking Time: 2.5 minutes.
INFO:cityseer.config:Distance: 400m, Beta: 0.01, Walking Time: 5.0 minutes.
INFO:cityseer.config:Distance: 800m, Beta: 0.005, Walking Time: 10.0 minutes.
The output columns are named cc_{key}_{distance}_{type}
. Where the keys will correspond to the input accessibility keys and the distances will correspond to the input distances. The types will correspond to either non-weighted nw
or distance weighted wt
count, or else the distance to the nearest instance of a given landuse.
Standard GeoPandas functionality can be used to explore, visualise, or save the results.
nodes_gdf.columns
Index(['ns_node_idx', 'x', 'y', 'live', 'weight', 'primal_edge',
'primal_edge_node_a', 'primal_edge_node_b', 'primal_edge_idx',
'dual_node', 'cc_pub_100_nw', 'cc_pub_100_wt', 'cc_pub_200_nw',
'cc_pub_200_wt', 'cc_pub_400_nw', 'cc_pub_400_wt', 'cc_pub_800_nw',
'cc_pub_800_wt', 'cc_pub_nearest_max_800', 'cc_restaurant_100_nw',
'cc_restaurant_100_wt', 'cc_restaurant_200_nw', 'cc_restaurant_200_wt',
'cc_restaurant_400_nw', 'cc_restaurant_400_wt', 'cc_restaurant_800_nw',
'cc_restaurant_800_wt', 'cc_restaurant_nearest_max_800'],
dtype='object')
= plt.subplots(1, 1, figsize=(8, 8), facecolor="#1d1d1d")
fig, ax
nodes_gdf.plot(="cc_restaurant_400_wt",
column="magma",
cmap=False,
legend=ax,
ax
)"amenity"] == "restaurant"].plot(
data_gdf[data_gdf[=2,
markersize=None,
edgecolor="white",
color=False,
legend=ax,
ax
)False) ax.axis(
(np.float64(697035.8123208123),
np.float64(700647.6928482385),
np.float64(5709134.052621752),
np.float64(5712638.694212982))
= plt.subplots(1, 1, figsize=(8, 8), facecolor="#1d1d1d")
fig, ax
nodes_gdf.plot(="cc_pub_nearest_max_800",
column="viridis_r",
cmap=False,
legend=ax,
ax
)"amenity"] == "pub"].plot(
data_gdf[data_gdf[=2,
markersize=None,
edgecolor="white",
color=False,
legend=ax,
ax
)False) ax.axis(
(np.float64(697035.8123208123),
np.float64(700647.6928482385),
np.float64(5709196.42405734),
np.float64(5712635.724144622))