import geopandas as gpd
import matplotlib.pyplot as plt
from cityseer.metrics import layers
from cityseer.tools import graphs, iostreets_gpd = gpd.read_file("data/madrid_streets/street_network.gpkg")
streets_gpd = streets_gpd.explode(reset_index=True)
G = io.nx_from_generic_geopandas(streets_gpd)
G_dual = graphs.nx_to_dual(G)
nodes_gdf, _edges_gdf, network_structure = io.network_structure_from_nx(G_dual)INFO:cityseer.tools.graphs:Merging parallel edges within buffer of 1.
INFO:cityseer.tools.graphs:Converting graph to dual.
INFO:cityseer.tools.graphs:Preparing dual nodes
INFO:cityseer.tools.graphs:Preparing dual edges (splitting and welding geoms)
INFO:cityseer.tools.io:Preparing node and edge arrays from networkX graph.
INFO:cityseer.graph:Edge R-tree built successfully with 104026 items.
prems_gpd = gpd.read_file("data/madrid_premises/madrid_premises.gpkg")
prems_gpd.head()| index | local_id | local_distr_id | local_distr_desc | local_neighb_id | local_neighb_desc | local_neighb_code | local_census_section_id | local_census_section_desc | section_id | section_desc | division_id | division_desc | epigraph_id | epigraph_desc | easting | northing | geometry | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 10003324 | 1 | CENTRO | 105 | UNIVERSIDAD | 5 | 1091 | 91 | I | hospitality | 56 | food_bev | 561001 | RESTAURANTE | 440181.6 | 4475586.5 | POINT (440181.6 4475586.5) |
| 1 | 1 | 10003330 | 1 | CENTRO | 105 | UNIVERSIDAD | 5 | 1115 | 115 | R | art_rec_entert | 90 | creat_entert | 900003 | TEATRO Y ACTIVIDADES ESCENICAS REALIZADAS EN D... | 440000.6 | 4474761.5 | POINT (440000.6 4474761.5) |
| 2 | 2 | 10003356 | 1 | CENTRO | 104 | JUSTICIA | 4 | 1074 | 74 | I | hospitality | 56 | food_bev | 561004 | BAR RESTAURANTE | 440618.6 | 4474692.5 | POINT (440618.6 4474692.5) |
| 3 | 3 | 10003364 | 1 | CENTRO | 104 | JUSTICIA | 4 | 1075 | 75 | G | wholesale_retail_motor | 47 | retail | 472401 | COMERCIO AL POR MENOR DE PAN Y PRODUCTOS DE PA... | 440666.6 | 4474909.5 | POINT (440666.6 4474909.5) |
| 4 | 4 | 10003367 | 1 | CENTRO | 106 | SOL | 6 | 1119 | 119 | G | wholesale_retail_motor | 47 | retail | 477701 | COMERCIO AL POR MENOR DE JOYAS, RELOJERIA Y BI... | 440378.6 | 4474380.5 | POINT (440378.6 4474380.5) |
# compute accessibility
distances = [800, 1600]
nodes_gdf, prems_gpd = layers.compute_accessibilities(
prems_gpd,
landuse_column_label="division_desc",
accessibility_keys=["retail"],
nodes_gdf=nodes_gdf,
network_structure=network_structure,
distances=distances,
)
# keep a copy before adding GTFS for comparison
for col in nodes_gdf.columns:
if col.startswith("cc_"):
nodes_gdf[f"{col}_not"] = nodes_gdf[col]INFO:cityseer.metrics.layers:Computing land-use accessibility for: retail
INFO:cityseer.metrics.layers:Assigning data to network.
INFO:cityseer.data:Assigning 117358 data entries to network nodes (max_dist: 100).
INFO:cityseer.data:Finished assigning data. 198773 assignments added to 24576 nodes.
INFO:cityseer.config:Metrics computed for:
INFO:cityseer.config:Distance: 800m, Beta: 0.005, Walking Time: 10.0 minutes.
INFO:cityseer.config:Distance: 1600m, Beta: 0.0025, Walking Time: 20.0 minutes.
network_structure, stops, avg_stop_pairs = io.add_transport_gtfs(
"data/madrid_gtfs/madrid_metro",
network_structure,
nodes_gdf.crs,
)INFO:cityseer.tools.io:Loading GTFS data from data/madrid_gtfs/madrid_metro
INFO:cityseer.tools.io:Loaded 1272 stops and 2360 stop times
INFO:cityseer.tools.io:Adding GTFS stops to network nodes (with street linking logic).
INFO:cityseer.tools.io:Generating segment durations between stops.
INFO:cityseer.tools.io:Adding GTFS segments to network edges.
INFO:cityseer.graph:Edge R-tree built successfully with 104026 items.
# convert stops to geopandas
stops_gdf = gpd.GeoDataFrame(
stops,
geometry=gpd.points_from_xy(stops["stop_lon"], stops["stop_lat"]),
crs=4326, # Adjust the CRS to suit your data if needed
)
stops_gdf = stops_gdf.to_crs(streets_gpd.crs.to_epsg())
stops_gdf| stop_id | stop_code | stop_name | stop_desc | stop_lat | stop_lon | zone_id | stop_url | location_type | parent_station | stop_timezone | wheelchair_boarding | avg_wait_time | geometry | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | gtfs-data/madrid_gtfs/madrid_metro-par_4_1 | 1 | PLAZA DE CASTILLA | Paseo de la Castellana 189 | 40.46690 | -3.68917 | A | http://www.crtm.es | 0 | est_90_21 | NaN | 0 | 430.357143 | POINT (441575.052 4479808.7) |
| 1 | gtfs-data/madrid_gtfs/madrid_metro-acc_4_1_1 | 1 | Plaza de Castilla | Paseo de la Castellana 189 | 40.46682 | -3.68918 | NaN | http://www.crtm.es | 2 | est_90_21 | NaN | 0 | NaN | POINT (441574.135 4479799.826) |
| 2 | gtfs-data/madrid_gtfs/madrid_metro-acc_4_1_1040 | 1 | Ascensor | Plaza de Castilla 9 | 40.46555 | -3.68877 | NaN | http://www.crtm.es | 2 | est_90_21 | NaN | 0 | NaN | POINT (441607.793 4479658.584) |
| 3 | gtfs-data/madrid_gtfs/madrid_metro-acc_4_1_1043 | 1 | Intercambiador Superficie | Paseo de la Castellana 191 B | 40.46728 | -3.68915 | NaN | http://www.crtm.es | 2 | est_90_21 | NaN | 0 | NaN | POINT (441577.077 4479850.867) |
| 4 | gtfs-data/madrid_gtfs/madrid_metro-acc_4_1_1044 | 1 | Ascensor | Paseo de la Castellana 189 | 40.46702 | -3.68918 | NaN | http://www.crtm.es | 2 | est_90_21 | NaN | 0 | NaN | POINT (441574.308 4479822.026) |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1267 | gtfs-data/madrid_gtfs/madrid_metro-est_4_234 | 234 | HOSPITAL SEVERO OCHOA | Avda Orellana 3 | 40.32177 | -3.76797 | B1 | http://www.crtm.es | 1 | NaN | Europe/Madrid | 2 | NaN | POINT (434754.721 4463754.488) |
| 1268 | gtfs-data/madrid_gtfs/madrid_metro-est_4_235 | 235 | LEGANES CENTRAL | Calle Virgen del Camino 1 | 40.32899 | -3.77154 | B1 | http://www.crtm.es | 1 | NaN | Europe/Madrid | 2 | NaN | POINT (434458.401 4464558.532) |
| 1269 | gtfs-data/madrid_gtfs/madrid_metro-est_4_236 | 236 | SAN NICASIO | Avda Mar Mediterráneo SN | 40.33616 | -3.77587 | B1 | http://www.crtm.es | 1 | NaN | Europe/Madrid | 2 | NaN | POINT (434097.544 4465357.604) |
| 1270 | gtfs-data/madrid_gtfs/madrid_metro-par_4_237 | 237 | OPERA | Plaza de Isabel II 9 | 40.41809 | -3.70928 | A | http://www.crtm.es | 0 | est_4_36 | NaN | 0 | 13.214286 | POINT (439826.687 4474404.289) |
| 1271 | gtfs-data/madrid_gtfs/madrid_metro-par_4_238 | 238 | PRINCIPE PIO | Paseo de la Florida 2 | 40.42099 | -3.72033 | A | http://www.crtm.es | 0 | est_90_18 | NaN | 1 | 13.214286 | POINT (438891.852 4474733.771) |
1272 rows × 14 columns
nodes_gdf, prems_gpd = layers.compute_accessibilities(
prems_gpd,
landuse_column_label="division_desc",
accessibility_keys=["retail"],
nodes_gdf=nodes_gdf,
network_structure=network_structure,
distances=distances,
)INFO:cityseer.metrics.layers:Computing land-use accessibility for: retail
INFO:cityseer.metrics.layers:Assigning data to network.
INFO:cityseer.data:Assigning 117358 data entries to network nodes (max_dist: 100).
INFO:cityseer.data:Finished assigning data. 198773 assignments added to 24576 nodes.
INFO:cityseer.config:Metrics computed for:
INFO:cityseer.config:Distance: 800m, Beta: 0.005, Walking Time: 10.0 minutes.
INFO:cityseer.config:Distance: 1600m, Beta: 0.0025, Walking Time: 20.0 minutes.
nodes_gdf.columnsIndex(['ns_node_idx', 'x', 'y', 'live', 'weight', 'primal_edge',
'primal_edge_node_a', 'primal_edge_node_b', 'primal_edge_idx',
'dual_node', 'cc_retail_800_nw', 'cc_retail_800_wt',
'cc_retail_1600_nw', 'cc_retail_1600_wt', 'cc_retail_nearest_max_1600',
'cc_retail_800_nw_not', 'cc_retail_800_wt_not', 'cc_retail_1600_nw_not',
'cc_retail_1600_wt_not', 'cc_retail_nearest_max_1600_not'],
dtype='object')
nodes_gdf["cc_retail_800_wt_diff"] = (
nodes_gdf["cc_retail_800_wt"] - nodes_gdf["cc_retail_800_wt_not"]
)fig, axes = plt.subplots(3, 1, figsize=(8, 20), facecolor="#1d1d1d")
nodes_gdf.plot(
column="cc_retail_800_wt_not",
cmap="magma",
legend=True,
ax=axes[0],
vmax=450,
)
axes[0].set_title(
"Retail Accessibility without Metro Stops",
fontsize=8,
color="#ddd",
)
stops_gdf.plot(ax=axes[0], color="red", markersize=1)
nodes_gdf.plot(
column="cc_retail_800_wt",
cmap="magma",
legend=True,
ax=axes[1],
vmax=450,
)
axes[1].set_title(
"Retail Accessibility with Metro Stops",
fontsize=8,
color="#ddd",
)
stops_gdf.plot(ax=axes[1], color="red", markersize=1)
nodes_gdf.plot(
column="cc_retail_800_wt_diff",
cmap="viridis",
legend=True,
ax=axes[2],
)
axes[2].set_title(
"Retail Accessibility with difference due to Metro Stops",
fontsize=8,
color="#ddd",
)
for ax in axes:
prems_gpd[prems_gpd["division_desc"] == "retail"].plot(
markersize=1,
edgecolor=None,
color="white",
legend=False,
ax=ax,
)
ax.set_xlim(438500, 438500 + 3500)
ax.set_ylim(4472500, 4472500 + 3500)
ax.axis(False)
plt.tight_layout()