import%20marimo%0A%0A__generated_with%20%3D%20%220.23.13%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Adding%20GTFS%20Transport%20Data%20to%20Centrality%0A%0A%20%20%20%20This%20notebook%20demonstrates%20how%20to%20integrate%20GTFS%20(General%20Transit%20Feed%20Specification)%20public%20transport%20data%20into%20the%20street%20network%20before%20computing%20centrality%20metrics.%20By%20adding%20metro%20stops%20and%20segment%20travel%20times%20to%20the%20network%2C%20centrality%20calculations%20can%20account%20for%20the%20connectivity%20boost%20provided%20by%20public%20transport.%0A%0A%20%20%20%20The%20approach%20computes%20centralities%20twice%3A%20once%20without%20and%20once%20with%20the%20GTFS%20data%2C%20so%20that%20the%20effect%20of%20the%20metro%20network%20on%20centrality%20can%20be%20compared.%0A%0A%20%20%20%20The%20analysis%20uses%20a%204km%20study%20area%20around%20the%20city%20centre%20rather%20than%20the%20whole%20city%3A%20this%20is%20large%20enough%20to%20span%20multiple%20metro%20lines%20and%20interchanges%2C%20and%20since%20centralities%20are%20computed%20per%20node%20from%20local%20catchments%2C%20the%20district%20reproduces%20the%20city-wide%20values%20at%20far%20lower%20cost.%20The%20network%20is%20buffered%20by%20the%20largest%20analysis%20distance%20so%20nodes%20near%20the%20study%20edge%20keep%20their%20full%20catchments.%0A%0A%20%20%20%20This%20recipe%20uses%20the%20high-level%20%5B%60CityNetwork%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fnetwork)%20class%2C%20integrating%20the%20transit%20data%20through%20its%20%60add_gtfs%60%20method.%20Two%20instances%20are%20built%20from%20the%20same%20clipped%20streets%3A%20one%20street-only%20baseline%20and%20one%20with%20the%20metro%20added.%0A%0A%20%20%20%20%3E%20GTFS%20support%20is%20experimental%3A%20the%20GTFS%20integration%20is%20still%20in%20development%20and%20may%20change%20in%20future%20releases.%0A%0A%20%20%20%20The%20bundled%20datasets%20and%20their%20source%20attributions%20are%20documented%20on%20the%20%5Bdatasets%20page%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fexamples%2Fdatasets).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20geopandas%20as%20gpd%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20from%20cityseer.network%20import%20CityNetwork%0A%0A%20%20%20%20streets_gpd%20%3D%20gpd.read_file(%22..%2F..%2Fdata%2Fmadrid_streets%2Fstreet_network.gpkg%22)%0A%20%20%20%20streets_gpd%20%3D%20streets_gpd.explode(ignore_index%3DTrue)%0A%20%20%20%20%23%20the%20source%20data%20contains%20a%20small%20number%20of%20exact%20duplicate%20geometries%0A%20%20%20%20streets_gpd%20%3D%20streets_gpd.drop_duplicates(subset%3D%22geometry%22)%0A%20%20%20%20%23%204km%20study%20area%20around%20the%20city%20centre%2C%20with%20the%20network%20buffered%20a%20further%205000m%0A%20%20%20%20%23%20(the%20largest%20analysis%20distance)%20to%20prevent%20edge%20rolloff%0A%20%20%20%20centre%20%3D%20gpd.GeoSeries.from_xy(%5B440300%5D%2C%20%5B4474300%5D%2C%20crs%3Dstreets_gpd.crs)%0A%20%20%20%20study_poly%20%3D%20centre.buffer(4000).iloc%5B0%5D%0A%20%20%20%20buffered_poly%20%3D%20centre.buffer(9000).iloc%5B0%5D%0A%20%20%20%20streets_clip%20%3D%20streets_gpd%5Bstreets_gpd.intersects(buffered_poly)%5D%0A%20%20%20%20cn_base%20%3D%20CityNetwork.from_geopandas(streets_clip%2C%20boundary%3Dstudy_poly)%0A%20%20%20%20return%20CityNetwork%2C%20cn_base%2C%20gpd%2C%20pd%2C%20plt%2C%20streets_clip%2C%20study_poly%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compute%20baseline%20centralities%20(without%20GTFS%20data)%20and%20snapshot%20the%20results%20with%20%60to_geopandas%60%20for%20later%20comparison.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cn_base)%3A%0A%20%20%20%20distances%20%3D%20%5B1000%2C%202000%2C%205000%5D%0A%20%20%20%20cn_base.centrality_shortest(distances%3Ddistances)%0A%20%20%20%20base_gdf%20%3D%20cn_base.to_geopandas()%0A%20%20%20%20return%20base_gdf%2C%20distances%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Build%20a%20second%20network%20from%20the%20same%20clipped%20streets%20and%20use%20%5B%60add_gtfs%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fnetwork%23add_gtfs)%20to%20load%20the%20bundled%20metro%20GTFS%20data.%20This%20adds%20metro%20stops%20as%20nodes%20and%20segment%20travel%20times%20as%20edges%2C%20enabling%20the%20shortest-path%20algorithms%20to%20route%20through%20the%20metro%20network.%20The%20method%20takes%20the%20GTFS%20directory%20path%3B%20the%20network%20CRS%20is%20used%20automatically.%20For%20direct%20access%20to%20the%20derived%20stop%20and%20headway%20tables%2C%20the%20lower-level%20%5B%60io.add_transport_gtfs%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Ftools%2Fio%23add_transport_gtfs)%20function%20returns%20them%20explicitly.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CityNetwork%2C%20streets_clip%2C%20study_poly)%3A%0A%20%20%20%20cn_metro%20%3D%20CityNetwork.from_geopandas(streets_clip%2C%20boundary%3Dstudy_poly)%0A%20%20%20%20cn_metro.add_gtfs(%22..%2F..%2Fdata%2Fmadrid_gtfs%2Fmadrid_metro%22)%0A%20%20%20%20return%20(cn_metro%2C)%0A%0A%0A%40app.cell%0Adef%20_(cn_metro%2C%20gpd%2C%20pd)%3A%0A%20%20%20%20%23%20read%20the%20GTFS%20stop%20locations%20for%20plotting%0A%20%20%20%20stops%20%3D%20pd.read_csv(%22..%2F..%2Fdata%2Fmadrid_gtfs%2Fmadrid_metro%2Fstops.txt%22)%0A%20%20%20%20stops_gdf%20%3D%20gpd.GeoDataFrame(%0A%20%20%20%20%20%20%20%20stops%2C%0A%20%20%20%20%20%20%20%20geometry%3Dgpd.points_from_xy(stops%5B%22stop_lon%22%5D%2C%20stops%5B%22stop_lat%22%5D)%2C%0A%20%20%20%20%20%20%20%20crs%3D4326%2C%20%20%23%20GTFS%20coordinates%20are%20WGS84%0A%20%20%20%20)%0A%20%20%20%20stops_gdf%20%3D%20stops_gdf.to_crs(cn_metro.crs)%0A%20%20%20%20stops_gdf%0A%20%20%20%20return%20(stops_gdf%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Calculate%20centralities%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20centrality%20methods%20can%20now%20be%20computed%20on%20the%20metro-integrated%20network.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cn_metro%2C%20distances)%3A%0A%20%20%20%20cn_metro.centrality_shortest(distances%3Ddistances)%0A%20%20%20%20metro_gdf%20%3D%20cn_metro.to_geopandas()%0A%20%20%20%20return%20(metro_gdf%2C)%0A%0A%0A%40app.cell%0Adef%20_(metro_gdf)%3A%0A%20%20%20%20metro_gdf.columns%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Compare%20results%0A%0A%20%20%20%20Compute%20the%20difference%20between%20the%20metro-integrated%20and%20baseline%20centralities.%20The%20three-panel%20plot%20below%20shows%20harmonic%20closeness%20centrality%20without%20the%20metro%2C%20with%20the%20metro%2C%20and%20the%20difference.%20Metro%20stop%20locations%20are%20overlaid%20in%20red.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(base_gdf%2C%20metro_gdf)%3A%0A%20%20%20%20metro_gdf%5B%22cc_harmonic_1000_diff%22%5D%20%3D%20metro_gdf%5B%22cc_harmonic_1000%22%5D%20-%20base_gdf%5B%22cc_harmonic_1000%22%5D%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(base_gdf%2C%20metro_gdf%2C%20plt%2C%20stops_gdf)%3A%0A%20%20%20%20import%20matplotlib.colors%20as%20mcolors%0A%0A%20%20%20%20fig%2C%20axes%20%3D%20plt.subplots(3%2C%201%2C%20figsize%3D(6.5%2C%2019.5)%2C%20dpi%3D150)%0A%20%20%20%20%23%20sequential%20closeness%20panels%3A%20OrRd%2C%20edge%20width%20by%20percentile%20rank%2C%20no%20colour%20bar%0A%20%20%20%20for%20_ax%2C%20_gdf%2C%20_title%20in%20%5B%0A%20%20%20%20%20%20%20%20(axes%5B0%5D%2C%20base_gdf%2C%20%22Harmonic%20closeness%2C%201000%20m%20(without%20metro)%22)%2C%0A%20%20%20%20%20%20%20%20(axes%5B1%5D%2C%20metro_gdf%2C%20%22Harmonic%20closeness%2C%201000%20m%20(with%20metro)%22)%2C%0A%20%20%20%20%5D%3A%0A%20%20%20%20%20%20%20%20g%20%3D%20_gdf.copy()%0A%20%20%20%20%20%20%20%20if%20%22live%22%20in%20g.columns%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20g%20%3D%20g%5Bg.live%5D.copy()%0A%20%20%20%20%20%20%20%20g%5B%22_r%22%5D%20%3D%20g%5B%22cc_harmonic_1000%22%5D.rank(pct%3DTrue)%0A%20%20%20%20%20%20%20%20g%20%3D%20g.sort_values(%22_r%22)%0A%20%20%20%20%20%20%20%20g.plot(ax%3D_ax%2C%20color%3Dplt.get_cmap(%22OrRd%22)(g%5B%22_r%22%5D)%2C%20linewidth%3D0.15%20%2B%202.25%20*%20g%5B%22_r%22%5D)%0A%20%20%20%20%20%20%20%20_ax.set_title(_title%2C%20loc%3D%22left%22)%0A%20%20%20%20%20%20%20%20%23%20transit%20stops%20as%20neutral%20dark%20markers%3B%20red%20now%20signifies%20flow%20intensity%0A%20%20%20%20%20%20%20%20stops_gdf.plot(ax%3D_ax%2C%20color%3D%22%23333333%22%2C%20markersize%3D1)%0A%20%20%20%20%23%20difference%20panel%3A%20signed%2C%20diverging%2C%20colour%20bar%20kept%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20norm%20%3D%20mcolors.TwoSlopeNorm(vcenter%3D0)%0A%20%20%20%20%20%20%20%20metro_gdf.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20ax%3Daxes%5B2%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20column%3D%22cc_harmonic_1000_diff%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20cmap%3D%22coolwarm%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20norm%3Dnorm%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20legend%3DTrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20legend_kwds%3D%7B%22shrink%22%3A%200.6%7D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20except%20ValueError%3A%0A%20%20%20%20%20%20%20%20metro_gdf.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20ax%3Daxes%5B2%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20column%3D%22cc_harmonic_1000_diff%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20cmap%3D%22coolwarm%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20legend%3DTrue%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20legend_kwds%3D%7B%22shrink%22%3A%200.6%7D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20axes%5B2%5D.set_title(%22Difference%20due%20to%20metro%20stops%2C%201000%20m%22%2C%20loc%3D%22left%22)%0A%20%20%20%20for%20ax%20in%20axes%3A%0A%20%20%20%20%20%20%20%20ax.set_xlim(438500%2C%20438500%20%2B%203500)%0A%20%20%20%20%20%20%20%20ax.set_ylim(4472500%2C%204472500%20%2B%203500)%0A%20%20%20%20%20%20%20%20ax.set_axis_off()%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20fig%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Conclusion%0A%0A%20%20%20%20This%20notebook%20demonstrated%20how%20to%20integrate%20GTFS%20public%20transport%20data%20(the%20bundled%20metro%20feed)%20into%20the%20street%20network%20via%20%60CityNetwork.add_gtfs%60%20and%20compare%20centrality%20metrics%20with%20and%20without%20transit%20connectivity.%20By%20adding%20metro%20stops%20and%20segment%20travel%20times%2C%20the%20shortest-path%20algorithms%20can%20route%20through%20the%20transit%20network%2C%20revealing%20how%20public%20transport%20reshapes%20the%20pattern%20of%20closeness%20centrality%20across%20the%20district.%0A%0A%20%20%20%20**Next%20steps%3A**%20For%20GTFS%20with%20accessibility%20metrics%2C%20see%20%5BGTFS%20Accessibility%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fexamples%2Faccessibility%2Faccessibility-metro).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
dbd0f957ae29bef31229dfe488ab9dc0