summaryrefslogtreecommitdiff
path: root/examples/geospatial/plot_delaunay.py
diff options
context:
space:
mode:
Diffstat (limited to 'examples/geospatial/plot_delaunay.py')
-rw-r--r--examples/geospatial/plot_delaunay.py72
1 files changed, 72 insertions, 0 deletions
diff --git a/examples/geospatial/plot_delaunay.py b/examples/geospatial/plot_delaunay.py
new file mode 100644
index 00000000..a94d46b5
--- /dev/null
+++ b/examples/geospatial/plot_delaunay.py
@@ -0,0 +1,72 @@
+"""
+=====
+Delaunay graphs from geographic points
+=====
+
+This example shows how to build a delaunay graph (plus its dual,
+the set of Voronoi polygons) from a set of points.
+For this, we will use the set of cholera cases at the Broad Street Pump,
+recorded by John Snow in 1853. The methods shown here can also work
+directly with polygonal data using their centroids as representative points.
+"""
+
+from libpysal import weights, examples
+from libpysal.cg import voronoi_frames
+from contextily import add_basemap
+import matplotlib.pyplot as plt
+import networkx as nx
+import numpy as np
+import geopandas
+
+# read in example data from a geopackage file. Geopackages
+# are a format for storing geographic data that is backed
+# by sqlite. geopandas reads data relying on the fiona package,
+# providing a high-level pandas-style interface to geographic data.
+# Many different kinds of geographic data formats can be read by geopandas.
+cases = geopandas.read_file("cholera_cases.gpkg")
+
+# In order for networkx to plot the nodes of our graph correctly, we
+# need to construct the array of coordinates for each point in our dataset.
+# To get this as a numpy array, we extract the x and y coordinates from the
+# geometry column.
+coordinates = np.column_stack((cases.geometry.x, cases.geometry.y))
+
+# While we could simply present the Delaunay graph directly, it is useful to
+# visualize the Delaunay graph alongside the Voronoi diagram. This is because
+# the two are intrinsically linked: the adjacency graph of the Voronoi diagram
+# is the Delaunay graph for the set of generator points! Put simply, this means
+# we can build the Voronoi diagram (relying on scipy.spatial for the underlying
+# computations), and then convert these polygons quickly into the Delaunay graph.
+# Be careful, though; our algorithm, by default, will clip the voronoi diagram to
+# the bounding box of the point pattern. This is controlled by the "clip" argument.
+cells, generators = voronoi_frames(coordinates, clip="convex hull")
+
+# With the voronoi polygons, we can construct the adjacency graph between them using
+# "Rook" contiguity. This represents voronoi cells as being adjacent if they share
+# an edge/face. This is an analogue to the "von Neuman" neighborhood, or the 4 cardinal
+# neighbors in a regular grid. The name comes from the directions a Rook piece can move
+# on a chessboard.
+delaunay = weights.Rook.from_dataframe(cells)
+
+# Once the graph is built, we can convert the graphs to networkx objects using the
+# relevant method.
+delaunay_graph = delaunay.to_networkx()
+
+# To plot with networkx, we need to merge the nodes back to
+# their positions in order to plot in networkx
+positions = dict(zip(delaunay_graph.nodes, coordinates))
+
+# Now, we can plot with a nice basemap.
+ax = cells.plot(facecolor="lightblue", alpha=0.50, edgecolor="cornsilk", linewidth=2)
+add_basemap(ax)
+ax.axis("off")
+nx.draw(
+ delaunay_graph,
+ positions,
+ ax=ax,
+ node_size=2,
+ node_color="k",
+ edge_color="k",
+ alpha=0.8,
+)
+plt.show()