Plot unstructured mesh data#

The labels that are present in xarray’s data structures allow for easy creation of informative plots: think of dates on the x-axis, or geospatial coordinates. Xarray provides a convenient way of plotting your data provided it is structured. Xugrid extends these plotting methods to easily make spatial (x-y) plots of unstructured grids.

Like Xarray’s focus for plotting is the DataArray, Xugrid’s focus is the UgridDataArray; like Xarray, if your (extracted) data fits into a pandas DataFrame, you’re better of using pandas tools instead.

As every other method in Xugrid, any logic involving the unstructured topology is accessed via the .ugrid accessor on the DataArrays and Datasets; UgridDatasets and UgridDataArrays behave the same as ordinary Xarray DataArrays and Datasets otherwise.

Imports#

The following imports suffice for the examples.

import matplotlib.pyplot as plt

import xugrid

We’ll use a simple synthetic example. This dataset contains data for all topological attributes of a two dimensional mesh:

  • Nodes: the coordinate pair (x, y) forming a point.

  • Edges: a line or curve bounded by two nodes.

  • Faces: the polygon enclosed by a set of edges.

In this disk example, very similar has been placed on the nodes, edges, and faces.

ds = xugrid.data.disk()
ds
<xarray.Dataset> Size: 19kB
Dimensions:        (mesh2d_nNodes: 217, mesh2d_nFaces: 384, mesh2d_nEdges: 600)
Coordinates:
  * mesh2d_nEdges  (mesh2d_nEdges) int64 5kB 0 1 2 3 4 5 ... 595 596 597 598 599
  * mesh2d_nFaces  (mesh2d_nFaces) int64 3kB 0 1 2 3 4 5 ... 379 380 381 382 383
  * mesh2d_nNodes  (mesh2d_nNodes) int64 2kB 0 1 2 3 4 5 ... 212 213 214 215 216
Data variables:
    node_z         (mesh2d_nNodes) float64 2kB 1.933 2.091 1.875 ... 5.688 7.491
    face_z         (mesh2d_nFaces) float64 3kB 1.737 1.918 2.269 ... 5.408 6.424
    edge_z         (mesh2d_nEdges) float64 5kB 1.989 1.875 1.8 ... 4.909 6.544


UgridDataArray#

Just like Xarray, we can create a plot by selecting a DataArray from the Dataset and calling the UgridDataArray.ugrid.plot() method.

uda = ds["face_z"]
uda.ugrid.plot()
plotting
<matplotlib.collections.PolyCollection object at 0x7f748afebaa0>

Like Xarray, the axes and the colorbar are labeled automatically using the available information.

The convenience function xugrid.UgridDataArray.ugrid.plot() dispatches on the topological dimension of the variable. In this case, the data is associated with the face dimension of the topology. Data located on the edges results in a different kind of plot:

ds["edge_z"].ugrid.plot()
plotting
<matplotlib.collections.LineCollection object at 0x7f748af82cf0>

The method called by default depends on the type of the data:

Dimension

Plotting function

Face

xugrid.plot.pcolormesh()

Edge

xugrid.plot.line()

Node

xugrid.plot.tripcolor()

We can put them side by side to illustrate the differences:

fig, (ax0, ax1, ax2) = plt.subplots(ncols=3, figsize=(11, 3), sharex=True, sharey=True)
ds["face_z"].ugrid.plot(ax=ax0)
ds["edge_z"].ugrid.plot(ax=ax1)
ds["node_z"].ugrid.plot(ax=ax2)
plotting
<matplotlib.collections.PolyCollection object at 0x7f748b24ff20>

We can also exactly control the type of plot we want. For example, to plot filled contours for data associated with the face dimension:

ds["face_z"].ugrid.plot.contourf()
plotting
<matplotlib.tri._tricontour.TriContourSet object at 0x7f748b2d94c0>

We can also overlay this data with the edges:

fig, ax = plt.subplots()
ds["face_z"].ugrid.plot.contourf()
ds["face_z"].ugrid.plot.line(color="black")
plotting
<matplotlib.collections.LineCollection object at 0x7f7495c432c0>

In general, there has to be data associated with the mesh topology before a plot can be made. plot.line() forms an exception to this rule, as the location of the edges is meaningful on its own: for this reason plot.line() does not error in the example above.

Other types of plot#

The available plotting methods per topology dimension are listed here.

For the face dimension:

For the edge dimension:

For the node dimension:

All these (2D) plots are illustrated here for completeness’ sake:

fig, axes = plt.subplots(nrows=5, ncols=3, figsize=(10, 15))

ds["face_z"].ugrid.plot.pcolormesh(ax=axes[0, 0])
ds["face_z"].ugrid.plot.contour(ax=axes[1, 0])
ds["face_z"].ugrid.plot.contourf(ax=axes[2, 0])
ds["face_z"].ugrid.plot.imshow(ax=axes[3, 0])
ds["face_z"].ugrid.plot.scatter(ax=axes[4, 0])

ds["edge_z"].ugrid.plot.line(ax=axes[0, 1])
ds["edge_z"].ugrid.plot.scatter(ax=axes[4, 1])

ds["node_z"].ugrid.plot.tripcolor(ax=axes[0, 2])
ds["node_z"].ugrid.plot.contour(ax=axes[1, 2])
ds["node_z"].ugrid.plot.contourf(ax=axes[2, 2])
ds["node_z"].ugrid.plot.scatter(ax=axes[4, 2])
plotting
<matplotlib.collections.PathCollection object at 0x7f748c3b83b0>

The surface methods generate 3D surface plots:

fig = plt.figure(figsize=plt.figaspect(0.5))
ax0 = fig.add_subplot(1, 2, 1, projection="3d")
ax1 = fig.add_subplot(1, 2, 2, projection="3d")
ds["face_z"].ugrid.plot.surface(ax=ax0)
ds["node_z"].ugrid.plot.surface(ax=ax1)
plotting
<mpl_toolkits.mplot3d.art3d.Poly3DCollection object at 0x7f748af6cda0>

Additional Arguments#

Once again like in Xarray, additional arguments are passed to the underlying matplotlib function and the additional arguments supported by Xarray can be used:

ds["face_z"].ugrid.plot(cmap="RdBu", levels=8, yincrease=False)
plotting
<matplotlib.collections.PolyCollection object at 0x7f748c34fcb0>

As a function#

The plotting methods can also be called as a function, in which case they take an xarray DataArray and a xugrid grid as arguments.

grid = ds.ugrid.grids[0]
da = ds.obj["face_z"]

xugrid.plot.pcolormesh(grid, da)
plotting
<matplotlib.collections.PolyCollection object at 0x7f748bf3b050>

Xarray DataArray plots#

As mentioned, apart from the .ugrid accessor, a UgridDataArray behaves the same as an Xarray DataArray. To illustrate, we can select a location somewhere in the unstructured topology, and plot the resulting timeseries:

ds = xugrid.data.adh_san_diego()
depth = ds["depth"]
depth.isel(node=1000).plot()
node_x = 4.84e+05, node_y = 3.614e+06, node = 1000
[<matplotlib.lines.Line2D object at 0x7f748be1f020>]

Total running time of the script: (0 minutes 13.984 seconds)

Gallery generated by Sphinx-Gallery