{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Use OSMnx to calculate street network indicators\n", "\n", "Author: [Geoff Boeing](https://geoffboeing.com/)\n", "\n", " - [Overview of OSMnx](http://geoffboeing.com/2016/11/osmnx-python-street-networks/)\n", " - [GitHub repo](https://github.com/gboeing/osmnx)\n", " - [Examples, demos, tutorials](https://github.com/gboeing/osmnx-examples)\n", " - [Documentation](https://osmnx.readthedocs.io/en/stable/)\n", " - [Journal article/citation](http://geoffboeing.com/publications/osmnx-complex-street-networks/)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import networkx as nx\n", "import osmnx as ox\n", "import pandas as pd\n", "\n", "ox.__version__" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Calculate basic street network measures (topological and geometric)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# get the network for Piedmont, calculate its basic stats, then show the average circuity\n", "stats = ox.basic_stats(ox.graph_from_place(\"Piedmont, California, USA\"))\n", "stats[\"circuity_avg\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To calculate density-based stats, you must also pass the network's bounding area in square meters (otherwise basic_stats() will just skip them in the calculation):" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# get the street network for a place, and its area in square meters\n", "place = \"Piedmont, California, USA\"\n", "gdf = ox.geocode_to_gdf(place)\n", "area = ox.project_gdf(gdf).unary_union.area\n", "G = ox.graph_from_place(place, network_type=\"drive\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# calculate basic and extended network stats, merge them together, and display\n", "stats = ox.basic_stats(G, area=area)\n", "pd.Series(stats)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Streets/intersection counts and proportions are nested dicts inside the stats dict. To convert these stats to a pandas dataframe (to compare/analyze multiple networks against each other), just unpack these nested dicts first:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# unpack dicts into individiual keys:values\n", "stats = ox.basic_stats(G, area=area)\n", "for k, count in stats[\"streets_per_node_counts\"].items():\n", " stats[f\"{k}way_int_count\"] = count\n", "for k, proportion in stats[\"streets_per_node_proportions\"].items():\n", " stats[f\"{k}way_int_prop\"] = proportion\n", "\n", "# delete the no longer needed dict elements\n", "del stats[\"streets_per_node_counts\"]\n", "del stats[\"streets_per_node_proportions\"]\n", "\n", "# load as a pandas dataframe\n", "pd.DataFrame(pd.Series(stats, name=\"value\")).round(3)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Inspect betweenness centrality" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# calculate betweenness with a digraph of G (ie, no parallel edges)\n", "bc = nx.betweenness_centrality(ox.get_digraph(G), weight=\"length\")\n", "max_node, max_bc = max(bc.items(), key=lambda x: x[1])\n", "max_node, max_bc" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In the city of Piedmont, California, the node with the highest betweenness centrality has ~31% of all shortest paths running through it. Let's highlight it in the plot:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "nc = [\"r\" if node == max_node else \"w\" for node in G.nodes]\n", "ns = [80 if node == max_node else 15 for node in G.nodes]\n", "fig, ax = ox.plot_graph(G, node_size=ns, node_color=nc, node_zorder=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "~30% of all shortest paths run through the node highlighted in red. Let's look at the relative betweenness centrality of every node in the graph:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# add the betweenness centraliy values as new node attributes, then plot\n", "nx.set_node_attributes(G, bc, \"bc\")\n", "nc = ox.plot.get_node_colors_by_attr(G, \"bc\", cmap=\"plasma\")\n", "fig, ax = ox.plot_graph(\n", " G,\n", " node_color=nc,\n", " node_size=30,\n", " node_zorder=2,\n", " edge_linewidth=0.2,\n", " edge_color=\"w\",\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Above, the nodes are visualized by betweenness centrality, from low (dark violet) to high (light yellow). The colors in the colorspace are linearly mapped to the attribute values." ] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python (ox)", "language": "python", "name": "ox" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.6" } }, "nbformat": 4, "nbformat_minor": 4 }