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%20Statistics%20from%20OSM%20Data%0A%0A%20%20%20%20Calculate%20building%20statistics%20from%20OpenStreetMap%20data%20fetched%20with%20%60osmnx%60.%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%20for%20the%20statistics%20computation.%20Because%20this%20example%20decomposes%20the%20network%20first%2C%20which%20is%20a%20lower-level%20graph%20operation%20without%20a%20%60CityNetwork%60%20equivalent%2C%20the%20graph%20is%20prepared%20with%20the%20%60tools%60%20modules%20and%20then%20bridged%20into%20the%20high-level%20API%20with%20%60CityNetwork.from_nx%60.%0A%0A%20%20%20%20Network%20data%20%C2%A9%20%5BOpenStreetMap%5D(https%3A%2F%2Fwww.openstreetmap.org%2Fcopyright)%20contributors%2C%20available%20under%20the%20Open%20Database%20Licence.%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%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20momepy%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20import%20seaborn%20as%20sns%0A%20%20%20%20from%20cityseer.network%20import%20CityNetwork%0A%20%20%20%20from%20cityseer.tools%20import%20graphs%2C%20io%0A%20%20%20%20from%20matplotlib%20import%20colors%0A%20%20%20%20from%20osmnx%20import%20features%0A%0A%20%20%20%20return%20CityNetwork%2C%20features%2C%20graphs%2C%20io%2C%20momepy%2C%20pd%2C%20plt%2C%20sns%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%20To%20start%2C%20follow%20the%20same%20approach%20as%20shown%20in%20the%20network%20examples%20to%20create%20the%20network.%20The%20graph%20is%20decomposed%20with%20%5B%60nx_decompose%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Ftools%2Fgraphs%23nx_decompose)%20so%20that%20statistics%20are%20sampled%20at%20a%20higher%20spatial%20resolution%2C%20then%20handed%20to%20%60CityNetwork.from_nx%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CityNetwork%2C%20graphs%2C%20io)%3A%0A%20%20%20%20lng%2C%20lat%20%3D%20-0.13396079424572427%2C%2051.51371088849723%0A%20%20%20%20buffer%20%3D%201500%0A%20%20%20%20poly_wgs%2C%20_epsg_code%20%3D%20io.buffered_point_poly(lng%2C%20lat%2C%20buffer)%0A%20%20%20%20G%20%3D%20io.osm_graph_from_poly(poly_wgs)%0A%20%20%20%20G%20%3D%20graphs.nx_decompose(G%2C%2050)%0A%20%20%20%20cn%20%3D%20CityNetwork.from_nx(G)%0A%20%20%20%20return%20G%2C%20cn%2C%20poly_wgs%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%20Prepare%20the%20buildings%20GeoDataFrame%20by%20downloading%20the%20data%20from%20OpenStreetMap.%20The%20%60osmnx%60%20%5B%60features_from_polygon%60%5D(https%3A%2F%2Fosmnx.readthedocs.io%2Fen%2Fstable%2Fuser-reference.html%23osmnx.features.features_from_polygon)%20function%20works%20well%20for%20this%20purpose.%20In%20this%20instance%2C%20we%20target%20features%20tagged%20as%20%60building%60.%0A%0A%20%20%20%20It%20is%20important%20to%20convert%20the%20derivative%20GeoDataFrame%20to%20the%20same%20CRS%20as%20the%20network.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(G%2C%20features%2C%20poly_wgs)%3A%0A%20%20%20%20data_gdf%20%3D%20features.features_from_polygon(poly_wgs%2C%20tags%3D%7B%22building%22%3A%20True%7D)%0A%20%20%20%20data_gdf%20%3D%20data_gdf.to_crs(G.graph%5B%22crs%22%5D)%0A%20%20%20%20data_gdf.tail()%0A%20%20%20%20return%20(data_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%20Some%20preparatory%20data%20cleaning%20is%20typically%20necessary.%20This%20example%20extracts%20the%20particular%20rows%20and%20columns%20of%20interest%20for%20the%20subsequent%20steps%20of%20analysis.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(data_gdf)%3A%0A%20%20%20%20bldg_way%20%3D%20data_gdf.loc%5B%22way%22%5D%0A%20%20%20%20bldg_way%20%3D%20bldg_way.reset_index(level%3D0%2C%20drop%3DTrue)%0A%20%20%20%20bldg_way%20%3D%20bldg_way%5B%5B%22geometry%22%5D%5D%0A%20%20%20%20bldg_way.head()%0A%20%20%20%20return%20(bldg_way%2C)%0A%0A%0A%40app.cell%0Adef%20_(data_gdf)%3A%0A%20%20%20%20bldg_rel%20%3D%20data_gdf.loc%5B%22relation%22%5D%0A%20%20%20%20bldg_rel%20%3D%20bldg_rel.reset_index(level%3D0%2C%20drop%3DTrue)%0A%20%20%20%20bldg_rel%20%3D%20bldg_rel%5B%5B%22geometry%22%5D%5D%0A%20%20%20%20bldg_rel.head()%0A%20%20%20%20return%20(bldg_rel%2C)%0A%0A%0A%40app.cell%0Adef%20_(bldg_rel%2C%20bldg_way%2C%20pd)%3A%0A%20%20%20%20bldgs_gpd%20%3D%20pd.concat(%5Bbldg_way%2C%20bldg_rel%5D%2C%20axis%3D0)%0A%20%20%20%20bldgs_gpd%20%3D%20bldgs_gpd.explode(ignore_index%3DTrue)%0A%20%20%20%20bldgs_gpd%20%3D%20bldgs_gpd.reset_index(drop%3DTrue)%0A%20%20%20%20bldgs_gpd.head()%0A%20%20%20%20return%20(bldgs_gpd%2C)%0A%0A%0A%40app.cell%0Adef%20_(bldgs_gpd%2C%20momepy)%3A%0A%20%20%20%20bldgs_gpd%5B%22area%22%5D%20%3D%20bldgs_gpd.geometry.area%0A%20%20%20%20bldgs_gpd%5B%22perimeter%22%5D%20%3D%20bldgs_gpd.geometry.length%0A%20%20%20%20bldgs_gpd%5B%22compactness%22%5D%20%3D%20momepy.circular_compactness(bldgs_gpd.geometry)%0A%20%20%20%20bldgs_gpd%5B%22orientation%22%5D%20%3D%20momepy.orientation(bldgs_gpd.geometry)%0A%20%20%20%20bldgs_gpd%5B%22shape_index%22%5D%20%3D%20momepy.shape_index(bldgs_gpd.geometry)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(bldgs_gpd)%3A%0A%20%20%20%20bldgs_gpd.geometry.type.unique()%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%20Use%20the%20%5B%60compute_stats%60%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fapi%2Fnetwork%23compute_stats)%20method%20to%20compute%20statistics%20for%20numeric%20columns%20in%20the%20%60GeoDataFrame%60%2C%20specified%20with%20the%20%60stats_column_labels%60%20argument.%20The%20statistics%20are%20aggregated%20over%20the%20network%20using%20network%20distances.%20The%20%60measures%60%20argument%20selects%20which%20statistics%20to%20compute%3B%20without%20it%2C%20the%20full%20set%20of%20%60count%60%2C%20%60sum%60%2C%20%60min%60%2C%20%60max%60%2C%20%60mean%60%2C%20%60median%60%2C%20%60mad%60%20(median%20absolute%20deviation)%20and%20%60var%60%20is%20generated.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(bldgs_gpd%2C%20cn)%3A%0A%20%20%20%20distances%20%3D%20%5B100%2C%20200%5D%0A%20%20%20%20_cn%2C%20bldgs_gpd_1%20%3D%20cn.compute_stats(%0A%20%20%20%20%20%20%20%20bldgs_gpd%2C%0A%20%20%20%20%20%20%20%20stats_column_labels%3D%5B%22area%22%2C%20%22perimeter%22%2C%20%22compactness%22%2C%20%22orientation%22%2C%20%22shape_index%22%5D%2C%0A%20%20%20%20%20%20%20%20distances%3Ddistances%2C%0A%20%20%20%20%20%20%20%20measures%3D%5B%22mean%22%2C%20%22count%22%5D%2C%0A%20%20%20%20)%0A%20%20%20%20nodes_gdf_1%20%3D%20cn.to_geopandas()%0A%20%20%20%20return%20bldgs_gpd_1%2C%20nodes_gdf_1%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%20This%20generates%20a%20%60cc_%7Bcolumn%7D_%7Bmeasure%7D_%7Bdistance%7D%60%20column%20per%20selected%20measure%20for%20each%20of%20the%20input%20distance%20thresholds.%20By%20default%20the%20aggregations%20are%20unweighted%3B%20pass%20a%20%60decay_fn%60%20expression%20such%20as%20%60%22exp(-4%20*%20p)%22%60%20to%20weight%20the%20contribution%20of%20each%20point%20by%20its%20distance%20from%20the%20point%20of%20measure.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(nodes_gdf_1)%3A%0A%20%20%20%20nodes_gdf_1.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%20The%20result%20in%20columns%20can%20be%20explored%20with%20conventional%20Python%20ecosystem%20tools%20such%20as%20%60seaborn%60%20and%20%60matplotlib%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(nodes_gdf_1%2C%20sns)%3A%0A%20%20%20%20sns.histplot(data%3Dnodes_gdf_1%2C%20x%3D%22cc_area_mean_100%22%2C%20bins%3D50)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(bldgs_gpd_1%2C%20nodes_gdf_1%2C%20plt)%3A%0A%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(1%2C%201%2C%20figsize%3D(7%2C%207)%2C%20dpi%3D150)%0A%20%20%20%20_g%20%3D%20nodes_gdf_1.copy()%0A%20%20%20%20_g%5B%22_r%22%5D%20%3D%20_g%5B%22cc_area_mean_100%22%5D.rank(pct%3DTrue)%0A%20%20%20%20_g%20%3D%20_g.sort_values(%22_r%22)%20%20%23%20strongest%20drawn%20last%0A%20%20%20%20_g.plot(ax%3Dax%2C%20color%3Dplt.get_cmap(%22OrRd%22)(_g%5B%22_r%22%5D)%2C%20linewidth%3D0.15%20%2B%202.25%20*%20_g%5B%22_r%22%5D)%0A%20%20%20%20bldgs_gpd_1.plot(color%3D%22%23cccccc%22%2C%20edgecolor%3D%22%23bbbbbb%22%2C%20alpha%3D0.5%2C%20ax%3Dax)%0A%20%20%20%20ax.set_title(%22Mean%20building%20area%2C%20100%20m%22%2C%20loc%3D%22left%22)%0A%20%20%20%20ax.set_axis_off()%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%20Conclusion%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%20This%20notebook%20computed%20network-distance%20building%20statistics%20from%20OpenStreetMap%20data%20for%20London%2C%20using%20%60osmnx%60%20to%20fetch%20building%20footprints%20and%20%60momepy%60%20to%20derive%20morphological%20attributes%20(area%2C%20perimeter%2C%20compactness%2C%20orientation%2C%20shape%20index).%20The%20decomposed%20graph%20was%20bridged%20into%20the%20high-level%20API%20with%20%60CityNetwork.from_nx%60%2C%20and%20the%20%60compute_stats%60%20method%20aggregates%20the%20numeric%20attributes%20over%20the%20network%20at%20specified%20distance%20thresholds.%0A%0A%20%20%20%20**Next%20steps%3A**%20For%20visual%20enclosure%20metrics%2C%20see%20%5BVisibility%20from%20OSM%5D(https%3A%2F%2Fcityseer.benchmarkurbanism.com%2Fexamples%2Fvisibility%2Fvis-osm).%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
1c0ad890a8b977fea6fbf9f4533ef474