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%20Modelled%20Demand%20Flows%0A%0A%20%20%20%20Standard%20betweenness%20treats%20every%20origin-destination%20pair%20as%20equally%20important.%20This%20recipe%20instead%20**models**%20the%20demand%20between%20weighted%20origins%20and%20destinations%2C%20then%20routes%20it%20across%20the%20network%20so%20each%20street%20accumulates%20the%20through-movement%20that%20passes%20along%20it.%20See%20the%20%5BOrigin-Destination%20Flows%20guide%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fguide%2Fflows)%20for%20the%20background.%0A%0A%20%20%20%20%5B%60betweenness_demand%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fmetrics%2Fnetworks%23betweenness_demand)%20uses%20a%20**singly%20(origin-)constrained**%20spatial%20interaction%20model.%20Each%20origin%20distributes%20its%20full%20weight%20%24W_o%24%20across%20the%20destinations%20it%20can%20reach%2C%20giving%20each%20a%20share%20proportional%20to%20its%20attractiveness%20%24W_d%24%20discounted%20by%20the%20network%20cost%20of%20getting%20there%3A%0A%0A%20%20%20%20%24%24W_%7Bod%7D%20%3D%20W_o%20%5Ccdot%20%5Cfrac%7BW_d%20%5C%2C%20f(c_%7Bod%7D)%7D%7B%5Csum_%7Bd'%7D%20W_%7Bd'%7D%20%5C%2C%20f(c_%7Bod'%7D)%7D%24%24%0A%0A%20%20%20%20where%20%24f%24%20is%20the%20deterrence%20function%20%60decay_fn%60%20and%20%24c_%7Bod%7D%24%20is%20the%20network%20distance.%20With%20an%20exponential%20decay%20this%20is%20the%20classic%20gravity%20model.%20Each%20allocated%20flow%20is%20routed%20along%20the%20shortest%20path%20and%20every%20street%20traversed%20accumulates%20it%2C%20so%20no%20explicit%20matrix%20is%20needed.%0A%0A%20%20%20%20%60CityNetwork.betweenness_demand%60%20runs%20the%20model%2C%20and%20%60to_geopandas%60%20projects%20the%20result%20onto%20the%20street%20segments%20for%20mapping.%20The%20bundled%20datasets%20and%20their%20attributions%20are%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%20from%20cityseer.network%20import%20CityNetwork%0A%0A%20%20%20%20return%20CityNetwork%2C%20gpd%2C%20plt%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%20Data%3A%20population%20origins%20and%20hospitality%20destinations%0A%0A%20%20%20%20We%20model%20home-to-amenity%20movement%20in%20central%20Madrid%3A%20**origins**%20are%20the%20Eurostat%20census%20grid%20(column%20%60T%60%2C%20total%20population%20per%20cell)%2C%20and%20**destinations**%20are%20hospitality%20premises%20(%60section_id%20%3D%3D%20%22I%22%60)%2C%20each%20weighted%20equally.%20Both%20are%20snapped%20to%20the%20nearest%20network%20node.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CityNetwork%2C%20gpd%2C%20mo)%3A%0A%20%20%20%20data_dir%20%3D%20(mo.notebook_dir()%20%2F%20%22..%22%20%2F%20%22..%22%20%2F%20%22data%22).resolve()%0A%20%20%20%20streets_gpd%20%3D%20gpd.read_file(data_dir%20%2F%20%22madrid_streets%22%20%2F%20%22street_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%203km%20study%20area%20around%20the%20centre%2C%20buffered%20a%20further%20800m%20(the%20analysis%20distance)%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(3000).iloc%5B0%5D%0A%20%20%20%20buffered_poly%20%3D%20centre.buffer(3800).iloc%5B0%5D%0A%20%20%20%20cn%20%3D%20CityNetwork.from_geopandas(streets_gpd%5Bstreets_gpd.intersects(buffered_poly)%5D%2C%20boundary%3Dstudy_poly)%0A%20%20%20%20print(f%22%7Bcn.node_count%7D%20street%20segments%2C%20%7Bint(cn.nodes_gdf.live.sum())%7D%20live%22)%0A%20%20%20%20return%20buffered_poly%2C%20cn%2C%20data_dir%0A%0A%0A%40app.cell%0Adef%20_(buffered_poly%2C%20data_dir%2C%20gpd)%3A%0A%20%20%20%20%23%20origins%3A%20census%20grid%20centroids%20with%20total%20population%0A%20%20%20%20census_gpd%20%3D%20gpd.read_file(data_dir%20%2F%20%22madrid_census%22%20%2F%20%22eu_stat_clipped.gpkg%22)%0A%20%20%20%20census_gpd%20%3D%20census_gpd.to_crs(25830)%0A%20%20%20%20origins_gpd%20%3D%20census_gpd%5B%5B%22T%22%2C%20%22geometry%22%5D%5D.copy()%0A%20%20%20%20origins_gpd%5B%22geometry%22%5D%20%3D%20origins_gpd.geometry.centroid%0A%20%20%20%20origins_gpd%20%3D%20origins_gpd%5Borigins_gpd.within(buffered_poly)%5D%0A%0A%20%20%20%20%23%20destinations%3A%20hospitality%20premises%2C%20weighted%20equally%0A%20%20%20%20premises_gpd%20%3D%20gpd.read_file(data_dir%20%2F%20%22madrid_premises%22%20%2F%20%22madrid_premises.gpkg%22)%0A%20%20%20%20dests_gpd%20%3D%20premises_gpd%5Bpremises_gpd%5B%22section_id%22%5D%20%3D%3D%20%22I%22%5D.copy()%0A%20%20%20%20dests_gpd%20%3D%20dests_gpd%5Bdests_gpd.within(buffered_poly)%5D%0A%20%20%20%20dests_gpd%5B%22weight%22%5D%20%3D%201.0%0A%20%20%20%20print(f%22%7Blen(origins_gpd)%7D%20origin%20cells%2C%20%7Blen(dests_gpd)%7D%20destination%20premises%22)%0A%20%20%20%20return%20dests_gpd%2C%20origins_gpd%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%20Computing%20demand-weighted%20betweenness%0A%0A%20%20%20%20The%20%60decay_fn%60%20expression%20is%20the%20deterrence%20function%20%24f%24.%20It%20uses%20the%20same%20variables%20as%20other%20cityseer%20expressions%3A%20%60c%60%20(network%20metres)%20and%20%60p%60%20(progress%20towards%20the%20threshold).%20%60%22exp(-0.002%20*%20c)%22%60%20is%20a%20gravity-style%20exponential%20suited%20to%20an%20800%20m%20walking%20threshold%3B%20see%20the%20%5Bdecay%20documentation%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fdecay)%20for%20alternatives.%20Setting%20%60closest_destination%3DTrue%60%20switches%20to%20all-or-nothing%20assignment%2C%20where%20each%20origin%20sends%20its%20full%20weight%20to%20the%20single%20nearest%20destination.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cn%2C%20dests_gpd%2C%20origins_gpd)%3A%0A%20%20%20%20cn.betweenness_demand(%0A%20%20%20%20%20%20%20%20origins_gdf%3Dorigins_gpd%2C%0A%20%20%20%20%20%20%20%20destinations_gdf%3Ddests_gpd%2C%0A%20%20%20%20%20%20%20%20origin_weight_col%3D%22T%22%2C%0A%20%20%20%20%20%20%20%20destination_weight_col%3D%22weight%22%2C%0A%20%20%20%20%20%20%20%20distances%3D%5B800%5D%2C%0A%20%20%20%20%20%20%20%20decay_fn%3D%22exp(-0.002%20*%20c)%22%2C%0A%20%20%20%20)%0A%20%20%20%20streets%20%3D%20cn.to_geopandas()%20%20%23%20cc_demand_800%20projected%20onto%20the%20street%20segments%0A%20%20%20%20print(streets%5B%22cc_demand_800%22%5D.describe())%0A%20%20%20%20return%20(streets%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%20Mapping%20the%20flows%0A%0A%20%20%20%20We%20map%20intensity%20by%20**line%20width%20and%20colour%20together**%3A%20a%20percentile%20rank%20spreads%20the%20heavily-skewed%20flow%20values%20across%20a%20hairline-to-bold%20width%20ramp%2C%20and%20the%20same%20rank%20drives%20an%20orange-red%20colour.%20Strong%20streets%20are%20drawn%20last%20so%20they%20sit%20on%20top.%20The%20map%20is%20legible%20without%20a%20colour%20bar.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plt%2C%20streets)%3A%0A%20%20%20%20live%20%3D%20streets%5Bstreets.live%5D.copy()%0A%20%20%20%20%23%20percentile%20rank%20spreads%20the%20skewed%20flow%20values%20across%20%5B0%2C%201%5D%0A%20%20%20%20live%5B%22rank%22%5D%20%3D%20live%5B%22cc_demand_800%22%5D.rank(pct%3DTrue)%0A%20%20%20%20live%20%3D%20live.sort_values(%22rank%22)%20%20%23%20strongest%20drawn%20last%2C%20on%20top%0A%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3D(7%2C%207))%0A%20%20%20%20live.plot(%0A%20%20%20%20%20%20%20%20ax%3Dax%2C%0A%20%20%20%20%20%20%20%20color%3Dplt.get_cmap(%22OrRd%22)(live%5B%22rank%22%5D)%2C%0A%20%20%20%20%20%20%20%20linewidth%3D0.15%20%2B%202.25%20*%20live%5B%22rank%22%5D%2C%0A%20%20%20%20)%0A%20%20%20%20ax.set_title(%22Modelled%20demand%20flows%20%C2%B7%20population%20to%20hospitality%20%C2%B7%20800%20m%22%2C%20loc%3D%22left%22)%0A%20%20%20%20ax.set_axis_off()%0A%20%20%20%20ax.set_aspect(%22equal%22)%0A%20%20%20%20fig.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%20Interpretation%20and%20variations%0A%0A%20%20%20%20High%20values%20mark%20streets%20carrying%20many%20short%20home-to-amenity%20trips%2C%20weighted%20by%20nearby%20population%20and%20the%20attractiveness%20of%20reachable%20destinations.%20Compared%20with%20unweighted%20betweenness%2C%20the%20demand-weighted%20flow%20concentrates%20where%20population%20and%20destinations%20actually%20interact.%0A%0A%20%20%20%20Variations%20to%20explore%3A%0A%0A%20%20%20%20-%20swap%20the%20destination%20set%20(parks%2C%20schools%2C%20transit%20stops)%2C%20or%20weight%20destinations%20by%20a%20capacity%20column%20such%20as%20floorspace%3B%0A%20%20%20%20-%20adjust%20the%20deterrence%3A%20a%20steeper%20decay%20(%60%22exp(-0.008%20*%20c)%22%60)%20localises%20flows%2C%20a%20flat%20function%20(%60%221%22%60)%20removes%20distance%20deterrence%20entirely%3B%0A%20%20%20%20-%20when%20you%20have%20observed%20flows%20rather%20than%20modelled%20demand%2C%20route%20an%20explicit%20matrix%20with%20%5B%60build_od_matrix%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fnetwork%23build_od_matrix)%20and%20%5B%60betweenness_od%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fnetwork%23betweenness_od)%3B%0A%20%20%20%20-%20compare%20against%20the%20uniform%20betweenness%20baseline%20from%20the%20%5Bmetric%20centrality%20recipe%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fexamples%2Fcentrality%2Fgpd-metric-centrality)%20to%20see%20how%20demand%20weighting%20shifts%20the%20pattern.%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
c0616b9aeb82077284e5649639a6eb17