Example 9 - Constructing the Voronoi diagram

Example 9 - Constructing the Voronoi diagram#

The dual of a Delaunay triangulation is its Voronoi diagram. Stripy can efficiently calculate the voronoi points from a triangulation and, optionally, build the Voronoi regions for you.

Notebook contents#

import numpy as np
import stripy
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

%matplotlib inline
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 3
      1 import numpy as np
      2 import stripy
----> 3 import matplotlib.pyplot as plt
      4 import cartopy.crs as ccrs
      6 get_ipython().run_line_magic('matplotlib', 'inline')

ModuleNotFoundError: No module named 'matplotlib'
## Set up a coarse mesh for visual clarity

mesh = stripy.spherical_meshes.icosahedral_mesh(refinement_levels=3)
proj_map = ccrs.Orthographic()
proj_flat = ccrs.PlateCarree()


lons = np.degrees(mesh.lons)
lats = np.degrees(mesh.lats)


fig = plt.figure(figsize=(10, 8), facecolor="none")
ax = fig.add_subplot(111, projection=proj_map, title="Delaunay triangulation")

ax.triplot(lons, lats, mesh.simplices, c='LightGrey', zorder=1, transform=proj_flat)
ax.scatter(lons, lats, c='C0', marker='.', zorder=2, transform=proj_flat)

plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[3], line 1
----> 1 proj_map = ccrs.Orthographic()
      2 proj_flat = ccrs.PlateCarree()
      5 lons = np.degrees(mesh.lons)

NameError: name 'ccrs' is not defined

Voronoi points#

A voronoi point (or circumcentre) exists for every triangle in the mesh. They are calculated by finding a constant radius (the circumradius) that is shared between each point in the triangle. Depending on the mesh, some voronoi points will reside outside the area contained within a triangle.

To calculate the Voronoi points from the mesh, use the voronoi_points method. This method may optionally return the circumradius, signed triangle area, or aspect ratio for each voronoi point.

vor_lons, vor_lats = mesh.voronoi_points()
vlons = np.degrees(vor_lons)
vlats = np.degrees(vor_lats)


fig = plt.figure(figsize=(10, 8), facecolor="none")
ax = fig.add_subplot(111, projection=proj_map, title="Delaunay triangulation + Voronoi points")

ax.axis('off')
ax.triplot(lons, lats, mesh.simplices, c='LightGrey', zorder=1, transform=proj_flat)
ax.scatter(lons, lats, c='C0', marker='.', zorder=2, transform=proj_flat)
ax.scatter(vlons, vlats, c='C1', marker='*', zorder=3, transform=proj_flat)

plt.show()
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[4], line 6
      2 vlons = np.degrees(vor_lons)
      3 vlats = np.degrees(vor_lats)
----> 6 fig = plt.figure(figsize=(10, 8), facecolor="none")
      7 ax = fig.add_subplot(111, projection=proj_map, title="Delaunay triangulation + Voronoi points")
      9 ax.axis('off')

NameError: name 'plt' is not defined

Voronoi regions#

Often it is desirable to obtain the corresponding region enclosed by voronoi points. We can easily find the line segments connecting each voronoi point by retrieving the neighbour simplices.

neighbours = mesh.neighbour_simplices()

circumcentres = np.column_stack([vlons, vlats])
voronoi_edges = circumcentres[neighbours]
voronoi_edges[neighbours == -1] = np.nan # remove edges at infinity


lines = []
lines.extend(zip(circumcentres, voronoi_edges[:,0,:]))
lines.extend(zip(circumcentres, voronoi_edges[:,1,:]))
lines.extend(zip(circumcentres, voronoi_edges[:,2,:]))
# Plot it
from matplotlib.collections import LineCollection

linesC = LineCollection(lines, edgecolor='k', transform=proj_flat)

fig = plt.figure(figsize=(10, 8), facecolor="none")
ax = fig.add_subplot(111, projection=proj_map, title='Voronoi diagram')

ax.scatter(lons, lats, c='C0', marker='.', zorder=2, transform=proj_flat)
ax.scatter(vlons, vlats, c='C1', marker='*', zorder=3, transform=proj_flat)
ax.add_collection(linesC)

plt.show()
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[6], line 2
      1 # Plot it
----> 2 from matplotlib.collections import LineCollection
      4 linesC = LineCollection(lines, edgecolor='k', transform=proj_flat)
      6 fig = plt.figure(figsize=(10, 8), facecolor="none")

ModuleNotFoundError: No module named 'matplotlib'

Looks good! But in order to find which voronoi points belong to a region, we need to iterate through all of the triangles. Since each vertex in the Delaunay represents a voronoi “site”, we can accumulate all of the circumcentres into a list and sort them using voronoi_points_and_regions.

vor_lons, vor_lats, regions = mesh.voronoi_points_and_regions()

linesC = LineCollection(lines, edgecolor='k', transform=proj_flat)

fig = plt.figure(figsize=(10, 8), facecolor="none")
ax = fig.add_subplot(111, projection=proj_map, title='Voronoi diagram')

ax.scatter(lons, lats, c='C0', marker='.', zorder=2, transform=proj_flat)
ax.scatter(vlons, vlats, c='C1', marker='*', zorder=3, transform=proj_flat)
ax.add_collection(linesC)

# highlight specific region
r = 1
region = regions[r]

# iterate through region and connect up the points
for i in range(len(region)):
    i0 = region[i - 1]
    i1 = region[i]

    ilon = [vlons[i0], vlons[i1]]
    ilat = [vlats[i0], vlats[i1]]
    ax.plot(ilon, ilat, c='r', linewidth=5, transform=proj_flat)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[7], line 3
      1 vor_lons, vor_lats, regions = mesh.voronoi_points_and_regions()
----> 3 linesC = LineCollection(lines, edgecolor='k', transform=proj_flat)
      5 fig = plt.figure(figsize=(10, 8), facecolor="none")
      6 ax = fig.add_subplot(111, projection=proj_map, title='Voronoi diagram')

NameError: name 'LineCollection' is not defined