From f2abae6af2bcd870a8ee1795b1720b577f192d1a Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 12:59:53 -0800 Subject: [PATCH 1/9] remove prettier --- .pre-commit-config.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 73ff911..f993e14 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,12 +15,6 @@ repos: args: [--branch, main] - id: trailing-whitespace - - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v4.0.0-alpha.8" - hooks: - - id: prettier - types_or: [markdown, yaml] - - repo: https://github.com/nbQA-dev/nbQA rev: "1.9.1" hooks: From a1c27b6034b88ea728d14d3b50883c24e3d3383b Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 13:02:31 -0800 Subject: [PATCH 2/9] update date --- LICENSE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE.txt b/LICENSE.txt index 032ddc1..81aea8b 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2016-2024 Geoff Boeing https://geoffboeing.com/ +Copyright (c) 2016-2025 Geoff Boeing https://geoffboeing.com/ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 8a67fefc1b849a7c1bf27d8d6b4638c04310ec38 Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 13:02:38 -0800 Subject: [PATCH 3/9] remove python pin --- environment.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/environment.yml b/environment.yml index b0b6503..e9bbb10 100644 --- a/environment.yml +++ b/environment.yml @@ -7,5 +7,4 @@ dependencies: - osmnx=2.0.* - pillow - pre-commit - - python=3.11.* - python-igraph From 1cc4f763192e0196008970a326869161fc74100d Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 13:48:49 -0800 Subject: [PATCH 4/9] update examples --- notebooks/00-osmnx-features-demo.ipynb | 62 ++++++++++++---------- notebooks/01-overview-osmnx.ipynb | 72 ++++++++++++++------------ notebooks/02-routing-speed-time.ipynb | 56 ++++++++++---------- notebooks/03-graph-place-queries.ipynb | 64 ++++++++++++----------- 4 files changed, 137 insertions(+), 117 deletions(-) diff --git a/notebooks/00-osmnx-features-demo.ipynb b/notebooks/00-osmnx-features-demo.ipynb index a4e91c7..2e10ec1 100644 --- a/notebooks/00-osmnx-features-demo.ipynb +++ b/notebooks/00-osmnx-features-demo.ipynb @@ -54,8 +54,8 @@ "outputs": [], "source": [ "# download/model a street network for some city then visualize it\n", - "G = ox.graph_from_place(\"Piedmont, California, USA\", network_type=\"drive\")\n", - "fig, ax = ox.plot_graph(G)" + "G = ox.graph.graph_from_place(\"Piedmont, California, USA\", network_type=\"drive\")\n", + "fig, ax = ox.plot.plot_graph(G)" ] }, { @@ -67,7 +67,9 @@ "OSMnx models all networks as NetworkX `MultiDiGraph` objects. You can convert to:\n", " - undirected MultiGraphs\n", " - DiGraphs without (possible) parallel edges\n", - " - GeoPandas node/edge GeoDataFrames" + " - GeoPandas node/edge GeoDataFrames\n", + "\n", + "Note that converting to an undirected MultiGraph is really only meant for use cases where a function or algorithm only accepts a MultiGraph argument. If you just want a fully bidirectional graph (such as for a walking network), just configure the `settings` module’s `bidirectional_network_types` before creating your graph." ] }, { @@ -76,6 +78,10 @@ "metadata": {}, "outputs": [], "source": [ + "# get a fully bidirection network (as a MultiDiGraph)\n", + "ox.settings.bidirectional_network_types += \"drive\"\n", + "G = ox.graph.graph_from_place(\"Piedmont, California, USA\", network_type=\"drive\")\n", + "\n", "# convert your MultiDiGraph to an undirected MultiGraph\n", "M = ox.convert.to_undirected(G)\n", "\n", @@ -90,7 +96,7 @@ "outputs": [], "source": [ "# you can convert your graph to node and edge GeoPandas GeoDataFrames\n", - "gdf_nodes, gdf_edges = ox.graph_to_gdfs(G)\n", + "gdf_nodes, gdf_edges = ox.convert.graph_to_gdfs(G)\n", "gdf_nodes.head()" ] }, @@ -117,7 +123,7 @@ "outputs": [], "source": [ "# convert node/edge GeoPandas GeoDataFrames to a NetworkX MultiDiGraph\n", - "G2 = ox.graph_from_gdfs(gdf_nodes, gdf_edges, graph_attrs=G.graph)" + "G2 = ox.convert.graph_from_gdfs(gdf_nodes, gdf_edges, graph_attrs=G.graph)" ] }, { @@ -134,9 +140,9 @@ "outputs": [], "source": [ "# what sized area does our network cover in square meters?\n", - "G_proj = ox.project_graph(G)\n", - "nodes_proj = ox.graph_to_gdfs(G_proj, edges=False)\n", - "graph_area_m = nodes_proj.unary_union.convex_hull.area\n", + "G_proj = ox.projection.project_graph(G)\n", + "nodes_proj = ox.convert.graph_to_gdfs(G_proj, edges=False)\n", + "graph_area_m = nodes_proj.union_all().convex_hull.area\n", "graph_area_m" ] }, @@ -147,7 +153,7 @@ "outputs": [], "source": [ "# show some basic stats about the network\n", - "ox.basic_stats(G_proj, area=graph_area_m, clean_int_tol=15)" + "ox.stats.basic_stats(G_proj, area=graph_area_m, clean_int_tol=15)" ] }, { @@ -164,8 +170,8 @@ "outputs": [], "source": [ "# save graph to disk as geopackage (for GIS) or graphml file (for gephi etc)\n", - "ox.save_graph_geopackage(G, filepath=\"./data/mynetwork.gpkg\")\n", - "ox.save_graphml(G, filepath=\"./data/mynetwork.graphml\")" + "ox.io.save_graph_geopackage(G, filepath=\"./data/mynetwork.gpkg\")\n", + "ox.io.save_graphml(G, filepath=\"./data/mynetwork.graphml\")" ] }, { @@ -196,7 +202,7 @@ "source": [ "# color edges in original graph with closeness centralities from line graph\n", "ec = ox.plot.get_edge_colors_by_attr(G, \"edge_centrality\", cmap=\"inferno\")\n", - "fig, ax = ox.plot_graph(G, edge_color=ec, edge_linewidth=2, node_size=0)" + "fig, ax = ox.plot.plot_graph(G, edge_color=ec, edge_linewidth=2, node_size=0)" ] }, { @@ -235,8 +241,8 @@ "outputs": [], "source": [ "# find the shortest path between nodes, minimizing travel time, then plot it\n", - "route = ox.shortest_path(G, orig, dest, weight=\"travel_time\")\n", - "fig, ax = ox.plot_graph_route(G, route, node_size=0)" + "route = ox.routing.shortest_path(G, orig, dest, weight=\"travel_time\")\n", + "fig, ax = ox.plot.plot_graph_route(G, route, node_size=0)" ] }, { @@ -286,7 +292,9 @@ " G = ox.elevation.add_node_elevations_google(G, api_key=google_elevation_api_key)\n", " G = ox.elevation.add_edge_grades(G)\n", " nc = ox.plot.get_node_colors_by_attr(G, \"elevation\", cmap=\"plasma\")\n", - " fig, ax = ox.plot_graph(G, node_color=nc, node_size=20, edge_linewidth=2, edge_color=\"#333\")\n", + " fig, ax = ox.plot.plot_graph(\n", + " G, node_color=nc, node_size=20, edge_linewidth=2, edge_color=\"#333\"\n", + " )\n", "except ImportError:\n", " print(\"You need a google_elevation_api_key to run this cell.\")" ] @@ -317,8 +325,8 @@ "source": [ "# you can make query an unambiguous dict to help the geocoder find it\n", "place = {\"city\": \"San Francisco\", \"state\": \"California\", \"country\": \"USA\"}\n", - "G = ox.graph_from_place(place, network_type=\"drive\", truncate_by_edge=True)\n", - "fig, ax = ox.plot_graph(G, figsize=(10, 10), node_size=0, edge_color=\"y\", edge_linewidth=0.2)" + "G = ox.graph.graph_from_place(place, network_type=\"drive\", truncate_by_edge=True)\n", + "fig, ax = ox.plot.plot_graph(G, figsize=(10, 10), node_size=0, edge_color=\"y\", edge_linewidth=0.2)" ] }, { @@ -328,8 +336,8 @@ "outputs": [], "source": [ "# you can get networks anywhere in the world\n", - "G = ox.graph_from_place(\"Sinalunga, Italy\", network_type=\"all\")\n", - "fig, ax = ox.plot_graph(G, node_size=0, edge_linewidth=0.5)" + "G = ox.graph.graph_from_place(\"Sinalunga, Italy\", network_type=\"all\")\n", + "fig, ax = ox.plot.plot_graph(G, node_size=0, edge_linewidth=0.5)" ] }, { @@ -342,8 +350,8 @@ "# ...useful when OSM just doesn't already have a polygon for the place you want\n", "wurster_hall = (37.870605, -122.254830)\n", "one_mile = 1609 # meters\n", - "G = ox.graph_from_point(wurster_hall, dist=one_mile, network_type=\"drive\")\n", - "fig, ax = ox.plot_graph(G, node_size=0)" + "G = ox.graph.graph_from_point(wurster_hall, dist=one_mile, network_type=\"drive\")\n", + "fig, ax = ox.plot.plot_graph(G, node_size=0)" ] }, { @@ -369,7 +377,7 @@ "outputs": [], "source": [ "# get NY subway rail network\n", - "G = ox.graph_from_place(\n", + "G = ox.graph.graph_from_place(\n", " \"New York, New York, USA\",\n", " retain_all=False,\n", " truncate_by_edge=True,\n", @@ -377,7 +385,7 @@ " custom_filter='[\"railway\"~\"subway\"]',\n", ")\n", "\n", - "fig, ax = ox.plot_graph(G, node_size=0, edge_color=\"w\", edge_linewidth=0.2)" + "fig, ax = ox.plot.plot_graph(G, node_size=0, edge_color=\"w\", edge_linewidth=0.2)" ] }, { @@ -398,7 +406,7 @@ "# get all building footprints in some neighborhood\n", "place = \"SoHo, New York, NY\"\n", "tags = {\"building\": True}\n", - "gdf = ox.features_from_place(place, tags)\n", + "gdf = ox.features.features_from_place(place, tags)\n", "gdf.shape" ] }, @@ -408,7 +416,7 @@ "metadata": {}, "outputs": [], "source": [ - "fig, ax = ox.plot_footprints(gdf, figsize=(3, 3))" + "fig, ax = ox.plot.plot_footprints(gdf, figsize=(3, 3))" ] }, { @@ -426,7 +434,7 @@ "source": [ "# get all parks and bus stops in some neighborhood\n", "tags = {\"leisure\": \"park\", \"highway\": \"bus_stop\"}\n", - "gdf = ox.features_from_place(place, tags)\n", + "gdf = ox.features.features_from_place(place, tags)\n", "gdf.shape" ] }, @@ -454,7 +462,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/01-overview-osmnx.ipynb b/notebooks/01-overview-osmnx.ipynb index 571353a..db43cf6 100644 --- a/notebooks/01-overview-osmnx.ipynb +++ b/notebooks/01-overview-osmnx.ipynb @@ -65,7 +65,7 @@ "outputs": [], "source": [ "# get the boundary polygon for manhattan, project it, and plot it\n", - "city = ox.geocode_to_gdf(\"Manhattan, New York, USA\")\n", + "city = ox.geocoder.geocode_to_gdf(\"Manhattan, New York, USA\")\n", "city_proj = ox.projection.project_gdf(city)\n", "ax = city_proj.plot(fc=\"gray\", ec=\"none\")\n", "_ = ax.axis(\"off\")" @@ -85,7 +85,7 @@ " \"Emeryville, California, USA\",\n", " \"Alameda, Alameda County, CA, USA\",\n", "]\n", - "east_bay = ox.geocode_to_gdf(place_names)\n", + "east_bay = ox.geocoder.geocode_to_gdf(place_names)\n", "east_bay.to_file(\"./data/east_bay.gpkg\", driver=\"GPKG\")\n", "east_bay = ox.projection.project_gdf(east_bay)\n", "ax = east_bay.plot(fc=\"gray\", ec=\"none\")\n", @@ -99,7 +99,7 @@ "outputs": [], "source": [ "# if you know the OSM ID of the place(s) you want, you can query it directly\n", - "ox.geocode_to_gdf([\"R357794\", \"N8170768521\", \"W427818536\"], by_osmid=True)" + "ox.geocoder.geocode_to_gdf([\"R357794\", \"N8170768521\", \"W427818536\"], by_osmid=True)" ] }, { @@ -119,12 +119,14 @@ " - a .osm formatted xml file\n", "\n", "You can also specify several different network types:\n", + " - 'all' - download all OSM streets and paths, including private-access ones (this is the default network type unless you specify a different one)\n", + " - 'all_public' - download all non-private OSM streets and paths\n", + " - 'bike' - get all streets and paths that cyclists can use\n", " - 'drive' - get drivable public streets (but not service roads)\n", " - 'drive_service' - get drivable streets, including service roads\n", - " - 'walk' - get all streets and paths that pedestrians can use (this network type ignores one-way directionality)\n", - " - 'bike' - get all streets and paths that cyclists can use\n", - " - 'all' - download all non-private OSM streets and paths (this is the default network type unless you specify a different one)\n", - " - 'all_private' - download all OSM streets and paths, including private-access ones" + " - 'walk' - get all streets and paths that pedestrians can use\n", + "\n", + "If you just want a fully bidirectional graph, just configure the `settings` module's `bidirectional_network_types` before creating your graph (it includes the \"walk\" network type by default)." ] }, { @@ -141,11 +143,11 @@ "metadata": {}, "outputs": [], "source": [ - "# define a bounding box in San Francisco\n", - "bbox = 37.79, 37.78, -122.41, -122.43\n", + "# define a bounding box in San Francisco as (left, bottom, right, top)\n", + "bbox = -122.43, 37.78, -122.41, 37.79\n", "\n", "# create network from that bounding box\n", - "G = ox.graph_from_bbox(bbox=bbox, network_type=\"drive_service\")" + "G = ox.graph.graph_from_bbox(bbox, network_type=\"drive_service\")" ] }, { @@ -166,7 +168,7 @@ "location_point = (37.791427, -122.410018)\n", "\n", "# create network from point, inside bounding box of N, S, E, W each 750m from point\n", - "G = ox.graph_from_point(location_point, dist=750, dist_type=\"bbox\", network_type=\"drive\")" + "G = ox.graph.graph_from_point(location_point, dist=750, dist_type=\"bbox\", network_type=\"drive\")" ] }, { @@ -185,8 +187,8 @@ "outputs": [], "source": [ "# same point again, but create network only of nodes within 500m along the network from point\n", - "G = ox.graph_from_point(location_point, dist=500, dist_type=\"network\")\n", - "fig, ax = ox.plot_graph(G, node_color=\"r\")" + "G = ox.graph.graph_from_point(location_point, dist=500, dist_type=\"network\")\n", + "fig, ax = ox.plot.plot_graph(G, node_color=\"r\")" ] }, { @@ -203,8 +205,8 @@ "outputs": [], "source": [ "# create network only of nodes within 500m walking along the network from point\n", - "G = ox.graph_from_point(location_point, dist=500, dist_type=\"network\", network_type=\"walk\")\n", - "fig, ax = ox.plot_graph(G, node_color=\"r\")" + "G = ox.graph.graph_from_point(location_point, dist=500, dist_type=\"network\", network_type=\"walk\")\n", + "fig, ax = ox.plot.plot_graph(G, node_color=\"r\")" ] }, { @@ -222,7 +224,7 @@ "outputs": [], "source": [ "# network from address, including only nodes within 1km along the network from the address\n", - "G = ox.graph_from_address(\n", + "G = ox.graph.graph_from_address(\n", " address=\"350 5th Ave, New York, NY\",\n", " dist=1000,\n", " dist_type=\"network\",\n", @@ -230,7 +232,7 @@ ")\n", "\n", "# you can project the network to UTM (zone calculated automatically)\n", - "G_projected = ox.project_graph(G)" + "G_projected = ox.projection.project_graph(G)" ] }, { @@ -249,7 +251,7 @@ "outputs": [], "source": [ "# create the street network within the city of Piedmont's borders\n", - "G = ox.graph_from_place(\"Piedmont, California, USA\", network_type=\"drive\")" + "G = ox.graph.graph_from_place(\"Piedmont, California, USA\", network_type=\"drive\")" ] }, { @@ -264,7 +266,7 @@ " {\"city\": \"Los Altos Hills\", \"state\": \"California\"},\n", " \"Loyola, California\",\n", "]\n", - "G = ox.graph_from_place(places, truncate_by_edge=True)" + "G = ox.graph.graph_from_place(places, truncate_by_edge=True)" ] }, { @@ -274,8 +276,8 @@ "outputs": [], "source": [ "# save to disk as GeoPackage file then plot\n", - "ox.save_graph_geopackage(G)\n", - "fig, ax = ox.plot_graph(G, node_size=0, edge_color=\"w\", edge_linewidth=0.2)" + "ox.io.save_graph_geopackage(G)\n", + "fig, ax = ox.plot.plot_graph(G, node_size=0, edge_color=\"w\", edge_linewidth=0.2)" ] }, { @@ -297,7 +299,7 @@ "mission_district = calif[(calif[\"CITY\"] == \"San Francisco\") & (calif[\"NAME\"] == \"Mission\")]\n", "polygon = mission_district[\"geometry\"].iloc[0]\n", "\n", - "G2 = ox.graph_from_polygon(polygon, network_type=\"drive_service\")" + "G2 = ox.graph.graph_from_polygon(polygon, network_type=\"drive_service\")" ] }, { @@ -314,7 +316,7 @@ "outputs": [], "source": [ "# create graph from .osm extract file\n", - "G = ox.graph_from_xml(\"./input_data/West-Oakland.osm.bz2\")" + "G = ox.graph.graph_from_xml(\"./input_data/West-Oakland.osm.bz2\")" ] }, { @@ -334,7 +336,9 @@ "source": [ "# create a network around some (lat, lng) point but do not simplify it yet\n", "location_point = (33.299896, -111.831638)\n", - "G = ox.graph_from_point(location_point, network_type=\"drive_service\", dist=500, simplify=False)" + "G = ox.graph.graph_from_point(\n", + " location_point, network_type=\"drive_service\", dist=500, simplify=False\n", + ")" ] }, { @@ -345,7 +349,7 @@ "source": [ "# turn off strict mode and see what nodes we'd remove, in yellow\n", "nc = [\"r\" if ox.simplification._is_endpoint(G, node, None, None) else \"y\" for node in G.nodes()]\n", - "fig, ax = ox.plot_graph(G, node_color=nc)" + "fig, ax = ox.plot.plot_graph(G, node_color=nc)" ] }, { @@ -362,8 +366,8 @@ "outputs": [], "source": [ "# simplify the network\n", - "G = ox.simplify_graph(G)\n", - "fig, ax = ox.plot_graph(G, node_color=\"r\")" + "G = ox.simplification.simplify_graph(G)\n", + "fig, ax = ox.plot.plot_graph(G, node_color=\"r\")" ] }, { @@ -374,7 +378,7 @@ "source": [ "# show the simplified network with edges colored by length\n", "ec = ox.plot.get_edge_colors_by_attr(G, attr=\"length\", cmap=\"plasma_r\")\n", - "fig, ax = ox.plot_graph(\n", + "fig, ax = ox.plot.plot_graph(\n", " G, node_color=\"w\", node_edgecolor=\"k\", node_size=50, edge_color=ec, edge_linewidth=3\n", ")" ] @@ -387,7 +391,7 @@ "source": [ "# highlight all parallel (multiple) edges\n", "ec = [\"gray\" if k == 0 or u == v else \"r\" for u, v, k in G.edges(keys=True)]\n", - "fig, ax = ox.plot_graph(\n", + "fig, ax = ox.plot.plot_graph(\n", " G, node_color=\"w\", node_edgecolor=\"k\", node_size=50, edge_color=ec, edge_linewidth=3\n", ")" ] @@ -400,7 +404,7 @@ "source": [ "# highlight all one-way edges in the mission district network from earlier\n", "ec = [\"r\" if data[\"oneway\"] else \"w\" for u, v, key, data in G2.edges(keys=True, data=True)]\n", - "fig, ax = ox.plot_graph(G2, node_size=0, edge_color=ec, edge_linewidth=1.5, edge_alpha=0.7)" + "fig, ax = ox.plot.plot_graph(G2, node_size=0, edge_color=ec, edge_linewidth=1.5, edge_alpha=0.7)" ] }, { @@ -419,7 +423,7 @@ "outputs": [], "source": [ "# save street network as GeoPackage to work with in GIS\n", - "ox.save_graph_geopackage(G, filepath=\"./data/network.gpkg\")" + "ox.io.save_graph_geopackage(G, filepath=\"./data/network.gpkg\")" ] }, { @@ -429,7 +433,7 @@ "outputs": [], "source": [ "# save street network as GraphML file to work with later in OSMnx or networkx or gephi\n", - "ox.save_graphml(G, filepath=\"./data/network.graphml\")" + "ox.io.save_graphml(G, filepath=\"./data/network.graphml\")" ] }, { @@ -446,7 +450,7 @@ "outputs": [], "source": [ "# calculate basic street network metrics and display average circuity\n", - "stats = ox.basic_stats(G)\n", + "stats = ox.stats.basic_stats(G)\n", "stats[\"circuity_avg\"]" ] }, @@ -484,7 +488,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/02-routing-speed-time.ipynb b/notebooks/02-routing-speed-time.ipynb index 55f5c4c..19a89f9 100644 --- a/notebooks/02-routing-speed-time.ipynb +++ b/notebooks/02-routing-speed-time.ipynb @@ -38,8 +38,8 @@ "outputs": [], "source": [ "place = \"Piedmont, California, USA\"\n", - "G = ox.graph_from_place(place, network_type=\"drive\")\n", - "Gp = ox.project_graph(G)" + "G = ox.graph.graph_from_place(place, network_type=\"drive\")\n", + "Gp = ox.projection.project_graph(G)" ] }, { @@ -72,7 +72,7 @@ "outputs": [], "source": [ "# find each nearest node to several points, and optionally return distance\n", - "nodes, dists = ox.nearest_nodes(Gp, X, Y, return_dist=True)" + "nodes, dists = ox.distance.nearest_nodes(Gp, X, Y, return_dist=True)" ] }, { @@ -82,7 +82,7 @@ "outputs": [], "source": [ "# or, find the nearest node to a single point\n", - "node = ox.nearest_nodes(Gp, X0, Y0)\n", + "node = ox.distance.nearest_nodes(Gp, X0, Y0)\n", "node" ] }, @@ -93,7 +93,7 @@ "outputs": [], "source": [ "# find each nearest edge to several points, and optionally return distance\n", - "edges, dists = ox.nearest_edges(Gp, X, Y, return_dist=True)" + "edges, dists = ox.distance.nearest_edges(Gp, X, Y, return_dist=True)" ] }, { @@ -103,7 +103,7 @@ "outputs": [], "source": [ "# find the nearest edge to a single point\n", - "edge = ox.nearest_edges(Gp, X0, Y0)\n", + "edge = ox.distance.nearest_edges(Gp, X0, Y0)\n", "edge" ] }, @@ -125,8 +125,8 @@ "# find the shortest path (by distance) between these nodes then plot it\n", "orig = list(G)[0]\n", "dest = list(G)[120]\n", - "route = ox.shortest_path(G, orig, dest, weight=\"length\")\n", - "fig, ax = ox.plot_graph_route(G, route, route_color=\"y\", route_linewidth=6, node_size=0)" + "route = ox.routing.shortest_path(G, orig, dest, weight=\"length\")\n", + "fig, ax = ox.plot.plot_graph_route(G, route, route_color=\"y\", route_linewidth=6, node_size=0)" ] }, { @@ -142,8 +142,10 @@ "metadata": {}, "outputs": [], "source": [ - "routes = ox.k_shortest_paths(G, orig, dest, k=30, weight=\"length\")\n", - "fig, ax = ox.plot_graph_routes(G, list(routes), route_colors=\"y\", route_linewidth=4, node_size=0)" + "routes = ox.routing.k_shortest_paths(G, orig, dest, k=30, weight=\"length\")\n", + "fig, ax = ox.plot.plot_graph_routes(\n", + " G, list(routes), route_colors=\"y\", route_linewidth=4, node_size=0\n", + ")" ] }, { @@ -162,10 +164,10 @@ "outputs": [], "source": [ "# impute speed on all edges missing data\n", - "G = ox.add_edge_speeds(G)\n", + "G = ox.routing.add_edge_speeds(G)\n", "\n", "# calculate travel time (seconds) for all edges\n", - "G = ox.add_edge_travel_times(G)" + "G = ox.routing.add_edge_travel_times(G)" ] }, { @@ -175,7 +177,7 @@ "outputs": [], "source": [ "# see mean speed/time values by road type\n", - "edges = ox.graph_to_gdfs(G, nodes=False)\n", + "edges = ox.convert.graph_to_gdfs(G, nodes=False)\n", "edges[\"highway\"] = edges[\"highway\"].astype(str)\n", "edges.groupby(\"highway\")[[\"length\", \"speed_kph\", \"travel_time\"]].mean().round(1)" ] @@ -189,8 +191,8 @@ "# same thing again, but this time pass in a few default speed values (km/hour)\n", "# to fill in edges with missing `maxspeed` from OSM\n", "hwy_speeds = {\"residential\": 35, \"secondary\": 50, \"tertiary\": 60}\n", - "G = ox.add_edge_speeds(G, hwy_speeds=hwy_speeds)\n", - "G = ox.add_edge_travel_times(G)" + "G = ox.routing.add_edge_speeds(G, hwy_speeds=hwy_speeds)\n", + "G = ox.routing.add_edge_travel_times(G)" ] }, { @@ -202,8 +204,8 @@ "# calculate two routes by minimizing travel distance vs travel time\n", "orig = list(G)[1]\n", "dest = list(G)[120]\n", - "route1 = ox.shortest_path(G, orig, dest, weight=\"length\")\n", - "route2 = ox.shortest_path(G, orig, dest, weight=\"travel_time\")" + "route1 = ox.routing.shortest_path(G, orig, dest, weight=\"length\")\n", + "route2 = ox.routing.shortest_path(G, orig, dest, weight=\"travel_time\")" ] }, { @@ -213,7 +215,7 @@ "outputs": [], "source": [ "# plot the routes\n", - "fig, ax = ox.plot_graph_routes(\n", + "fig, ax = ox.plot.plot_graph_routes(\n", " G, routes=[route1, route2], route_colors=[\"r\", \"y\"], route_linewidth=6, node_size=0\n", ")" ] @@ -284,7 +286,7 @@ "# %%time\n", "# it takes 2.3 seconds to solve all the routes using all the cores on my computer\n", "# I have a 24-thread AMD 5900x: performance will depend on your specific CPU\n", - "# routes = ox.shortest_path(G, origs, dests, weight=\"travel_time\", cpus=None)" + "# routes = ox.routing.shortest_path(G, origs, dests, weight=\"travel_time\", cpus=None)" ] }, { @@ -295,7 +297,7 @@ "source": [ "%%time\n", "# it takes 29 seconds to solve all the routes using just 1 core on my computer\n", - "routes = ox.shortest_path(G, origs, dests, weight=\"travel_time\", cpus=1)" + "routes = ox.routing.shortest_path(G, origs, dests, weight=\"travel_time\", cpus=1)" ] }, { @@ -333,7 +335,7 @@ "metadata": {}, "outputs": [], "source": [ - "G2 = ox.graph_from_address(\n", + "G2 = ox.graph.graph_from_address(\n", " \"N. Sicily Pl., Chandler, Arizona\",\n", " dist=800,\n", " network_type=\"drive\",\n", @@ -343,8 +345,8 @@ "destination = (33.312994, -111.894998)\n", "origin_node = ox.distance.nearest_nodes(G2, origin[1], origin[0])\n", "destination_node = ox.distance.nearest_nodes(G2, destination[1], destination[0])\n", - "route = ox.shortest_path(G2, origin_node, destination_node)\n", - "fig, ax = ox.plot_graph_route(G2, route, route_color=\"c\", node_size=0)" + "route = ox.routing.shortest_path(G2, origin_node, destination_node)\n", + "fig, ax = ox.plot.plot_graph_route(G2, route, route_color=\"c\", node_size=0)" ] }, { @@ -361,13 +363,13 @@ "outputs": [], "source": [ "location_point = (33.299896, -111.831638)\n", - "G2 = ox.graph_from_point(location_point, dist=400, truncate_by_edge=True)\n", + "G2 = ox.graph.graph_from_point(location_point, dist=400, truncate_by_edge=True)\n", "origin = (33.301821, -111.829871)\n", "destination = (33.301402, -111.833108)\n", "origin_node = ox.distance.nearest_nodes(G2, origin[1], origin[0])\n", "destination_node = ox.distance.nearest_nodes(G2, destination[1], destination[0])\n", - "route = ox.shortest_path(G2, origin_node, destination_node)\n", - "fig, ax = ox.plot_graph_route(G2, route, route_color=\"c\", node_size=0)" + "route = ox.routing.shortest_path(G2, origin_node, destination_node)\n", + "fig, ax = ox.plot.plot_graph_route(G2, route, route_color=\"c\", node_size=0)" ] }, { @@ -394,7 +396,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/03-graph-place-queries.ipynb b/notebooks/03-graph-place-queries.ipynb index d29ed85..3822dda 100644 --- a/notebooks/03-graph-place-queries.ipynb +++ b/notebooks/03-graph-place-queries.ipynb @@ -62,13 +62,13 @@ "outputs": [], "source": [ "# neighborhoods or boroughs\n", - "gdf = ox.geocode_to_gdf(\"Manhattan, New York, New York, USA\")\n", + "gdf = ox.geocoder.geocode_to_gdf(\"Manhattan, New York, New York, USA\")\n", "\n", "# counties\n", - "gdf = ox.geocode_to_gdf(\"Cook County, Illinois, United States\")\n", + "gdf = ox.geocoder.geocode_to_gdf(\"Cook County, Illinois, United States\")\n", "\n", "# states\n", - "gdf = ox.geocode_to_gdf(\"Iowa\")" + "gdf = ox.geocoder.geocode_to_gdf(\"Iowa\")" ] }, { @@ -78,7 +78,7 @@ "outputs": [], "source": [ "# you can get multiple countries in a single query\n", - "gdf = ox.geocode_to_gdf([\"United Kingdom\", \"Ireland\"])\n", + "gdf = ox.geocoder.geocode_to_gdf([\"United Kingdom\", \"Ireland\"])\n", "\n", "# or multiple cities\n", "places = [\n", @@ -88,7 +88,7 @@ " \"Emeryville, California, USA\",\n", " \"Alameda, Alameda County, CA, USA\",\n", "]\n", - "gdf = ox.geocode_to_gdf(places)" + "gdf = ox.geocoder.geocode_to_gdf(places)" ] }, { @@ -107,10 +107,10 @@ "outputs": [], "source": [ "# oops, this gets the county of alameda rather than the city!\n", - "alameda1 = ox.geocode_to_gdf(\"Alameda, California, USA\")\n", + "alameda1 = ox.geocoder.geocode_to_gdf(\"Alameda, California, USA\")\n", "\n", "# this gets the city of alameda\n", - "alameda2 = ox.geocode_to_gdf(\n", + "alameda2 = ox.geocoder.geocode_to_gdf(\n", " {\n", " \"city\": \"Alameda\",\n", " \"county\": \"Alameda County\",\n", @@ -138,7 +138,7 @@ "metadata": {}, "outputs": [], "source": [ - "mexico = ox.geocode_to_gdf(\"Mexico\", which_result=2)\n", + "mexico = ox.geocoder.geocode_to_gdf(\"Mexico\", which_result=2)\n", "type(mexico[\"geometry\"].iloc[0])" ] }, @@ -149,7 +149,7 @@ "outputs": [], "source": [ "# let the geocoder find the first Polygon/MultiPolygon result\n", - "mexico = ox.geocode_to_gdf(\"Mexico\", which_result=None)\n", + "mexico = ox.geocoder.geocode_to_gdf(\"Mexico\", which_result=None)\n", "type(mexico[\"geometry\"].iloc[0])" ] }, @@ -160,7 +160,7 @@ "outputs": [], "source": [ "# instead of a string, you can pass a dict containing a structured query for better precision\n", - "mexico = ox.geocode_to_gdf({\"country\": \"Mexico\"})\n", + "mexico = ox.geocoder.geocode_to_gdf({\"country\": \"Mexico\"})\n", "type(mexico[\"geometry\"].iloc[0])" ] }, @@ -171,7 +171,7 @@ "outputs": [], "source": [ "# you can pass multiple queries with mixed types (dicts and strings)\n", - "mx_gt_tx = ox.geocode_to_gdf([{\"country\": \"Mexico\"}, \"Guatemala\", {\"state\": \"Texas\"}])\n", + "mx_gt_tx = ox.geocoder.geocode_to_gdf([{\"country\": \"Mexico\"}, \"Guatemala\", {\"state\": \"Texas\"}])\n", "mx_gt_tx = ox.projection.project_gdf(mx_gt_tx)\n", "ax = mx_gt_tx.plot(fc=\"gray\", ec=\"w\")\n", "_ = ax.axis(\"off\")" @@ -181,7 +181,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "If you query 'France', OSM returns the country with all its overseas territories as result 1 and Metropolitan France alone as later down the results. Passing `which_result` can help you specifically retrieve the desired geocoding result, or you can query the geospatial feature you want by its OSM ID." + "If you query 'France', OSM returns the country with all its overseas territories as result 1 and Metropolitan France alone as later down the results. Passing `which_result` can help you specifically retrieve the desired geocoding result." ] }, { @@ -190,19 +190,26 @@ "metadata": {}, "outputs": [], "source": [ - "france = ox.geocode_to_gdf(\"France\")\n", + "france = ox.geocoder.geocode_to_gdf(\"France\")\n", "france = ox.projection.project_gdf(france)\n", "ax = france.plot(fc=\"gray\", ec=\"none\")\n", "_ = ax.axis(\"off\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, note that you can also retrieve an element by its OSM ID rather, than trying to geocode a place name, by passing `by_osmid=True` to the function. See documentation for usage details." + ] + }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "france = ox.projection.project_gdf(ox.geocode_to_gdf(\"R1403916\", by_osmid=True))\n", + "france = ox.projection.project_gdf(ox.geocoder.geocode_to_gdf(\"R1403916\", by_osmid=True))\n", "ax = france.plot(fc=\"gray\", ec=\"none\")\n", "_ = ax.axis(\"off\")" ] @@ -211,8 +218,6 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Finally, note that you can also query by OSM ID rather than place name by passing `by_osmid=True` to the function. See documentation for usage details.\n", - "\n", "## 2. Get street networks by place name\n", "\n", "This \"by place\" querying logic works the same as the place boundary querying we just saw above." @@ -225,7 +230,7 @@ "outputs": [], "source": [ "# get the walking network for piedmont\n", - "G = ox.graph_from_place(\"Piedmont, California, USA\", network_type=\"walk\")" + "G = ox.graph.graph_from_place(\"Piedmont, California, USA\", network_type=\"walk\")" ] }, { @@ -235,10 +240,9 @@ "outputs": [], "source": [ "# or get the walking network within a 500 meter buffer of piedmont\n", - "polygon = ox.geocode_to_gdf(\"Piedmont, California, USA\").loc[0, \"geometry\"]\n", - "poly_proj, crs_proj = ox.projection.project_geometry(polygon)\n", - "polygon, _ = ox.projection.project_geometry(poly_proj.buffer(500), crs=crs_proj, to_latlong=True)\n", - "G = ox.graph_from_polygon(polygon, network_type=\"walk\")" + "gdf = ox.geocoder.geocode_to_gdf(\"Piedmont, CA, USA\")\n", + "polygon = ox.utils_geo.buffer_geometry(gdf.iloc[0][\"geometry\"], 500)\n", + "G = ox.graph.graph_from_polygon(polygon, network_type=\"walk\")" ] }, { @@ -255,8 +259,8 @@ "]\n", "\n", "# use retain_all to keep all disconnected subgraphs (e.g. if your places aren't contiguous)\n", - "G = ox.graph_from_place(places, network_type=\"drive\", retain_all=True)\n", - "fig, ax = ox.plot_graph(G, node_size=0, edge_color=\"#FFFF5C\", edge_linewidth=0.25)" + "G = ox.graph.graph_from_place(places, network_type=\"drive\", retain_all=True)\n", + "fig, ax = ox.plot.plot_graph(G, node_size=0, edge_color=\"#FFFF5C\", edge_linewidth=0.25)" ] }, { @@ -270,7 +274,7 @@ " {\"city\": \"Daly City\", \"state\": \"California\"},\n", " {\"city\": \"South San Francisco\", \"state\": \"California\"},\n", "]\n", - "G = ox.graph_from_place(places, network_type=\"drive\")" + "G = ox.graph.graph_from_place(places, network_type=\"drive\")" ] }, { @@ -280,7 +284,7 @@ "outputs": [], "source": [ "# get the network for the borough of manhattan\n", - "G = ox.graph_from_place(\"Manhattan, New York, New York, USA\", network_type=\"drive\")" + "G = ox.graph.graph_from_place(\"Manhattan, New York, New York, USA\", network_type=\"drive\")" ] }, { @@ -291,7 +295,7 @@ "source": [ "# get the network for a neighborhood\n", "place = \"SoHo, New York, NY\"\n", - "G = ox.graph_from_place(place, network_type=\"drive\")" + "G = ox.graph.graph_from_place(place, network_type=\"drive\")" ] }, { @@ -303,8 +307,10 @@ "%%time\n", "# get the network for all of LA\n", "# takes a couple minutes to do all the downloading and processing\n", + "# retain_all=True means we'll keep all the disconnected graph components\n", + "# simplify=False means we won't simplify the graph topology\n", "place = \"Los Angeles, California, USA\"\n", - "G = ox.graph_from_place(place, network_type=\"drive\", simplify=False, retain_all=True)" + "G = ox.graph.graph_from_place(place, network_type=\"drive\", simplify=False, retain_all=True)" ] }, { @@ -314,7 +320,7 @@ "outputs": [], "source": [ "# create a network constrained to the shape of hong kong island\n", - "G = ox.graph_from_place(\"Hong Kong Island\", network_type=\"drive\")" + "G = ox.graph.graph_from_place(\"Hong Kong Island\", network_type=\"drive\")" ] }, { @@ -342,7 +348,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.8" } }, "nbformat": 4, From e8b6a2e4cc8137676e536f0edf5d20ba8a28b83e Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 14:01:16 -0800 Subject: [PATCH 5/9] update examples --- .../04-simplify-graph-consolidate-nodes.ipynb | 34 +++++++++++-------- notebooks/05-save-load-networks.ipynb | 22 ++++++------ .../06-stats-indicators-centrality.ipynb | 18 +++++----- notebooks/07-plot-graph-over-shape.ipynb | 12 +++---- .../08-custom-filters-infrastructure.ipynb | 18 +++++----- 5 files changed, 54 insertions(+), 50 deletions(-) diff --git a/notebooks/04-simplify-graph-consolidate-nodes.ipynb b/notebooks/04-simplify-graph-consolidate-nodes.ipynb index 6dd5362..39cbc38 100644 --- a/notebooks/04-simplify-graph-consolidate-nodes.ipynb +++ b/notebooks/04-simplify-graph-consolidate-nodes.ipynb @@ -45,8 +45,8 @@ "source": [ "# get a street network and plot it with all edge intersections\n", "point = 37.858495, -122.267468\n", - "G = ox.graph_from_point(point, network_type=\"drive\", dist=500)\n", - "fig, ax = ox.plot_graph(G, node_color=\"r\")" + "G = ox.graph.graph_from_point(point, network_type=\"drive\", dist=500)\n", + "fig, ax = ox.plot.plot_graph(G, node_color=\"r\")" ] }, { @@ -65,8 +65,10 @@ "outputs": [], "source": [ "# get a GeoSeries of consolidated intersections\n", - "G_proj = ox.project_graph(G)\n", - "ints = ox.consolidate_intersections(G_proj, rebuild_graph=False, tolerance=15, dead_ends=False)\n", + "G_proj = ox.projection.project_graph(G)\n", + "ints = ox.simplification.consolidate_intersections(\n", + " G_proj, rebuild_graph=False, tolerance=15, dead_ends=False\n", + ")\n", "len(ints)" ] }, @@ -97,7 +99,9 @@ "source": [ "# consolidate intersections and rebuild graph topology\n", "# this reconnects edge geometries to the new consolidated nodes\n", - "G2 = ox.consolidate_intersections(G_proj, rebuild_graph=True, tolerance=15, dead_ends=False)\n", + "G2 = ox.simplification.consolidate_intersections(\n", + " G_proj, rebuild_graph=True, tolerance=15, dead_ends=False\n", + ")\n", "len(G2)" ] }, @@ -107,7 +111,7 @@ "metadata": {}, "outputs": [], "source": [ - "fig, ax = ox.plot_graph(G2, node_color=\"r\")" + "fig, ax = ox.plot.plot_graph(G2, node_color=\"r\")" ] }, { @@ -138,8 +142,8 @@ "source": [ "# create a network around some (lat, lng) point and plot it\n", "location_point = (33.299896, -111.831638)\n", - "G = ox.graph_from_point(location_point, dist=500, simplify=False)\n", - "fig, ax = ox.plot_graph(G, node_color=\"r\")" + "G = ox.graph.graph_from_point(location_point, dist=500, simplify=False)\n", + "fig, ax = ox.plot.plot_graph(G, node_color=\"r\")" ] }, { @@ -150,7 +154,7 @@ "source": [ "# show which nodes we'd remove if we simplify it (yellow)\n", "nc = [\"r\" if ox.simplification._is_endpoint(G, node, None, None) else \"y\" for node in G.nodes()]\n", - "fig, ax = ox.plot_graph(G, node_color=nc)" + "fig, ax = ox.plot.plot_graph(G, node_color=nc)" ] }, { @@ -160,7 +164,7 @@ "outputs": [], "source": [ "# simplify the network\n", - "G2 = ox.simplify_graph(G)" + "G2 = ox.simplification.simplify_graph(G)" ] }, { @@ -172,7 +176,7 @@ "# plot the simplified network and highlight any self-loop edges\n", "loops = [edge[0] for edge in nx.selfloop_edges(G2)]\n", "nc = [\"r\" if node in loops else \"y\" for node in G2.nodes()]\n", - "fig, ax = ox.plot_graph(G2, node_color=nc)" + "fig, ax = ox.plot.plot_graph(G2, node_color=nc)" ] }, { @@ -185,7 +189,7 @@ "nc = [\n", " \"r\" if ox.simplification._is_endpoint(G, node, [\"osmid\"], None) else \"y\" for node in G.nodes()\n", "]\n", - "fig, ax = ox.plot_graph(G, node_color=nc)" + "fig, ax = ox.plot.plot_graph(G, node_color=nc)" ] }, { @@ -195,8 +199,8 @@ "outputs": [], "source": [ "# simplify network with strict mode turned off\n", - "G3 = ox.simplify_graph(G.copy(), edge_attrs_differ=[\"osmid\"])\n", - "fig, ax = ox.plot_graph(G3, node_color=\"r\")" + "G3 = ox.simplification.simplify_graph(G.copy(), edge_attrs_differ=[\"osmid\"])\n", + "fig, ax = ox.plot.plot_graph(G3, node_color=\"r\")" ] }, { @@ -235,7 +239,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/05-save-load-networks.ipynb b/notebooks/05-save-load-networks.ipynb index e7dd3a3..00afd1b 100644 --- a/notebooks/05-save-load-networks.ipynb +++ b/notebooks/05-save-load-networks.ipynb @@ -35,7 +35,7 @@ "source": [ "# get a network\n", "place = \"Piedmont, California, USA\"\n", - "G = ox.graph_from_place(place, network_type=\"drive\")" + "G = ox.graph.graph_from_place(place, network_type=\"drive\")" ] }, { @@ -52,7 +52,7 @@ "outputs": [], "source": [ "# save graph as a geopackage\n", - "ox.save_graph_geopackage(G, filepath=\"./data/piedmont.gpkg\")" + "ox.io.save_graph_geopackage(G, filepath=\"./data/piedmont.gpkg\")" ] }, { @@ -71,8 +71,8 @@ "# save/load graph as a graphml file: this is the best way to save your model\n", "# for subsequent work later\n", "filepath = \"./data/piedmont.graphml\"\n", - "ox.save_graphml(G, filepath)\n", - "G = ox.load_graphml(filepath)" + "ox.io.save_graphml(G, filepath)\n", + "G = ox.io.load_graphml(filepath)" ] }, { @@ -82,7 +82,7 @@ "outputs": [], "source": [ "# if you want to work with your model in gephi, use gephi compatibility mode\n", - "ox.save_graphml(G, filepath=filepath, gephi=True)" + "ox.io.save_graphml(G, filepath=filepath, gephi=True)" ] }, { @@ -99,7 +99,7 @@ "outputs": [], "source": [ "# save street network as SVG\n", - "fig, ax = ox.plot_graph(G, show=False, save=True, close=True, filepath=\"./images/piedmont.svg\")" + "fig, ax = ox.plot.plot_graph(G, show=False, save=True, close=True, filepath=\"./images/piedmont.svg\")" ] }, { @@ -116,7 +116,7 @@ "outputs": [], "source": [ "# get all \"amenities\" and save as a geopackage via geopandas\n", - "gdf = ox.features_from_place(place, tags={\"amenity\": True})\n", + "gdf = ox.features.features_from_place(place, tags={\"amenity\": True})\n", "gdf = gdf.apply(lambda c: c.astype(str) if c.name != \"geometry\" else c, axis=0)\n", "gdf.to_file(\"./data/pois.gpkg\", driver=\"GPKG\")" ] @@ -128,7 +128,7 @@ "outputs": [], "source": [ "# get all building footprints and save as a geopackage via geopandas\n", - "gdf = ox.features_from_place(place, tags={\"building\": True})\n", + "gdf = ox.features.features_from_place(place, tags={\"building\": True})\n", "gdf = gdf.apply(lambda c: c.astype(str) if c.name != \"geometry\" else c, axis=0)\n", "gdf.to_file(\"./data/building_footprints.gpkg\", driver=\"GPKG\")" ] @@ -153,8 +153,8 @@ "# save graph to disk as .osm xml file\n", "ox.settings.all_oneway = True\n", "ox.settings.log_console = True\n", - "G = ox.graph_from_place(\"Piedmont, California, USA\", network_type=\"drive\", simplify=False)\n", - "ox.save_graph_xml(G, filepath=\"./data/piedmont.osm\")" + "G = ox.graph.graph_from_place(\"Piedmont, California, USA\", network_type=\"drive\", simplify=False)\n", + "ox.io.save_graph_xml(G, filepath=\"./data/piedmont.osm\")" ] }, { @@ -182,7 +182,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/06-stats-indicators-centrality.ipynb b/notebooks/06-stats-indicators-centrality.ipynb index 8d78c1e..9c30bcd 100644 --- a/notebooks/06-stats-indicators-centrality.ipynb +++ b/notebooks/06-stats-indicators-centrality.ipynb @@ -41,7 +41,7 @@ "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 = ox.stats.basic_stats(ox.graph.graph_from_place(\"Piedmont, California, USA\"))\n", "stats[\"circuity_avg\"]" ] }, @@ -60,9 +60,9 @@ "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.projection.project_gdf(gdf).unary_union.area\n", - "G = ox.graph_from_place(place, network_type=\"drive\")" + "gdf = ox.geocoder.geocode_to_gdf(place)\n", + "area = ox.projection.project_gdf(gdf).union_all().area\n", + "G = ox.graph.graph_from_place(place, network_type=\"drive\")" ] }, { @@ -72,7 +72,7 @@ "outputs": [], "source": [ "# calculate basic and extended network stats, merge them together, and display\n", - "stats = ox.basic_stats(G, area=area)\n", + "stats = ox.stats.basic_stats(G, area=area)\n", "pd.Series(stats)" ] }, @@ -90,7 +90,7 @@ "outputs": [], "source": [ "# unpack dicts into individiual keys:values\n", - "stats = ox.basic_stats(G, area=area)\n", + "stats = ox.stats.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", @@ -138,7 +138,7 @@ "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)" + "fig, ax = ox.plot.plot_graph(G, node_size=ns, node_color=nc, node_zorder=2)" ] }, { @@ -157,7 +157,7 @@ "# 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", + "fig, ax = ox.plot.plot_graph(\n", " G,\n", " node_color=nc,\n", " node_size=30,\n", @@ -192,7 +192,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/07-plot-graph-over-shape.ipynb b/notebooks/07-plot-graph-over-shape.ipynb index f296fdd..c8a93a1 100644 --- a/notebooks/07-plot-graph-over-shape.ipynb +++ b/notebooks/07-plot-graph-over-shape.ipynb @@ -36,10 +36,10 @@ "source": [ "# get the place boundaries\n", "place = \"Portland, Maine\"\n", - "gdf = ox.geocode_to_gdf(place)\n", + "gdf = ox.geocoder.geocode_to_gdf(place)\n", "\n", "# get the street network, with retain_all=True to retain all the disconnected islands' networks\n", - "G = ox.graph_from_place(place, network_type=\"drive\", retain_all=True)" + "G = ox.graph.graph_from_place(place, network_type=\"drive\", retain_all=True)" ] }, { @@ -49,7 +49,7 @@ "outputs": [], "source": [ "# plot the network, but do not show it or close it yet\n", - "fig, ax = ox.plot_graph(\n", + "fig, ax = ox.plot.plot_graph(\n", " G,\n", " show=False,\n", " close=False,\n", @@ -64,7 +64,7 @@ "\n", "# optionally set up the axes extents\n", "margin = 0.02\n", - "west, south, east, north = gdf.unary_union.bounds\n", + "west, south, east, north = gdf.union_all().bounds\n", "margin_ns = (north - south) * margin\n", "margin_ew = (east - west) * margin\n", "ax.set_ylim((south - margin_ns, north + margin_ns))\n", @@ -85,7 +85,7 @@ "metadata": {}, "outputs": [], "source": [ - "islands = ox.features_from_place(place, tags={\"place\": [\"island\", \"islet\"]})\n", + "islands = ox.features.features_from_place(place, tags={\"place\": [\"island\", \"islet\"]})\n", "islands.shape" ] }, @@ -114,7 +114,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/08-custom-filters-infrastructure.ipynb b/notebooks/08-custom-filters-infrastructure.ipynb index 652e544..a316a8c 100644 --- a/notebooks/08-custom-filters-infrastructure.ipynb +++ b/notebooks/08-custom-filters-infrastructure.ipynb @@ -45,17 +45,17 @@ "\n", "# only get motorway ways\n", "cf = '[\"highway\"~\"motorway\"]'\n", - "G = ox.graph_from_place(place, network_type=\"drive\", custom_filter=cf)\n", + "G = ox.graph.graph_from_place(place, network_type=\"drive\", custom_filter=cf)\n", "print(len(G), \"motorway\")\n", "\n", "# only get primary ways\n", "cf = '[\"highway\"~\"primary\"]'\n", - "G = ox.graph_from_place(place, network_type=\"drive\", custom_filter=cf)\n", + "G = ox.graph.graph_from_place(place, network_type=\"drive\", custom_filter=cf)\n", "print(len(G), \"primary\")\n", "\n", "# use the pipe (|) as 'or' operator\n", "cf = '[\"highway\"~\"motorway|primary\"]'\n", - "G = ox.graph_from_place(place, network_type=\"drive\", custom_filter=cf)\n", + "G = ox.graph.graph_from_place(place, network_type=\"drive\", custom_filter=cf)\n", "print(len(G), \"motorway + primary\")" ] }, @@ -67,7 +67,7 @@ "source": [ "# network of the canals of amsterdam\n", "place = \"Amsterdam, Netherlands\"\n", - "G = ox.graph_from_place(place, custom_filter='[\"waterway\"~\"canal\"]')" + "G = ox.graph.graph_from_place(place, custom_filter='[\"waterway\"~\"canal\"]')" ] }, { @@ -88,8 +88,8 @@ "# takes a couple minutes to do all the downloading and processing\n", "# OSMnx automatically divides up the query into multiple requests to not overload server\n", "cf = '[\"highway\"~\"motorway|motorway_link|trunk|trunk_link\"]'\n", - "G = ox.graph_from_place(\"Belgium\", network_type=\"drive\", custom_filter=cf)\n", - "fig, ax = ox.plot_graph(G, node_size=0)" + "G = ox.graph.graph_from_place(\"Belgium\", network_type=\"drive\", custom_filter=cf)\n", + "fig, ax = ox.plot.plot_graph(G, node_size=0)" ] }, { @@ -102,7 +102,7 @@ "# note this is rail *infrastructure* and thus includes crossovers, sidings, spurs, yards, etc\n", "# for station-based rail network, you should download a station adjacency matrix elsewhere\n", "ox.settings.useful_tags_way += [\"railway\"]\n", - "G = ox.graph_from_place(\n", + "G = ox.graph.graph_from_place(\n", " \"New York, New York, USA\",\n", " retain_all=False,\n", " truncate_by_edge=True,\n", @@ -110,7 +110,7 @@ " custom_filter='[\"railway\"~\"subway\"]',\n", ")\n", "\n", - "fig, ax = ox.plot_graph(G, node_size=0, edge_color=\"w\", edge_linewidth=0.2)" + "fig, ax = ox.plot.plot_graph(G, node_size=0, edge_color=\"w\", edge_linewidth=0.2)" ] }, { @@ -137,7 +137,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.8" } }, "nbformat": 4, From e80128cfe0123f53039c891611c7b3b7b5304918 Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 14:03:12 -0800 Subject: [PATCH 6/9] drop pillow dependency --- environment.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/environment.yml b/environment.yml index e9bbb10..9f706d4 100644 --- a/environment.yml +++ b/environment.yml @@ -5,6 +5,5 @@ channels: dependencies: - jupyterlab - osmnx=2.0.* - - pillow - pre-commit - python-igraph From 9d0a9781d19d4c34e2cbeb6eb8e1006033158452 Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 15:30:59 -0800 Subject: [PATCH 7/9] update examples --- notebooks/09-example-figure-ground.ipynb | 4 +- notebooks/10-building-footprints.ipynb | 18 +++---- notebooks/11-interactive-web-mapping.ipynb | 32 ++++++------ .../12-node-elevations-edge-grades.ipynb | 52 ++++++++++--------- notebooks/13-isolines-isochrones.ipynb | 20 +++---- notebooks/14-osmnx-to-igraph.ipynb | 4 +- notebooks/15-advanced-plotting.ipynb | 48 ++++++++--------- .../16-download-osm-geospatial-features.ipynb | 47 +++++++++++++++-- .../17-street-network-orientations.ipynb | 10 ++-- 9 files changed, 138 insertions(+), 97 deletions(-) diff --git a/notebooks/09-example-figure-ground.ipynb b/notebooks/09-example-figure-ground.ipynb index dfee892..d339489 100644 --- a/notebooks/09-example-figure-ground.ipynb +++ b/notebooks/09-example-figure-ground.ipynb @@ -345,7 +345,7 @@ "point = (37.793897, -122.402189)\n", "fp = \"./images/sf_custom.png\"\n", "G = ox.graph.graph_from_point(point, dist=1000, network_type=\"all\", truncate_by_edge=True)\n", - "fig, ax = ox.plot_figure_ground(\n", + "fig, ax = ox.plot.plot_figure_ground(\n", " G=G,\n", " filepath=fp,\n", " street_widths=street_widths,\n", @@ -383,7 +383,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.9" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/10-building-footprints.ipynb b/notebooks/10-building-footprints.ipynb index 1a16032..7fbc3a7 100644 --- a/notebooks/10-building-footprints.ipynb +++ b/notebooks/10-building-footprints.ipynb @@ -56,10 +56,10 @@ "metadata": {}, "outputs": [], "source": [ - "gdf = ox.features_from_place(\"Piedmont, California, USA\", tags)\n", + "gdf = ox.features.features_from_place(\"Piedmont, California, USA\", tags)\n", "gdf_proj = ox.projection.project_gdf(gdf)\n", "fp = f\"./{img_folder}/piedmont_bldgs.{extension}\"\n", - "fig, ax = ox.plot_footprints(gdf_proj, filepath=fp, dpi=400, save=True, show=False, close=True)\n", + "fig, ax = ox.plot.plot_footprints(gdf_proj, filepath=fp, dpi=400, save=True, show=False, close=True)\n", "Image(fp, height=size, width=size)" ] }, @@ -109,7 +109,7 @@ "outputs": [], "source": [ "# get the total area within Piedmont's admin boundary in sq meters\n", - "place = ox.geocode_to_gdf(\"Piedmont, California, USA\")\n", + "place = ox.geocoder.geocode_to_gdf(\"Piedmont, California, USA\")\n", "place_proj = ox.projection.project_gdf(place)\n", "place_proj.area.iloc[0]" ] @@ -139,11 +139,11 @@ "source": [ "point = (48.873446, 2.294255)\n", "dist = 612\n", - "gdf = ox.features_from_point(point, tags, dist=dist)\n", + "gdf = ox.features.features_from_point(point, tags, dist=dist)\n", "gdf_proj = ox.projection.project_gdf(gdf)\n", "bbox = ox.utils_geo.bbox_from_point(point=point, dist=dist, project_utm=True)\n", "fp = f\"./{img_folder}/paris_bldgs.{extension}\"\n", - "fig, ax = ox.plot_footprints(\n", + "fig, ax = ox.plot.plot_footprints(\n", " gdf_proj,\n", " bbox=bbox,\n", " color=\"w\",\n", @@ -188,8 +188,8 @@ " G = ox.graph.graph_from_point(\n", " point, dist=dist, network_type=network_type, truncate_by_edge=True\n", " )\n", - " gdf = ox.features_from_point(point, tags, dist=dist)\n", - " fig, ax = ox.plot_figure_ground(\n", + " gdf = ox.features.features_from_point(point, tags, dist=dist)\n", + " fig, ax = ox.plot.plot_figure_ground(\n", " G=G,\n", " dist=dist,\n", " default_width=default_width,\n", @@ -198,7 +198,7 @@ " show=False,\n", " close=True,\n", " )\n", - " fig, ax = ox.plot_footprints(\n", + " fig, ax = ox.plot.plot_footprints(\n", " gdf, ax=ax, filepath=fp, dpi=dpi, save=True, show=False, close=True\n", " )\n", "\n", @@ -276,7 +276,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/11-interactive-web-mapping.ipynb b/notebooks/11-interactive-web-mapping.ipynb index a88df0e..2e68f28 100644 --- a/notebooks/11-interactive-web-mapping.ipynb +++ b/notebooks/11-interactive-web-mapping.ipynb @@ -40,10 +40,10 @@ "source": [ "# download a street network then solve a shortest-path route on it\n", "weight = \"length\"\n", - "G = ox.graph_from_place(\"Piedmont, CA, USA\", network_type=\"drive\")\n", + "G = ox.graph.graph_from_place(\"Piedmont, CA, USA\", network_type=\"drive\")\n", "orig = list(G.nodes)[0]\n", "dest = list(G.nodes)[-1]\n", - "route = ox.shortest_path(G, orig, dest, weight=weight)" + "route = ox.routing.shortest_path(G, orig, dest, weight=weight)" ] }, { @@ -62,7 +62,7 @@ "outputs": [], "source": [ "# explore graph edges interactively, with a simple one-liner\n", - "ox.graph_to_gdfs(G, nodes=False).explore()" + "ox.convert.graph_to_gdfs(G, nodes=False).explore()" ] }, { @@ -81,7 +81,7 @@ "outputs": [], "source": [ "# explore graph nodes interactively, with different basemap tiles\n", - "nodes = ox.graph_to_gdfs(G, edges=False)\n", + "nodes = ox.convert.graph_to_gdfs(G, edges=False)\n", "nodes.explore(tiles=\"cartodbpositron\", marker_kwds={\"radius\": 8})" ] }, @@ -94,7 +94,7 @@ "outputs": [], "source": [ "# explore nodes and edges together in a single map\n", - "nodes, edges = ox.graph_to_gdfs(G)\n", + "nodes, edges = ox.convert.graph_to_gdfs(G)\n", "m = edges.explore(color=\"skyblue\", tiles=\"cartodbdarkmatter\")\n", "nodes.explore(m=m, color=\"pink\", marker_kwds={\"radius\": 6})" ] @@ -124,7 +124,7 @@ "source": [ "# explore graph nodes interactively, colored by betweenness centrality\n", "nx.set_node_attributes(G, nx.betweenness_centrality(G, weight=\"length\"), name=\"bc\")\n", - "nodes = ox.graph_to_gdfs(G, edges=False)\n", + "nodes = ox.convert.graph_to_gdfs(G, edges=False)\n", "nodes.explore(tiles=\"cartodbdarkmatter\", column=\"bc\", marker_kwds={\"radius\": 8})" ] }, @@ -169,7 +169,7 @@ "outputs": [], "source": [ "# or explore multiple routes together in a single map\n", - "routes = ox.k_shortest_paths(G, orig, dest, k=200, weight=weight)\n", + "routes = ox.routing.k_shortest_paths(G, orig, dest, k=200, weight=weight)\n", "gdfs = (ox.routing.route_to_gdf(G, route, weight=weight) for route in routes)\n", "m = edges.explore(color=\"#222222\", tiles=\"cartodbdarkmatter\")\n", "for route_edges in gdfs:\n", @@ -203,11 +203,11 @@ "outputs": [], "source": [ "# explore a city's bus stops and rail transit interactively\n", - "bus = ox.features_from_place(place, tags={\"highway\": \"bus_stop\"})\n", + "bus = ox.features.features_from_place(place, tags={\"highway\": \"bus_stop\"})\n", "m = bus.explore(tiles=tiles, color=\"red\", tooltip=\"name\", marker_kwds=mk)\n", - "rail = ox.features_from_place(place, tags={\"railway\": \"light_rail\"})\n", + "rail = ox.features.features_from_place(place, tags={\"railway\": \"light_rail\"})\n", "m = rail.explore(m=m, tiles=tiles, color=\"yellow\", tooltip=\"name\")\n", - "stations = ox.features_from_place(place, tags={\"railway\": \"station\"})\n", + "stations = ox.features.features_from_place(place, tags={\"railway\": \"station\"})\n", "stations.explore(m=m, tiles=tiles, color=\"yellow\", tooltip=\"name\", marker_kwds=mk)" ] }, @@ -218,7 +218,7 @@ "outputs": [], "source": [ "# explore a city's parks interactively\n", - "parks = ox.features_from_place(place, tags={\"leisure\": \"park\"})\n", + "parks = ox.features.features_from_place(place, tags={\"leisure\": \"park\"})\n", "parks.explore(tiles=tiles, color=\"lime\", tooltip=\"name\")" ] }, @@ -229,7 +229,7 @@ "outputs": [], "source": [ "# explore a neighborhood's buildings interactively\n", - "gdf = ox.features_from_place(\"SoHo, New York, NY\", tags={\"building\": True})\n", + "gdf = ox.features.features_from_place(\"SoHo, New York, NY\", tags={\"building\": True})\n", "cols = [\"height\", \"addr:housenumber\", \"addr:street\", \"addr:postcode\"]\n", "gdf.explore(tiles=\"cartodbdarkmatter\", tooltip=cols)" ] @@ -243,10 +243,10 @@ "# explore a neighborhood's buildings + street network interactively\n", "place = \"SoHo, New York, NY\"\n", "cols = [\"height\", \"addr:housenumber\", \"addr:street\", \"addr:postcode\"]\n", - "G = ox.graph_from_place(place, network_type=\"drive\", truncate_by_edge=True)\n", - "gdf = ox.features_from_place(place, tags={\"building\": True})\n", + "G = ox.graph.graph_from_place(place, network_type=\"drive\", truncate_by_edge=True)\n", + "gdf = ox.features.features_from_place(place, tags={\"building\": True})\n", "m = gdf.explore(tiles=tiles, tooltip=cols)\n", - "ox.graph_to_gdfs(G, nodes=False).explore(m=m, color=\"yellow\")" + "ox.convert.graph_to_gdfs(G, nodes=False).explore(m=m, color=\"yellow\")" ] }, { @@ -286,7 +286,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/12-node-elevations-edge-grades.ipynb b/notebooks/12-node-elevations-edge-grades.ipynb index 3671b49..9c428e1 100644 --- a/notebooks/12-node-elevations-edge-grades.ipynb +++ b/notebooks/12-node-elevations-edge-grades.ipynb @@ -47,7 +47,7 @@ "outputs": [], "source": [ "address = \"600 Montgomery St, San Francisco, California, USA\"\n", - "G = ox.graph_from_address(address=address, dist=500, dist_type=\"bbox\", network_type=\"bike\")" + "G = ox.graph.graph_from_address(address=address, dist=500, dist_type=\"bbox\", network_type=\"bike\")" ] }, { @@ -91,7 +91,7 @@ "source": [ "## Elevation from Google Maps Elevation API\n", "\n", - "You will need a Google Maps Elevation [API key](https://developers.google.com/maps/documentation/elevation/start). Consider your API usage limits. OSMnx rounds coordinates to 5 decimal places (approx 1 meter) to fit 350 locations in a batch. Note that there is some spatial inaccuracy given Google's dataset's resolution. For example, in San Francisco (where the resolution is 19 meters) a couple of edges in hilly parks have a 50+ percent grade because Google assigns one of their nodes the elevation of a hill adjacent to the street." + "You will need a Google Maps Elevation [API key](https://developers.google.com/maps/documentation/elevation/start). Remember to track your API usage and costs. If you don't want to set up a Google Maps API key, you could use a free alternative web service that provides the same interface, such as [Open Topo Data](https://www.opentopodata.org/) which doesn't require an API key. Note that there is some spatial inaccuracy in elevation data resolution. For example, in San Francisco (where Google's resolution is ~19 meters) a couple of edges in hilly parks have a 50+ percent grade because Google assigns one of their nodes the elevation of a hill adjacent to the street." ] }, { @@ -100,11 +100,15 @@ "metadata": {}, "outputs": [], "source": [ - "# replace this with your own API key!\n", - "try:\n", - " from keys import google_elevation_api_key\n", - "except ImportError:\n", - " sys.exit() # you need an API key to proceed" + "# add elevation to each of the nodes, using the Open Topo Data, then calculate edge grades\n", + "G = ox.graph.graph_from_place(\"Piedmont, California, USA\", network_type=\"drive\")\n", + "original_elevation_url = ox.settings.elevation_url_template\n", + "ox.settings.elevation_url_template = (\n", + " \"https://api.opentopodata.org/v1/aster30m?locations={locations}\"\n", + ")\n", + "G = ox.elevation.add_node_elevations_google(G, batch_size=100, pause=1)\n", + "G = ox.elevation.add_edge_grades(G)\n", + "ox.settings.elevation_url_template = original_elevation_url" ] }, { @@ -113,19 +117,19 @@ "metadata": {}, "outputs": [], "source": [ + "# or use the Google Maps Elevation API\n", + "# replace this with your own API key!\n", + "try:\n", + " from keys import google_elevation_api_key\n", + "except ImportError:\n", + " sys.exit() # you need an API key to proceed\n", + "\n", "# get the street network for san francisco\n", "place = \"San Francisco\"\n", "place_query = {\"city\": \"San Francisco\", \"state\": \"California\", \"country\": \"USA\"}\n", - "G = ox.graph_from_place(place_query, network_type=\"drive\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# add elevation to each of the nodes, using the google elevation API, then calculate edge grades\n", + "G = ox.graph_from_place(place_query, network_type=\"drive\")\n", + "\n", + "# add elevation to each of the nodes then calculate edge grades\n", "G = ox.elevation.add_node_elevations_google(G, api_key=google_elevation_api_key)\n", "G = ox.elevation.add_edge_grades(G)" ] @@ -180,7 +184,7 @@ "source": [ "# get one color for each node, by elevation, then plot the network\n", "nc = ox.plot.get_node_colors_by_attr(G, \"elevation\", cmap=\"plasma\")\n", - "fig, ax = ox.plot_graph(G, node_color=nc, node_size=5, edge_color=\"#333333\", bgcolor=\"k\")" + "fig, ax = ox.plot.plot_graph(G, node_color=nc, node_size=5, edge_color=\"#333333\", bgcolor=\"k\")" ] }, { @@ -200,7 +204,7 @@ "source": [ "# get a color for each edge, by grade, then plot the network\n", "ec = ox.plot.get_edge_colors_by_attr(G, \"grade_abs\", cmap=\"plasma\", num_bins=5, equal_size=True)\n", - "fig, ax = ox.plot_graph(G, edge_color=ec, edge_linewidth=0.5, node_size=0, bgcolor=\"k\")" + "fig, ax = ox.plot.plot_graph(G, edge_color=ec, edge_linewidth=0.5, node_size=0, bgcolor=\"k\")" ] }, { @@ -258,8 +262,8 @@ "metadata": {}, "outputs": [], "source": [ - "route_by_length = ox.shortest_path(G, origin, destination, weight=\"length\")\n", - "fig, ax = ox.plot_graph_route(G, route_by_length, bbox=bbox, node_size=0)" + "route_by_length = ox.routing.shortest_path(G, origin, destination, weight=\"length\")\n", + "fig, ax = ox.plot.plot_graph_route(G, route_by_length, bbox=bbox, node_size=0)" ] }, { @@ -275,8 +279,8 @@ "metadata": {}, "outputs": [], "source": [ - "route_by_impedance = ox.shortest_path(G, origin, destination, weight=\"impedance\")\n", - "fig, ax = ox.plot_graph_route(G, route_by_impedance, bbox=bbox, node_size=0)" + "route_by_impedance = ox.routing.shortest_path(G, origin, destination, weight=\"impedance\")\n", + "fig, ax = ox.plot.plot_graph_route(G, route_by_impedance, bbox=bbox, node_size=0)" ] }, { @@ -358,7 +362,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/13-isolines-isochrones.ipynb b/notebooks/13-isolines-isochrones.ipynb index 87d0a00..ff62880 100644 --- a/notebooks/13-isolines-isochrones.ipynb +++ b/notebooks/13-isolines-isochrones.ipynb @@ -60,7 +60,7 @@ "outputs": [], "source": [ "# download the street network\n", - "G = ox.graph_from_place(place, network_type=network_type)" + "G = ox.graph.graph_from_place(place, network_type=network_type)" ] }, { @@ -70,10 +70,10 @@ "outputs": [], "source": [ "# find the centermost node and then project the graph to UTM\n", - "gdf_nodes = ox.graph_to_gdfs(G, edges=False)\n", - "x, y = gdf_nodes[\"geometry\"].unary_union.centroid.xy\n", + "gdf_nodes = ox.convert.graph_to_gdfs(G, edges=False)\n", + "x, y = gdf_nodes[\"geometry\"].union_all().centroid.xy\n", "center_node = ox.distance.nearest_nodes(G, x[0], y[0])\n", - "G = ox.project_graph(G)" + "G = ox.projection.project_graph(G)" ] }, { @@ -121,7 +121,7 @@ " node_colors[node] = color\n", "nc = [node_colors[node] if node in node_colors else \"none\" for node in G.nodes()]\n", "ns = [15 if node in node_colors else 0 for node in G.nodes()]\n", - "fig, ax = ox.plot_graph(\n", + "fig, ax = ox.plot.plot_graph(\n", " G,\n", " node_color=nc,\n", " node_size=ns,\n", @@ -151,7 +151,7 @@ "for trip_time in sorted(trip_times, reverse=True):\n", " subgraph = nx.ego_graph(G, center_node, radius=trip_time, distance=\"time\")\n", " node_points = [Point((data[\"x\"], data[\"y\"])) for node, data in subgraph.nodes(data=True)]\n", - " bounding_poly = gpd.GeoSeries(node_points).unary_union.convex_hull\n", + " bounding_poly = gpd.GeoSeries(node_points).union_all().convex_hull\n", " isochrone_polys.append(bounding_poly)\n", "gdf = gpd.GeoDataFrame(geometry=isochrone_polys)" ] @@ -163,7 +163,7 @@ "outputs": [], "source": [ "# plot the network then add isochrones as colored polygon patches\n", - "fig, ax = ox.plot_graph(\n", + "fig, ax = ox.plot.plot_graph(\n", " G, show=False, close=False, edge_color=\"#999999\", edge_alpha=0.2, node_size=0\n", ")\n", "gdf.plot(ax=ax, color=iso_colors, ec=\"none\", alpha=0.6, zorder=-1)\n", @@ -202,7 +202,7 @@ " n = nodes_gdf.buffer(node_buff).geometry\n", " e = gpd.GeoSeries(edge_lines).buffer(edge_buff).geometry\n", " all_gs = list(n) + list(e)\n", - " new_iso = gpd.GeoSeries(all_gs).unary_union\n", + " new_iso = gpd.GeoSeries(all_gs).union_all()\n", "\n", " # try to fill in surrounded areas so shapes will appear solid and\n", " # blocks without white space inside them\n", @@ -217,7 +217,7 @@ "gdf = gpd.GeoDataFrame(geometry=isochrone_polys)\n", "\n", "# plot the network then add isochrones as colored polygon patches\n", - "fig, ax = ox.plot_graph(\n", + "fig, ax = ox.plot.plot_graph(\n", " G, show=False, close=False, edge_color=\"#999999\", edge_alpha=0.2, node_size=0\n", ")\n", "gdf.plot(ax=ax, color=iso_colors, ec=\"none\", alpha=0.6, zorder=-1)\n", @@ -248,7 +248,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/14-osmnx-to-igraph.ipynb b/notebooks/14-osmnx-to-igraph.ipynb index 27a483d..d2be5ab 100644 --- a/notebooks/14-osmnx-to-igraph.ipynb +++ b/notebooks/14-osmnx-to-igraph.ipynb @@ -51,7 +51,7 @@ "outputs": [], "source": [ "# create networkx graph\n", - "G_nx = ox.graph_from_place(\"Piedmont, CA, USA\", network_type=\"drive\")\n", + "G_nx = ox.graph.graph_from_place(\"Piedmont, CA, USA\", network_type=\"drive\")\n", "osmids = list(G_nx.nodes)\n", "G_nx = nx.relabel.convert_node_labels_to_integers(G_nx)\n", "\n", @@ -223,7 +223,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/15-advanced-plotting.ipynb b/notebooks/15-advanced-plotting.ipynb index d1fb7cb..d244177 100644 --- a/notebooks/15-advanced-plotting.ipynb +++ b/notebooks/15-advanced-plotting.ipynb @@ -33,7 +33,7 @@ "outputs": [], "source": [ "place = \"Piedmont, California, USA\"\n", - "G = ox.graph_from_place(place, network_type=\"drive\")" + "G = ox.graph.graph_from_place(place, network_type=\"drive\")" ] }, { @@ -63,7 +63,7 @@ "source": [ "# get node colors by linearly mapping an attribute's values to a colormap\n", "nc = ox.plot.get_node_colors_by_attr(G, attr=\"y\", cmap=\"plasma\")\n", - "fig, ax = ox.plot_graph(G, node_color=nc, edge_linewidth=0.3)" + "fig, ax = ox.plot.plot_graph(G, node_color=nc, edge_linewidth=0.3)" ] }, { @@ -80,7 +80,7 @@ "ec = ox.plot.get_edge_colors_by_attr(G, attr=\"length\")\n", "\n", "# plot the graph with colored edges\n", - "fig, ax = ox.plot_graph(G, node_size=5, edge_color=ec, bgcolor=\"k\")" + "fig, ax = ox.plot.plot_graph(G, node_size=5, edge_color=ec, bgcolor=\"k\")" ] }, { @@ -98,7 +98,7 @@ "metadata": {}, "outputs": [], "source": [ - "fig, ax = ox.plot_graph(\n", + "fig, ax = ox.plot.plot_graph(\n", " G,\n", " ax=None, # optionally draw on pre-existing axis\n", " figsize=(8, 8), # figure size to create if ax is None\n", @@ -135,10 +135,10 @@ "metadata": {}, "outputs": [], "source": [ - "Gc = ox.consolidate_intersections(ox.project_graph(G), dead_ends=True)\n", - "c = ox.graph_to_gdfs(G, edges=False).unary_union.centroid\n", + "Gc = ox.simplification.consolidate_intersections(ox.projection.project_graph(G), dead_ends=True)\n", + "c = ox.convert.graph_to_gdfs(G, edges=False).union_all().centroid\n", "bbox = ox.utils_geo.bbox_from_point(point=(c.y, c.x), dist=200, project_utm=True)\n", - "fig, ax = ox.plot_graph(\n", + "fig, ax = ox.plot.plot_graph(\n", " Gc,\n", " figsize=(5, 5),\n", " bbox=bbox,\n", @@ -157,7 +157,7 @@ "outputs": [], "source": [ "# or save a figure to disk instead of showing it\n", - "fig, ax = ox.plot_graph(G, filepath=\"./images/image.png\", save=True, show=False, close=True)" + "fig, ax = ox.plot.plot_graph(G, filepath=\"./images/image.png\", save=True, show=False, close=True)" ] }, { @@ -174,17 +174,17 @@ "outputs": [], "source": [ "# impute missing edge speeds and calculate free-flow travel times\n", - "G = ox.add_edge_speeds(G)\n", - "G = ox.add_edge_travel_times(G)\n", + "G = ox.routing.add_edge_speeds(G)\n", + "G = ox.routing.add_edge_travel_times(G)\n", "\n", "# calculate 3 shortest paths, minimizing travel time\n", "w = \"travel_time\"\n", "orig, dest = list(G)[10], list(G)[-10]\n", - "route1 = ox.shortest_path(G, orig, dest, weight=w)\n", + "route1 = ox.routing.shortest_path(G, orig, dest, weight=w)\n", "orig, dest = list(G)[0], list(G)[-1]\n", - "route2 = ox.shortest_path(G, orig, dest, weight=w)\n", + "route2 = ox.routing.shortest_path(G, orig, dest, weight=w)\n", "orig, dest = list(G)[-100], list(G)[100]\n", - "route3 = ox.shortest_path(G, orig, dest, weight=w)" + "route3 = ox.routing.shortest_path(G, orig, dest, weight=w)" ] }, { @@ -200,7 +200,7 @@ "metadata": {}, "outputs": [], "source": [ - "fig, ax = ox.plot_graph_route(G, route1, orig_dest_size=0, node_size=0)" + "fig, ax = ox.plot.plot_graph_route(G, route1, orig_dest_size=0, node_size=0)" ] }, { @@ -210,7 +210,7 @@ "outputs": [], "source": [ "# you can also pass any ox.plot_graph parameters as additional keyword args\n", - "fig, ax = ox.plot_graph_route(G, route1, save=True, show=False, close=True)" + "fig, ax = ox.plot.plot_graph_route(G, route1, save=True, show=False, close=True)" ] }, { @@ -230,7 +230,7 @@ "source": [ "routes = [route1, route2, route3]\n", "rc = [\"r\", \"y\", \"c\"]\n", - "fig, ax = ox.plot_graph_routes(G, routes, route_colors=rc, route_linewidth=6, node_size=0)" + "fig, ax = ox.plot.plot_graph_routes(G, routes, route_colors=rc, route_linewidth=6, node_size=0)" ] }, { @@ -248,7 +248,7 @@ "metadata": {}, "outputs": [], "source": [ - "G2 = ox.graph_from_address(\"Piedmont, CA, USA\", dist=200, network_type=\"drive\")\n", + "G2 = ox.graph.graph_from_address(\"Piedmont, CA, USA\", dist=200, network_type=\"drive\")\n", "G2 = ox.convert.to_undirected(G2)" ] }, @@ -258,8 +258,8 @@ "metadata": {}, "outputs": [], "source": [ - "fig, ax = ox.plot_graph(G2, edge_linewidth=3, node_size=0, show=False, close=False)\n", - "for _, edge in ox.graph_to_gdfs(G2, nodes=False).fillna(\"\").iterrows():\n", + "fig, ax = ox.plot.plot_graph(G2, edge_linewidth=3, node_size=0, show=False, close=False)\n", + "for _, edge in ox.convert.graph_to_gdfs(G2, nodes=False).fillna(\"\").iterrows():\n", " text = edge[\"name\"]\n", " c = edge[\"geometry\"].centroid\n", " ax.annotate(text, (c.x, c.y), c=\"y\")\n", @@ -282,7 +282,7 @@ "outputs": [], "source": [ "# get all the building footprints in a city\n", - "gdf = ox.features_from_place(\"Piedmont, California, USA\", {\"building\": True})\n", + "gdf = ox.features.features_from_place(\"Piedmont, California, USA\", {\"building\": True})\n", "gdf.shape" ] }, @@ -292,7 +292,7 @@ "metadata": {}, "outputs": [], "source": [ - "fig, ax = ox.plot_footprints(gdf)" + "fig, ax = ox.plot.plot_footprints(gdf)" ] }, { @@ -302,8 +302,8 @@ "outputs": [], "source": [ "# or plot street network and the geospatial features' footprints together\n", - "fig, ax = ox.plot_footprints(gdf, alpha=0.4, show=False)\n", - "fig, ax = ox.plot_graph(G, ax=ax, node_size=0, edge_color=\"w\", edge_linewidth=0.7)" + "fig, ax = ox.plot.plot_footprints(gdf, alpha=0.4, show=False)\n", + "fig, ax = ox.plot.plot_graph(G, ax=ax, node_size=0, edge_color=\"w\", edge_linewidth=0.7)" ] }, { @@ -330,7 +330,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/16-download-osm-geospatial-features.ipynb b/notebooks/16-download-osm-geospatial-features.ipynb index cbae85b..aa16ea4 100644 --- a/notebooks/16-download-osm-geospatial-features.ipynb +++ b/notebooks/16-download-osm-geospatial-features.ipynb @@ -25,6 +25,7 @@ "outputs": [], "source": [ "import osmnx as ox\n", + "import pandas as pd\n", "\n", "ox.__version__" ] @@ -53,7 +54,7 @@ "# `True` means retrieve any object with this tag, regardless of value\n", "place = \"SoHo, New York, NY\"\n", "tags = {\"building\": True}\n", - "gdf = ox.features_from_place(place, tags)\n", + "gdf = ox.features.features_from_place(place, tags)\n", "gdf.shape" ] }, @@ -63,7 +64,7 @@ "metadata": {}, "outputs": [], "source": [ - "fig, ax = ox.plot_footprints(gdf, figsize=(3, 3))" + "fig, ax = ox.plot.plot_footprints(gdf, figsize=(3, 3))" ] }, { @@ -75,7 +76,7 @@ "# get all the parks in some neighborhood\n", "# constrain acceptable `leisure` tag values to `park`\n", "tags = {\"leisure\": \"park\"}\n", - "gdf = ox.features_from_place(place, tags)\n", + "gdf = ox.features.features_from_place(place, tags)\n", "gdf.shape" ] }, @@ -89,7 +90,7 @@ "# and everything tagged landuse = retail or commercial,\n", "# and everything tagged highway = bus_stop\n", "tags = {\"amenity\": True, \"landuse\": [\"retail\", \"commercial\"], \"highway\": \"bus_stop\"}\n", - "gdf = ox.features_from_place(\"Piedmont, California, USA\", tags)\n", + "gdf = ox.features.features_from_place(\"Piedmont, California, USA\", tags)\n", "gdf.shape" ] }, @@ -113,6 +114,42 @@ "gdf[gdf[\"highway\"] == \"bus_stop\"].dropna(axis=1, how=\"any\").head()" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Attach features to nearest nodes\n", + "\n", + "For example, attach parking information to nearest network nodes." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# first create the graph and the features\n", + "place = \"Piedmont, CA, USA\"\n", + "G = ox.graph.graph_from_place(place, network_type=\"drive\")\n", + "features = ox.features.features_from_place(place, {\"amenity\": \"parking\"})" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# then attach your features to your graph's nearest nodes as attributes\n", + "feature_points = features.representative_point()\n", + "nn = ox.distance.nearest_nodes(G, feature_points.x, feature_points.y)\n", + "useful_tags = [\"access\", \"parking\", \"surface\", \"capacity\", \"fee\"]\n", + "for node, feature in zip(nn, features[useful_tags].to_dict(orient=\"records\")):\n", + " feature = {k: v for k, v in feature.items() if pd.notna(v)}\n", + " G.nodes[node].update({\"parking\": feature})" + ] + }, { "cell_type": "code", "execution_count": null, @@ -137,7 +174,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, diff --git a/notebooks/17-street-network-orientations.ipynb b/notebooks/17-street-network-orientations.ipynb index 318f315..7d2e201 100644 --- a/notebooks/17-street-network-orientations.ipynb +++ b/notebooks/17-street-network-orientations.ipynb @@ -74,7 +74,7 @@ "outputs": [], "source": [ "# verify OSMnx geocodes each query to what you expect (i.e., a [multi]polygon geometry)\n", - "gdf = ox.geocode_to_gdf(list(places.values()))\n", + "gdf = ox.geocoder.geocode_to_gdf(list(places.values()))\n", "gdf" ] }, @@ -96,9 +96,9 @@ " print(ox.utils.ts(), place)\n", "\n", " # get undirected graphs with edge bearing attributes\n", - " G = ox.graph_from_place(place, network_type=\"drive\")\n", - " Gu = ox.add_edge_bearings(ox.convert.to_undirected(G))\n", - " fig, ax = ox.plot_orientation(Gu, ax=ax, title=place, area=True)\n", + " G = ox.graph.graph_from_place(place, network_type=\"drive\")\n", + " Gu = ox.bearing.add_edge_bearings(ox.convert.to_undirected(G))\n", + " fig, ax = ox.plot.plot_orientation(Gu, ax=ax, title=place, area=True)\n", "\n", "# add figure title and save image\n", "suptitle_font = {\n", @@ -145,7 +145,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.3" + "version": "3.12.8" } }, "nbformat": 4, From 9fab273a1d43dbe37b6d0369808585af5c537eca Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 15:50:27 -0800 Subject: [PATCH 8/9] add custom_filter union example --- .../08-custom-filters-infrastructure.ipynb | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/notebooks/08-custom-filters-infrastructure.ipynb b/notebooks/08-custom-filters-infrastructure.ipynb index a316a8c..576530c 100644 --- a/notebooks/08-custom-filters-infrastructure.ipynb +++ b/notebooks/08-custom-filters-infrastructure.ipynb @@ -113,6 +113,42 @@ "fig, ax = ox.plot.plot_graph(G, node_size=0, edge_color=\"w\", edge_linewidth=0.2)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Unions in custom filters\n", + "\n", + "You can pass a list via `custom_filter` to perform a union of multiple queries (i.e., an \"or\" operation)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# add useful OSM tags to the default list, to retain them\n", + "ox.settings.useful_tags_way.extend([\"cycleway\", \"cycleway:left\", \"cycleway:right\"])\n", + "place = \"Bologna, Italia\"\n", + "\n", + "# get all ways with a 'cycleway:left'\n", + "cf1 = '[\"cycleway:left\"]'\n", + "G1 = ox.graph.graph_from_place(place, custom_filter=cf1, retain_all=True)\n", + "print(len(G1))\n", + "\n", + "# get all ways with a 'cycleway:right' tag\n", + "cf2 = '[\"cycleway:right\"]'\n", + "G2 = ox.graph.graph_from_place(place, custom_filter=cf2, retain_all=True)\n", + "print(len(G2))\n", + "\n", + "# union: get all ways with either a 'cycleway:right' or 'cycleway:left' tag\n", + "cf = [cf1, cf2]\n", + "ox.settings.useful_tags_way.extend([cf1, cf2])\n", + "G = ox.graph.graph_from_place(place, custom_filter=cf, retain_all=True)\n", + "print(len(G))" + ] + }, { "cell_type": "code", "execution_count": null, From ece39a9d94843cb2fabd4b5098ee011a36cd0fb8 Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Wed, 18 Dec 2024 16:58:04 -0800 Subject: [PATCH 9/9] add network-constrained clustering example --- .../18-network-constrained-clustering.ipynb | 310 ++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 notebooks/18-network-constrained-clustering.ipynb diff --git a/notebooks/18-network-constrained-clustering.ipynb b/notebooks/18-network-constrained-clustering.ipynb new file mode 100644 index 0000000..796c75b --- /dev/null +++ b/notebooks/18-network-constrained-clustering.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Network-Constrained Spatial Clustering\n", + "\n", + "Author: [Geoff Boeing](https://geoffboeing.com/)\n", + "\n", + "Cluster a set of firms based on their network distances from each other. That is, two locations may be close to each other spatially, but are they close to each other along the network that constrains travel?\n", + "\n", + " - [Documentation](https://osmnx.readthedocs.io/)\n", + " - [Journal article and citation info](https://geoffboeing.com/publications/osmnx-paper/)\n", + " - [Code repository](https://github.com/gboeing/osmnx)\n", + " - [Examples gallery](https://github.com/gboeing/osmnx-examples)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import networkx as nx\n", + "import numpy as np\n", + "import osmnx as ox\n", + "import pandas as pd\n", + "from scipy.sparse import csr_matrix\n", + "from sklearn.cluster import DBSCAN\n", + "from sklearn.neighbors import sort_graph_by_row_values" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# model the street network\n", + "latlon = (37.8226, -122.2340)\n", + "G = ox.graph.graph_from_point(latlon, dist=1500, network_type=\"drive\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create a fake set of firms" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# fake data: create n_firms randomly distributed across 3 firm centers\n", + "n_firms = 30\n", + "center_latlons = [(37.8175, -122.2316), (37.8216, -122.2439), (37.8268, -122.2286)]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# randomly scatter the firms around the centers\n", + "np.random.seed(0)\n", + "scale = 0.001\n", + "size = int(n_firms / len(center_latlons))\n", + "firm_lats = []\n", + "firm_lons = []\n", + "for lat, lon in center_latlons:\n", + " firm_lons.extend(np.random.normal(loc=lon, scale=scale, size=size))\n", + " firm_lats.extend(np.random.normal(loc=lat, scale=scale, size=size))\n", + "\n", + "firms = pd.DataFrame({\"lat\": firm_lats, \"lon\": firm_lons})\n", + "len(firms)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# plot the firms and the points around which they cluster\n", + "fig, ax = ox.plot.plot_graph(G, node_color=\"#aaaaaa\", node_size=0, show=False, close=True)\n", + "ax.scatter(x=firms[\"lon\"], y=firms[\"lat\"], c=\"w\", marker=\".\", s=100, zorder=2)\n", + "fig.canvas.draw()\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Regular spatial clustering with DBSCAN" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# parameterize DBSCAN\n", + "eps = 300 # meters\n", + "minpts = 3 # smallest cluster size allowed" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# compute DBSCAN using great-circle distances\n", + "eps_rad = eps / 3671000.0 # meters to radians\n", + "db = DBSCAN(eps=eps_rad, min_samples=minpts, metric=\"haversine\", algorithm=\"ball_tree\")\n", + "firms[\"spatial_cluster\"] = db.fit_predict(np.deg2rad(firms[[\"lat\", \"lon\"]]))\n", + "len(firms[\"spatial_cluster\"].unique())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# plot firms by cluster\n", + "color_map = {-1: \"w\", 0: \"y\", 1: \"r\", 2: \"c\", 3: \"b\"}\n", + "point_colors = [color_map[c] for c in firms[\"spatial_cluster\"]]\n", + "fig, ax = ox.plot.plot_graph(G, node_size=0, show=False, close=True)\n", + "ax.scatter(x=firms[\"lon\"], y=firms[\"lat\"], c=point_colors, marker=\".\", s=100, zorder=2)\n", + "fig.canvas.draw()\n", + "fig" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Create network-constrained distance matrix\n", + "\n", + "Speed up the distance matrix computation: rather than calculating every firm to every firm, find every node with at least 1 firm attached, then calculate every such node to every such node distance. Once we have the node-to-node distances, reindex it to make those distances firm-to-firm." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# attach nearest network node to each firm\n", + "firms[\"nn\"] = ox.distance.nearest_nodes(G, X=firms[\"lon\"], Y=firms[\"lat\"])\n", + "len(firms[\"nn\"])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# get distances for each pair of nodes that have firms attached to them\n", + "nodes_unique = pd.Series(firms[\"nn\"].unique())\n", + "nodes_unique.index = nodes_unique.values\n", + "len(nodes_unique)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def network_distance_matrix(u, D, vs=nodes_unique):\n", + " dists = (nx.dijkstra_path_length(D, source=u, target=v, weight=\"length\") for v in vs)\n", + " return pd.Series(dists, index=vs)\n", + "\n", + "\n", + "# create node-based distance matrix\n", + "# convert DiGraph for simpler faster distance matrix calculation\n", + "D = ox.convert.to_digraph(G, weight=\"length\")\n", + "node_dm = nodes_unique.apply(network_distance_matrix, D=D)\n", + "node_dm = node_dm.astype(int)\n", + "node_dm.size" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Make distance matrix sparse\n", + "\n", + "In a regular distance matrix, zero elements are considered neighbors (they're on top of each other). With a sparse matrix only nonzero elements may be considered neighbors for DBSCAN. First, make all zeros a very small number instead, so we don't ignore them. Otherwise, we wouldn't consider two firms attached to the same node as cluster neighbors. Then set everything bigger than epsilon to 0, so we do ignore it as we won't consider them neighbors anyway." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "node_dm[node_dm == 0] = 1\n", + "node_dm[node_dm > eps] = 0" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# reindex node-based distance matrix to create network-based distance matrix\n", + "net_dm = node_dm.reindex(index=firms[\"nn\"], columns=firms[\"nn\"])\n", + "net_dm.size" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# convert network-based distance matrix to a sparse matrix\n", + "net_dm_sparse = sort_graph_by_row_values(csr_matrix(net_dm), warn_when_not_sorted=False)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Cluster firms along the network\n", + "\n", + "Use the sparse network-based distance matrix to compute DBSCAN (converges much faster and uses much less memory than using the dense matrix with a big data set)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# use metric=precomputed to fit model to the sparse network-based distance matrix\n", + "db = DBSCAN(eps=eps, min_samples=minpts, metric=\"precomputed\")\n", + "firms[\"network_cluster\"] = db.fit_predict(net_dm_sparse)\n", + "len(firms[\"network_cluster\"].unique())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# plot firms by cluster\n", + "color_map = {-1: \"w\", 0: \"y\", 1: \"r\", 2: \"c\", 3: \"m\"}\n", + "point_colors = [color_map[c] for c in firms[\"network_cluster\"]]\n", + "ns = [50 if n in firms[\"nn\"].values else 0 for n in G.nodes()]\n", + "fig, ax = ox.plot.plot_graph(G, node_color=\"gray\", node_size=0, show=False, close=True)\n", + "ax.scatter(x=firms[\"lon\"], y=firms[\"lat\"], c=point_colors, marker=\".\", s=100, zorder=3)\n", + "fig.canvas.draw()\n", + "fig" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# compare firms' spatial clusters to network-based clusters\n", + "firms = firms.reindex(columns=[\"lon\", \"lat\", \"nn\", \"spatial_cluster\", \"network_cluster\"])\n", + "firms.iloc[4:9]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "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.12.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}