Contour Demos#

Author: Dylan Nelson

Demos of the .plot.contour() method with unstructured grids in UXarray. The grids are the small test meshes of the UXarray repository. The plots are interactive and show the variety of ways that plots can be generated using this method.

from pathlib import Path

import cartopy.crs as ccrs
import geoviews.feature as gf
import holoviews as hv
import numpy as np
import xarray as xr

import uxarray as ux

MESH = Path("../../../test/meshfiles")

1. Cubed sphere: edge contours#

Dataset

Variable

Method

Contours

Color

Line width

Cubed sphere outCSne30, 5,400 quad cells

psi, on cells

edges (default)

7 levels (default)

black

default

uxds = ux.open_dataset(
    MESH / "ugrid/outCSne30/outCSne30.ug",
    MESH / "ugrid/outCSne30/outCSne30_vortex.nc",
)
psi = uxds["psi"]

contours = psi.plot.contour(color="black")

psi.plot(
    cmap="viridis",
    width=800,
    height=400,
    title="Cubed sphere, method='edges'",
) * contours

2. MPAS ocean: interpolated contours#

Dataset

Variable

Method

Contours

Color

Line width

MPAS ocean oQU480, 1,791 hexagon cells

bottomDepth, on cells

interpolated

2000 m and 4000 m

white

1

Using an ocean model where there are no land cells

mpas = ux.open_dataset(
    MESH / "mpas/QU/oQU480.231010.nc", MESH / "mpas/QU/oQU480.231010.nc"
)
depth = mpas["bottomDepth"]

contours = depth.plot.contour(
    levels=[2000, 4000],
    method="interpolated",
    color="white",
    line_width=1,
)

depth.plot(
    cmap="Blues",
    width=800,
    height=400,
    title="MPAS ocean depth (m), method='interpolated'",
) * contours

3. ICON triangles: edge contours, without labels#

Dataset

Variable

Method

Contours

Color

Line width

Labels

ICON R02B04, 20,480 triangle cells

wave (test field), on cells

edges (default)

about 6 levels

crimson

1

off

The same default method on a triangular grid. The lines zigzag along triangle edges. labels=False gives the lines without the numbered labels.

def wave(uxgrid):
    lon, lat = np.radians(uxgrid.face_lon.values), np.radians(uxgrid.face_lat.values)
    values = np.cos(lat) ** 2 * np.sin(3 * lon) + 0.5 * np.sin(lat)
    return ux.UxDataArray(
        xr.DataArray(values, dims="n_face", name="wave"), uxgrid=uxgrid
    )


icon = wave(ux.open_grid(MESH / "icon/R02B04/icon_grid_0010_R02B04_G.nc"))

contours = icon.plot.contour(
    levels=6,
    color="crimson",
    line_width=1,
    labels=False,
)

icon.plot(
    cmap="Greys",
    width=800,
    height=400,
    title="ICON triangles, method='edges', labels=False",
) * contours

4. Data on nodes: interpolated contours, colored by level#

Dataset

Variable

Method

Contours

Color

Line width

Geoflow, 6,000 nodes

v1, on nodes (first time and layer)

interpolated (default for node data)

−12, −6, 6, 12

by level, coolwarm, with colorbar

1.2

Edge contours need cells, so node data uses the interpolated method. Without a color, each line is colored by its level.

.plot() draws node data as points, so the shading behind the lines is the same variable averaged onto the cells with topological_mean(). Its colorbar is turned off, which leaves the colorbar of the lines.

geoflow = ux.open_dataset(
    MESH / "ugrid/geoflow-small/grid.nc", MESH / "ugrid/geoflow-small/v1.nc"
)
v1 = geoflow["v1"].isel(time=0, meshLayers=0)

contours = v1.plot.contour(
    levels=[-12, -6, 6, 12],
    cmap="coolwarm",
    colorbar=True,
    line_width=1.2,
    show_legend=False,
)

v1.topological_mean(destination="face").plot(
    cmap="Greys",
    colorbar=False,
    ylim=(-20, 20),
    width=800,
    height=400,
    title="Geoflow v1 on nodes, method='interpolated'",
) * contours

5. HEALPix in a map projection#

Dataset

Variable

Method

Contours

Color

Line width

Projection

HEALPix zoom 4, 3,072 cells

wave (test field), on cells

edges (default)

about 5 levels

dark orange

1.2

Robinson

With projection=, the contours and their labels come back as GeoViews objects and overlay on a projected plot and on coastlines. The shaded plot and the contours get the same projection.

healpix = wave(ux.Grid.from_healpix(zoom=4))
projection = ccrs.Robinson()

contours = healpix.plot.contour(
    levels=5, projection=projection, color="darkorange", line_width=1.2
)

(
    healpix.plot(
        projection=projection,
        cmap="cividis",
        width=800,
        height=400,
        title="HEALPix, method='edges', Robinson projection",
    )
    * contours
    * gf.coastline(projection=projection)
)

6. Changing the Number of Levels#

Dataset

Variable

Method

Contours

Color

Line width

Cubed sphere outCSne30

psi, on cells

edges (default)

about 3, 7 and 15 levels

black

default

levels as a number lets the user decide the quantity of levels, and the function cleanly decides where to place them. Alternatively, they can be provided as a list, with levels at each number in the list.

panels = []
for n in (3, 7, 15):
    contours = psi.plot.contour(levels=n, color="black")
    panels.append(
        psi.plot(
            cmap="viridis", colorbar=False, width=420, height=260, title=f"levels={n}"
        )
        * contours
    )

(panels[0] + panels[1] + panels[2]).cols(3)

7. Outlined levels vs Smoothed levels#

Dataset

Variable

Method

Contours

Color

Line width

Cubed sphere outCSne30

psi, on cells

edges, then interpolated

0.7, 1.0, 1.3

black

default

levels as a list draws lines at exactly those values.

chosen = [0.7, 1.0, 1.3]

panels = []
for method in ("edges", "interpolated"):
    contours = psi.plot.contour(levels=chosen, method=method, color="black")
    panels.append(
        psi.plot(
            cmap="viridis",
            colorbar=False,
            width=600,
            height=330,
            title=f"levels={chosen}, method='{method}'",
        )
        * contours
    )

(panels[0] + panels[1]).cols(2)

8. Across the antimeridian#

Dataset

Variable

Method

Contours

Color

Line width

Labels

Projection

MPAS ocean oQU480

bottomDepth, on cells

interpolated

2000 m and 4000 m

white

1

dark grey, bold

Robinson, centered on 180°

The lines continue across ±180° longitude. On a map centered on the Pacific that is the middle of the picture.

The labels take the color of the lines. hv.opts.Labels restyles them after the call, here to dark grey and bold. Many additional options can be provided through .opts. See Applying Customizations in the HoloViews documentation for how .opts works and how to list the options an element has.

pacific = ccrs.Robinson(central_longitude=180)

contours = depth.plot.contour(
    levels=[2000, 4000],
    method="interpolated",
    projection=pacific,
    color="white",
    line_width=1,
).opts(hv.opts.Labels(text_color="#333333", text_font_style="bold"))

(
    depth.plot(
        projection=pacific,
        cmap="Blues",
        width=800,
        height=400,
        title="MPAS ocean depth (m), centered on 180°",
    )
    * contours
    * gf.coastline(projection=pacific)
)