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_nNodes  (mesh2d_nNodes) int64 2kB 0 1 2 3 4 5 ... 212 213 214 215 216
  * mesh2d_nFaces  (mesh2d_nFaces) int64 3kB 0 1 2 3 4 5 ... 379 380 381 382 383
  * mesh2d_nEdges  (mesh2d_nEdges) int64 5kB 0 1 2 3 4 5 ... 595 596 597 598 599
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 0x7f605235f770>

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 0x7f6047830890>

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 0x7f6047bab3e0>

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 0x7f60477da3f0>

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 0x7f6051ed6630>

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 0x7f60527f6810>

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 0x7f605210a2a0>

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 0x7f60486f05f0>

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 0x7f60489b4b90>

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 0x7f6047bbd0a0>]

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

Gallery generated by Sphinx-Gallery