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%20London%20Cycling%20Flows%3A%20Observed%20and%20Modelled%0A%0A%20%20%20%20This%20recipe%20maps%20cycle-commute%20through-movement%20across%20central%20London%20two%20ways%2C%20from%20observed%20trips%20and%20from%20modelled%20trips%2C%20using%20only%20open%20data.%0A%0A%20%20%20%20-%20**Observed**%3A%20the%20%5BPropensity%20to%20Cycle%20Tool%5D(https%3A%2F%2Fwww.pct.bike%2F)%20(PCT)%20gives%20journey-to-work%20bicycle%20counts%20between%20MSOA%20zones.%20%5B%60betweenness_od%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fnetwork%23betweenness-od)%20routes%20that%20explicit%20matrix.%0A%20%20%20%20-%20**Modelled**%3A%20%5B%60betweenness_demand%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fnetwork%23betweenness-demand)%20allocates%20trips%20from%20weighted%20origins%20to%20weighted%20destinations%20with%20a%20singly-constrained%20gravity%20model%2C%20then%20routes%20them.%20No%20matrix%20is%20supplied.%0A%0A%20%20%20%20Modelling%20the%20flows%20lets%20us%20check%20the%20model%20against%20the%20observed%20pattern%2C%20and%20calibrate%20the%20two%20decay%20levers%20separately%3A%20the%20distance%20decay%20%60beta%60%20shapes%20destination%20choice%2C%20and%20%60participation%60%20scales%20how%20many%20trips%20are%20generated.%0A%0A%20%20%20%20The%20observed%20cycle%20flows%20come%20from%20the%20**Propensity%20to%20Cycle%20Tool%20(PCT)**%2C%20developed%20by%20**Robin%20Lovelace**%20and%20colleagues%20(Lovelace%20et%20al.%2C%202017)%2C%20which%20we%20gratefully%20acknowledge%20as%20the%20source%20of%20the%20origin-destination%20cycling%20data%20used%20here.%0A%0A%20%20%20%20Data%20and%20licences%3A%20street%20network%20%C2%A9%20%5BOpenStreetMap%5D(https%3A%2F%2Fwww.openstreetmap.org%2Fcopyright)%20contributors%20(ODbL)%3B%20cycle-commute%20origin-destination%20flows%20from%20the%20%5BPCT%5D(https%3A%2F%2Fwww.pct.bike%2F)%20(%5Bpct-data%5D(https%3A%2F%2Fgithub.com%2FRobinlovelace%2Fpct-data))%2C%20derived%20from%20the%202011%20Census%20travel-to-work%20table%20(WU03EW)%2C%20Open%20Government%20Licence%3B%20MSOA%20zone%20boundaries%20%C2%A9%20ONS%2C%20Open%20Government%20Licence.%0A%0A%20%20%20%20%3E%20Lovelace%2C%20R.%2C%20Goodman%2C%20A.%2C%20Aldred%2C%20R.%2C%20Berkoff%2C%20N.%2C%20Abbas%2C%20A.%20and%20Woodcock%2C%20J.%20(2017).%20The%20Propensity%20to%20Cycle%20Tool%3A%20An%20open%20source%20online%20system%20for%20sustainable%20transport%20planning.%20*Journal%20of%20Transport%20and%20Land%20Use*%2C%2010(1)%2C%20505-528.%20%5Bdoi%3A10.5198%2Fjtlu.2016.862%5D(https%3A%2F%2Fdoi.org%2F10.5198%2Fjtlu.2016.862)%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%20tempfile%0A%20%20%20%20import%20urllib.request%0A%20%20%20%20from%20pathlib%20import%20Path%0A%0A%20%20%20%20import%20geopandas%20as%20gpd%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20from%20cityseer.network%20import%20CityNetwork%0A%20%20%20%20from%20cityseer.tools%20import%20io%0A%20%20%20%20from%20matplotlib%20import%20pyplot%20as%20plt%0A%20%20%20%20from%20scipy.stats%20import%20spearmanr%0A%0A%20%20%20%20return%20CityNetwork%2C%20Path%2C%20gpd%2C%20io%2C%20np%2C%20pd%2C%20plt%2C%20spearmanr%2C%20tempfile%2C%20urllib%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%20Network%20and%20data%0A%0A%20%20%20%20The%20network%20is%20downloaded%20from%20OpenStreetMap%20for%20a%204%20km%20study%20area%20around%20the%20centre.%20%60from_osm%60%20fetches%2C%20simplifies%2C%20and%20builds%20the%20dual%20graph%20in%20one%20call%2C%20including%20cycleways%20by%20default.%0A%0A%20%20%20%20The%20PCT%20files%20are%20downloaded%20once%20and%20cached%3A%20%60l.csv%60%20has%20one%20row%20per%20(residence%20MSOA%2C%20workplace%20MSOA)%20pair%20with%20a%20commuter%20count%20per%20mode%3B%20%60z.geojson%60%20holds%20the%20MSOA%20boundaries.%20We%20keep%20the%20zones%20whose%20centroids%20fall%20inside%20the%20study%20area%20and%20the%20pairs%20between%20them.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CityNetwork%2C%20Path%2C%20gpd%2C%20io%2C%20pd%2C%20tempfile%2C%20urllib)%3A%0A%20%20%20%20%23%20central-London%20study%20area%3B%20build%20the%20OSM%20network%20in%20the%20British%20National%20Grid.%0A%20%20%20%20%23%20green_footways%20%2F%20green_service_roads%20keep%20the%20paths%20through%20parks%20(Regent's%20Park%2C%0A%20%20%20%20%23%20Hyde%20Park)%2C%20which%20cyclists%20use%20but%20which%20a%20roads-only%20network%20omits%2C%20forcing%20detours.%0A%20%20%20%20%23%20cache_path%20caches%20the%20Overpass%20response%20to%20a%20temp%20file%20so%20re-runs%20reuse%20it%20rather%20than%0A%20%20%20%20%23%20re-querying%20(Overpass%20rate-limits%20by%20request%20volume%2C%20so%20caching%20is%20what%20helps%3B%20if%20a%20first%0A%20%20%20%20%23%20run%20hits%20a%20busy%20Overpass%2C%20simply%20retry).%0A%20%20%20%20cache%20%3D%20Path(tempfile.gettempdir())%20%2F%20%22cityseer_pct_london%22%0A%20%20%20%20cache.mkdir(exist_ok%3DTrue)%0A%20%20%20%20lng%2C%20lat%20%3D%20-0.12%2C%2051.51%0A%20%20%20%20poly_wgs%2C%20_epsg%20%3D%20io.buffered_point_poly(lng%2C%20lat%2C%204000)%0A%20%20%20%20cn%20%3D%20CityNetwork.from_osm(%0A%20%20%20%20%20%20%20%20poly_wgs%2C%0A%20%20%20%20%20%20%20%20to_crs_code%3D27700%2C%0A%20%20%20%20%20%20%20%20green_footways%3DTrue%2C%0A%20%20%20%20%20%20%20%20green_service_roads%3DTrue%2C%0A%20%20%20%20%20%20%20%20cache_path%3Dstr(cache%20%2F%20%22osm_network.json%22)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20PCT%20London%20cycle-commute%20data%2C%20downloaded%20once%20to%20the%20same%20temp%20cache%0A%20%20%20%20base%20%3D%20%22https%3A%2F%2Fraw.githubusercontent.com%2FRobinlovelace%2Fpct-data%2Fmaster%2Flondon%22%0A%20%20%20%20for%20_name%20in%20(%22l.csv%22%2C%20%22z.geojson%22)%3A%0A%20%20%20%20%20%20%20%20if%20not%20(cache%20%2F%20_name).exists()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20urllib.request.urlretrieve(f%22%7Bbase%7D%2F%7B_name%7D%22%2C%20cache%20%2F%20_name)%0A%20%20%20%20l_df%20%3D%20pd.read_csv(cache%20%2F%20%22l.csv%22)%0A%20%20%20%20zones%20%3D%20gpd.read_file(cache%20%2F%20%22z.geojson%22).to_crs(27700)%0A%0A%20%20%20%20%23%20zones%20and%20pairs%20inside%20the%20study%20area%0A%20%20%20%20centre%20%3D%20gpd.GeoSeries.from_xy(%5Blng%5D%2C%20%5Blat%5D%2C%20crs%3D4326).to_crs(27700).iloc%5B0%5D%0A%20%20%20%20study%20%3D%20centre.buffer(4500)%0A%20%20%20%20zones_in%20%3D%20zones%5Bzones.geometry.centroid.within(study)%5D.reset_index(drop%3DTrue)%0A%20%20%20%20keep%20%3D%20set(zones_in%5B%22geo_code%22%5D)%0A%20%20%20%20sub%20%3D%20l_df%5Bl_df%5B%22msoa1%22%5D.isin(keep)%20%26%20l_df%5B%22msoa2%22%5D.isin(keep)%5D%0A%20%20%20%20print(f%22%7Blen(zones_in)%7D%20MSOAs%2C%20%7Blen(sub)%7D%20OD%20pairs%2C%20%7Bsub%5B'bicycle'%5D.sum()%3A.0f%7D%20cycle%20commutes%22)%0A%20%20%20%20return%20cn%2C%20sub%2C%20zones_in%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%20Observed%20flows%20with%20%60betweenness_od%60%0A%0A%20%20%20%20%60build_od_matrix%60%20turns%20the%20flow%20table%20and%20the%20zones%20into%20a%20sparse%20matrix%2C%20assigning%20each%20MSOA%20centroid%20to%20its%20nearest%20network%20node.%20%60betweenness_od%60%20then%20routes%20each%20pair's%20%60bicycle%60%20count%20along%20the%20shortest%20path%20between%20its%20zones%20and%20accumulates%20the%20flow%20on%20the%20streets%20in%20between.%0A%0A%20%20%20%20The%20distance%20threshold%20is%20set%20to%2020%20km%2C%20beyond%20the%20study%20extent%2C%20so%20that%20in%20the%20modelled%20version%20below%20the%20decay%20rather%20than%20a%20hard%20cutoff%20governs%20destination%20choice.%20%60tolerance%60%20spreads%20flow%20across%20near-equal%20shortest%20paths%2C%20which%20avoids%20the%20artificial%20striping%20that%20a%20single%20arbitrary%20geodesic%20produces%20on%20a%20grid.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cn%2C%20sub%2C%20zones_in)%3A%0A%20%20%20%20D%20%3D%2020000%0A%20%20%20%20matrix%20%3D%20cn.build_od_matrix(%0A%20%20%20%20%20%20%20%20sub%5B%5B%22msoa1%22%2C%20%22msoa2%22%2C%20%22bicycle%22%5D%5D%2C%0A%20%20%20%20%20%20%20%20zones_in%2C%0A%20%20%20%20%20%20%20%20origin_col%3D%22msoa1%22%2C%0A%20%20%20%20%20%20%20%20destination_col%3D%22msoa2%22%2C%0A%20%20%20%20%20%20%20%20weight_col%3D%22bicycle%22%2C%0A%20%20%20%20%20%20%20%20zone_id_col%3D%22geo_code%22%2C%0A%20%20%20%20%20%20%20%20max_netw_assign_dist%3D1500.0%2C%0A%20%20%20%20)%0A%20%20%20%20cn.centrality_shortest(distances%3D%5BD%5D%2C%20closeness%3D%7B%7D%2C%20betweenness%3DNone%2C%20cycles%3DFalse)%0A%20%20%20%20std%20%3D%20cn.to_geopandas()%0A%20%20%20%20cn.betweenness_od(matrix%2C%20distances%3D%5BD%5D%2C%20tolerance%3D10.0)%0A%20%20%20%20observed%20%3D%20cn.to_geopandas()%0A%20%20%20%20return%20D%2C%20observed%2C%20std%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%20Modelled%20flows%20with%20%60betweenness_demand%60%0A%0A%20%20%20%20Rather%20than%20supplying%20the%20matrix%2C%20we%20model%20it.%20The%20inputs%20are%20deliberately%20independent%20of%20the%20cycle%20counts%20we%20are%20trying%20to%20reproduce%3A%20each%20MSOA%20is%20weighted%20by%20its%20all-mode%20commuter%20mass%2C%20residents%20departing%20it%20as%20origins%20and%20workers%20arriving%20at%20it%20as%20destinations%20(the%20%60all%60%20column%20summed%20by%20residence%20and%20by%20workplace).%0A%0A%20%20%20%20The%20two%20decay%20levers%20are%20calibrated%20separately%2C%20as%20intended%3A%0A%0A%20%20%20%20-%20%60beta%60%20shapes%20destination%20choice.%20We%20fit%20it%20against%20the%20observed%20spatial%20pattern%20(Spearman%20rho%20of%20the%20modelled%20flow%20versus%20the%20observed%20flow)%20and%20take%20the%20best%20value.%0A%20%20%20%20-%20%60participation%60%20scales%20trip%20generation.%20With%20all-mode%20origins%2C%20the%20share%20who%20make%20a%20*cycle*%20trip%20is%20the%20cycle%20mode%20share%2C%20which%20we%20read%20straight%20from%20the%20data%20(total%20cycle%20commuters%20over%20total%20commuters).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(D%2C%20cn%2C%20np%2C%20observed%2C%20spearmanr%2C%20sub%2C%20zones_in)%3A%0A%20%20%20%20%23%20independent%20inputs%3A%20all-mode%20commuter%20masses%20per%20MSOA%0A%20%20%20%20orig_all%20%3D%20sub.groupby(%22msoa1%22)%5B%22all%22%5D.sum()%0A%20%20%20%20dest_all%20%3D%20sub.groupby(%22msoa2%22)%5B%22all%22%5D.sum()%0A%20%20%20%20cents%20%3D%20zones_in.copy()%0A%20%20%20%20cents%5B%22geometry%22%5D%20%3D%20cents.geometry.centroid%0A%20%20%20%20origins_gdf%20%3D%20cents.assign(w%3Dcents%5B%22geo_code%22%5D.map(orig_all).fillna(0.0))%0A%20%20%20%20origins_gdf%20%3D%20origins_gdf%5Borigins_gdf%5B%22w%22%5D%20%3E%200%5D%5B%5B%22w%22%2C%20%22geometry%22%5D%5D%0A%20%20%20%20dests_gdf%20%3D%20cents.assign(w%3Dcents%5B%22geo_code%22%5D.map(dest_all).fillna(0.0))%0A%20%20%20%20dests_gdf%20%3D%20dests_gdf%5Bdests_gdf%5B%22w%22%5D%20%3E%200%5D%5B%5B%22w%22%2C%20%22geometry%22%5D%5D%0A%0A%20%20%20%20live%20%3D%20observed.live.to_numpy()%0A%20%20%20%20obs_v%20%3D%20observed%5Bf%22cc_betweenness_%7BD%7D%22%5D.to_numpy()%0A%0A%20%20%20%20def%20demand(beta%2C%20participation)%3A%0A%20%20%20%20%20%20%20%20cn.betweenness_demand(%0A%20%20%20%20%20%20%20%20%20%20%20%20origins_gdf%3Dorigins_gdf%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20destinations_gdf%3Ddests_gdf%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20origin_weight_col%3D%22w%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20destination_weight_col%3D%22w%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20distances%3D%5BD%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20decay_fn%3Df%22exp(-%7Bbeta%3A.6f%7D%20*%20c)%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20participation%3Dparticipation%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tolerance%3D10.0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20max_netw_assign_dist%3D1500.0%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20modelled%20%3D%20cn.to_geopandas()%0A%20%20%20%20%20%20%20%20rho%20%3D%20spearmanr(modelled%5Bf%22cc_demand_%7BD%7D%22%5D.to_numpy()%5Blive%5D%2C%20obs_v%5Blive%5D).statistic%0A%20%20%20%20%20%20%20%20return%20modelled%2C%20rho%0A%0A%20%20%20%20%23%20calibrate%20beta%20against%20the%20observed%20pattern%3B%20participation%20from%20the%20cycle%20mode%20share%0A%20%20%20%20betas%20%3D%20%5B0.0001%2C%200.0002%2C%200.0005%2C%200.001%2C%200.002%5D%0A%20%20%20%20rhos%20%3D%20%5Bdemand(b%2C%201.0)%5B1%5D%20for%20b%20in%20betas%5D%0A%20%20%20%20beta_star%20%3D%20betas%5Bint(np.argmax(rhos))%5D%0A%20%20%20%20participation%20%3D%20float(sub%5B%22bicycle%22%5D.sum())%20%2F%20float(sub%5B%22all%22%5D.sum())%0A%20%20%20%20modelled%2C%20rho%20%3D%20demand(beta_star%2C%20participation)%0A%20%20%20%20print(%22beta%20sweep%20(rho%20vs%20observed)%3A%22%2C%20%7Bb%3A%20round(r%2C%203)%20for%20b%2C%20r%20in%20zip(betas%2C%20rhos%2C%20strict%3DTrue)%7D)%0A%20%20%20%20print(f%22calibrated%3A%20beta%3D%7Bbeta_star%3Ag%7D%2C%20participation%3D%7Bparticipation%3A.3f%7D%2C%20rho%3D%7Brho%3A.3f%7D%22)%0A%20%20%20%20return%20beta_star%2C%20modelled%2C%20participation%2C%20rho%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%20Comparison%0A%0A%20%20%20%20The%20three%20panels%20share%20the%20network%2C%20the%2020%20km%20threshold%2C%20and%20the%20styling%3A%20a%20light-grey%20substrate%20for%20context%2C%20with%20more-travelled%20streets%20drawn%20darker%20and%20thicker%20on%20a%20percentile-scaled%20log%20ramp.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(D%2C%20beta_star%2C%20modelled%2C%20np%2C%20observed%2C%20participation%2C%20plt%2C%20rho%2C%20std)%3A%0A%20%20%20%20def%20draw(ax%2C%20gdf%2C%20col%2C%20title)%3A%0A%20%20%20%20%20%20%20%20liv%20%3D%20gdf%5Bgdf.live%5D.copy()%0A%20%20%20%20%20%20%20%20lv%20%3D%20np.log1p(liv%5Bcol%5D.to_numpy())%0A%20%20%20%20%20%20%20%20lo%2C%20hi%20%3D%20np.quantile(lv%2C%200.20)%2C%20np.quantile(lv%2C%200.99)%0A%20%20%20%20%20%20%20%20t%20%3D%20np.clip((lv%20-%20lo)%20%2F%20(hi%20-%20lo%20%2B%201e-9)%2C%200%2C%201)%20**%201.1%0A%20%20%20%20%20%20%20%20ax.set_facecolor(%22white%22)%0A%20%20%20%20%20%20%20%20liv.plot(ax%3Dax%2C%20color%3D%22%23dedede%22%2C%20linewidth%3D0.2)%0A%20%20%20%20%20%20%20%20order%20%3D%20np.argsort(t)%0A%20%20%20%20%20%20%20%20liv.iloc%5Border%5D.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20ax%3Dax%2C%20color%3Dplt.get_cmap(%22Reds%22)(0.10%20%2B%200.90%20*%20t%5Border%5D)%2C%20linewidth%3D0.05%20%2B%202.6%20*%20t%5Border%5D%20**%201.5%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20ax.set_title(title%2C%20color%3D%22black%22%2C%20fontsize%3D12%2C%20loc%3D%22left%22)%0A%20%20%20%20%20%20%20%20ax.set_aspect(%22equal%22)%0A%20%20%20%20%20%20%20%20ax.set_axis_off()%0A%0A%20%20%20%20bkey%2C%20dkey%20%3D%20f%22cc_betweenness_%7BD%7D%22%2C%20f%22cc_demand_%7BD%7D%22%0A%20%20%20%20fig%2C%20axes%20%3D%20plt.subplots(1%2C%203%2C%20figsize%3D(18%2C%207)%2C%20facecolor%3D%22white%22)%0A%20%20%20%20draw(axes%5B0%5D%2C%20std%2C%20bkey%2C%20%22Standard%20betweenness%20(uniform)%22)%0A%20%20%20%20draw(axes%5B1%5D%2C%20observed%2C%20bkey%2C%20%22Observed%20(PCT%20bicycle)%22)%0A%20%20%20%20draw(axes%5B2%5D%2C%20modelled%2C%20dkey%2C%20f%22Modelled%20(calibrated%2C%20rho%3D%7Brho%3A.2f%7D)%22)%0A%20%20%20%20fig.suptitle(%0A%20%20%20%20%20%20%20%20f%22Central%20London%20cycle%20flows%2C%2020%20km%2C%20beta%3D%7Bbeta_star%3Ag%7D%2C%20participation%3D%7Bparticipation%3A.2f%7D%22%2C%0A%20%20%20%20%20%20%20%20color%3D%22black%22%2C%0A%20%20%20%20%20%20%20%20y%3D0.04%2C%0A%20%20%20%20)%0A%20%20%20%20fig.tight_layout(rect%3D(0%2C%200.03%2C%201%2C%201))%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%0A%0A%20%20%20%20The%20observed%20and%20modelled%20panels%20concentrate%20flow%20on%20the%20same%20corridors%2C%20and%20the%20modelled%20flow%20tracks%20the%20observed%20pattern%20well%20above%20what%20topology%20alone%20gives%20(uniform%20betweenness%20correlates%20far%20more%20weakly%20with%20the%20observed%20flow).%20Most%20of%20the%20agreement%20comes%20from%20where%20commuters%20live%20and%20work%2C%20and%20from%20the%20network%20structure%2C%20rather%20than%20from%20fine%20tuning%20of%20the%20decay%3A%20over%20these%20central-London%20distances%20the%20fit%20is%20broad%20in%20%60beta%60.%20The%20%60participation%60%20value%20is%20the%20observed%20cycle%20mode%20share%2C%20so%20it%20sets%20the%20volume%20rather%20than%20the%20shape.%0A%0A%20%20%20%20Variations%20to%20try%3A%20swap%20the%20origins%20and%20destinations%20for%20population%20and%20employment%20layers%20from%20an%20independent%20source%3B%20lower%20%60participation%60%20further%20to%20see%20trip%20generation%20contract%20where%20access%20is%20poorer%3B%20or%20route%20the%20observed%20matrix%20at%20shorter%20thresholds%20to%20isolate%20local%20cycling%20catchments.%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.callout(%0A%20%20%20%20%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20**Replicating%20the%20modelled%20flows%20in%20QGIS**%0A%0A%20%20%20%20The%20modelled%20step%20maps%20directly%20onto%20the%20QGIS%20plugin%2C%20so%20the%20same%20result%20can%20be%20produced%20without%20code.%0A%0A%20%20%20%201.%20Load%20the%20street%20network%20and%20build%20the%20dual%20graph%20with%20the%20**Cityseer**%20network%20tools.%0A%20%20%20%202.%20Load%20the%20origin%20and%20destination%20points%20(here%2C%20MSOA%20centroids%20carrying%20the%20all-mode%20commuter%20masses).%0A%20%20%20%203.%20Run%20**Cityseer%20%26rsaquo%3B%20Demand%20Betweenness%20(OD%20Flow)**%20with%3A%0A%20%20%20%20%20%20%20%20-%20*Origins%20layer*%20and%20*Origin%20weight%20field*%20set%20to%20the%20residents%20mass.%0A%20%20%20%20%20%20%20%20-%20*Destinations%20layer*%20and%20*Destination%20weight%20field*%20set%20to%20the%20workplace%20mass.%0A%20%20%20%20%20%20%20%20-%20*Decay%20function*%20and%20*Participation*%20set%20to%20the%20calibrated%20values%20printed%20above%20(of%20the%20form%20%60exp(-beta%20*%20c)%60%20and%20the%20participation%20share).%0A%20%20%20%20%20%20%20%20-%20*Distance%20thresholds*%20%6020000%60.%0A%0A%20%20%20%20The%20algorithm%20writes%20a%20demand-weighted%20betweenness%20column%20per%20threshold%20onto%20the%20network%2C%20the%20same%20quantity%20%60betweenness_demand%60%20returns%20here.%20The%20explicit-matrix%20path%20(%60betweenness_od%60)%20is%20library-only%20for%20now%3B%20in%20QGIS%2C%20model%20the%20flows%20with%20the%20demand%20algorithm.%0A%20%20%20%20%22%22%22)%2C%0A%20%20%20%20%20%20%20%20kind%3D%22info%22%2C%0A%20%20%20%20)%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
b1d183773138b06dc3a86615a098d762