Plotting Guide

Nereus provides powerful plotting capabilities for visualizing unstructured model data on maps and as vertical transects.

2D Map Plotting

The plot() function is the primary tool for creating map visualizations:

import nereus as nr

fig, ax, interpolator = nr.plot(
    data, lon, lat,
    projection="rob",
    cmap="viridis"
)

Basic Parameters

dataarray-like

Array of values at each mesh point. Can be 1D or 2D. If xarray DataArray, coordinates may be extracted automatically.

lon, latarray-like, optional

Longitude and latitude coordinates (in degrees). Can be 1D or 2D. If not provided, will attempt to extract from xarray DataArray.

Flexible Input Formats

Nereus accepts various input formats and automatically handles the conversion:

Data

Lon/Lat

Behavior

1D

1D (same size)

Used directly (no warning)

2D

2D (same shape)

All raveled to 1D (warning issued)

1D

2D

Lon/lat raveled to match data (warning issued)

2D

1D

Meshgrid created, then all raveled (warning issued)

Example with 2D regular grid data:

# 2D data with 1D coordinates (like from NetCDF)
data_2d = np.random.rand(180, 360)
lon_1d = np.linspace(-179.5, 179.5, 360)
lat_1d = np.linspace(-89.5, 89.5, 180)

# Nereus automatically creates meshgrid internally
fig, ax, _ = nr.plot(data_2d, lon_1d, lat_1d)

Automatic Coordinate Extraction

When working with xarray DataArrays, coordinates can be extracted automatically:

import xarray as xr

# Load data with coordinates
ds = xr.open_dataset("ocean_data.nc")
temp = ds.temperature.isel(time=0, depth=0)

# No need to specify lon/lat - extracted automatically
fig, ax, _ = nr.plot(temp)

Nereus recognizes common coordinate names:

  • Longitude: lon, longitude, x, nav_lon, glon, xt_ocean, etc.

  • Latitude: lat, latitude, y, nav_lat, glat, yt_ocean, etc.

You can also override one coordinate while extracting the other:

# Use custom lon, extract lat from xarray
fig, ax, _ = nr.plot(temp, lon=custom_lon)
projectionstr, default “pc”

Map projection to use. See Map Projections below.

extenttuple, optional

Map extent as (lon_min, lon_max, lat_min, lat_max).

resolutionfloat or tuple, default 1.0

Target grid resolution. Either degrees (float) or (nlon, nlat).

Appearance Parameters

cmapstr, default “viridis”

Matplotlib colormap name.

vmin, vmaxfloat, optional

Color scale limits. If not specified, uses data min/max.

coastlinesbool, default True

Draw coastlines on the map.

landbool, default False

Fill land areas with gray color.

gridlinesbool, default False

Draw latitude/longitude gridlines.

colorbarbool, default True

Add a colorbar to the plot.

colorbar_labelstr, optional

Label for the colorbar.

titlestr, optional

Plot title.

figsizetuple, optional

Figure size as (width, height) in inches.

Advanced Parameters

axmatplotlib Axes, optional

Existing axes to plot on. Must be a Cartopy GeoAxes.

interpolatorRegridInterpolator, optional

Pre-computed interpolator to reuse.

method"nearest" or "linear", default "nearest"

Interpolation method. "linear" uses Delaunay triangulation for smoother results (slower).

influence_radiusfloat, default 80000.0

Maximum distance (meters) from data points for interpolation.

use_cachebool, default True

Use cached interpolator if available.

Map Projections

Nereus supports multiple map projections via short aliases:

Alias

Projection

Description

"pc"

PlateCarree

Simple equirectangular projection. Good default for regional maps.

"rob"

Robinson

Compromise projection for global maps. Good for world maps.

"merc"

Mercator

Conformal projection. Preserves angles but distorts area at high latitudes.

"moll"

Mollweide

Equal-area projection. Good for showing global distributions.

"np"

North Polar Stereographic

Centered on North Pole. Ideal for Arctic visualization.

"sp"

South Polar Stereographic

Centered on South Pole. Ideal for Antarctic visualization.

"ortho"

Orthographic

View of Earth from space. Requires central_longitude and central_latitude.

"lcc"

Lambert Conformal

Good for mid-latitude regions with east-west extent.

Examples with different projections:

# Global Robinson projection
nr.plot(data, lon, lat, projection="rob")

# Arctic view
nr.plot(data, lon, lat, projection="np")

# Antarctic view
nr.plot(data, lon, lat, projection="sp")

# Regional PlateCarree with extent
nr.plot(data, lon, lat, projection="pc",
        extent=(-80, 0, 0, 60))  # North Atlantic

# Orthographic (globe view)
nr.plot(data, lon, lat, projection="ortho",
        central_longitude=-30, central_latitude=45)

Polar Projections

For polar projections ("np" and "sp"), Nereus automatically:

  • Expands the data bounds to avoid edge artifacts

  • Uses circular map boundary

  • Sets appropriate extent

# Arctic sea ice
fig, ax, _ = nr.plot(
    ice_concentration, lon, lat,
    projection="np",
    cmap="Blues",
    vmin=0, vmax=1,
    title="Arctic Sea Ice Concentration"
)

Reusing Interpolators

For efficiency when plotting multiple fields on the same mesh:

# First plot creates the interpolator
fig1, ax1, interp = nr.plot(temp, lon, lat, projection="rob")

# Subsequent plots reuse it (much faster)
fig2, ax2, _ = nr.plot(salt, lon, lat, projection="rob", interpolator=interp)
fig3, ax3, _ = nr.plot(speed, lon, lat, projection="rob", interpolator=interp)

Creating Animations

Efficiently create frame sequences:

import matplotlib.pyplot as plt

interpolator = None
for t in range(len(ds.time)):
    data = ds.temp.isel(time=t, nz1=0).values

    fig, ax, interpolator = nr.plot(
        data, lon, lat,
        projection="rob",
        interpolator=interpolator,  # Reuse after first frame
        vmin=-2, vmax=30,
        cmap="RdBu_r",
        title=f"Time: {t}"
    )

    plt.savefig(f"frame_{t:04d}.png", dpi=150, bbox_inches="tight")
    plt.close()

Vertical Transects

The transect() function creates vertical cross-sections along a great circle path:

fig, ax = nr.transect(
    data_3d, lon, lat, depth,
    start=(lon1, lat1),
    end=(lon2, lat2),
    n_points=200
)

Parameters

dataarray-like

Data array with one of the following shapes:

  • (nlevels, npoints) - 2D array for unstructured meshes

  • (nlevels, nlat, nlon) - 3D array for regular grids (automatically reshaped)

If xarray DataArray, coordinates may be extracted automatically.

lon, latarray-like

Coordinate arrays. Can be:

  • 1D arrays of same size (unstructured mesh coordinates)

  • 1D arrays of different sizes (regular grid side coordinates - meshgrid created internally)

  • 2D arrays of same shape (full coordinate arrays - raveled internally)

deptharray-like

1D array of depth levels (positive downward).

start, endtuple

Start and end points as (lon, lat) tuples.

n_pointsint, default 100

Number of points along the transect.

depth_limtuple, optional

Depth limits as (min, max).

invert_depthbool, default True

Invert y-axis so depth increases downward (ocean convention). Set to False for atmosphere (height increases upward).

Flexible Input Formats

Nereus automatically handles various input combinations:

Data Shape

Lon/Lat

Behavior

(nlevels, npoints)

1D (same size)

Used directly

(nlevels, nlat, nlon)

1D (different sizes)

Data reshaped to 2D, meshgrid created for coordinates

(nlevels, npoints)

2D (same shape)

Coordinates raveled to 1D

Example with 3D regular grid data:

# 3D data with shape (depth, lat, lon)
# Common format from NetCDF files
temp = ds.temperature.values  # shape: (42, 173, 360)

fig, ax = nr.transect(
    temp,
    ds.lon.values,   # 1D: (360,)
    ds.lat.values,   # 1D: (173,)
    ds.depth.values,
    start=(-30, -60),
    end=(-30, 70),
    n_points=200
)

Example Transects

# Atlantic meridional transect
fig, ax = nr.transect(
    ds.temp.isel(time=0),
    ds.lon, ds.lat, ds.depth,
    start=(-30, 60),    # North Atlantic
    end=(-30, -60),     # South Atlantic
    n_points=300,
    cmap="RdBu_r",
    vmin=-2, vmax=25,
    depth_lim=(0, 5000),
    colorbar_label="Temperature (°C)",
    title="Atlantic Temperature Transect"
)

# Drake Passage transect
fig, ax = nr.transect(
    ds.u.isel(time=0),
    ds.lon, ds.lat, ds.depth,
    start=(-70, -55),
    end=(-55, -62),
    cmap="RdBu_r",
    depth_lim=(0, 3000),
    colorbar_label="Zonal Velocity (m/s)"
)

Multi-Panel Figures

Combining multiple plots:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs

fig = plt.figure(figsize=(14, 6))

# Temperature map
ax1 = fig.add_subplot(1, 2, 1, projection=ccrs.Robinson())
nr.plot(temp, lon, lat, ax=ax1, cmap="RdBu_r",
        title="Temperature", colorbar_label="°C")

# Salinity map
ax2 = fig.add_subplot(1, 2, 2, projection=ccrs.Robinson())
nr.plot(salt, lon, lat, ax=ax2, cmap="viridis",
        title="Salinity", colorbar_label="PSU")

plt.tight_layout()

Customizing Plots

The returned axes can be further customized:

fig, ax, _ = nr.plot(data, lon, lat, projection="rob")

# Add custom features
ax.set_title("Custom Title", fontsize=14, fontweight="bold")

# Add markers
ax.plot(-30, 45, 'r*', markersize=15, transform=ccrs.PlateCarree())

# Add text
ax.text(-30, 40, "Point A", transform=ccrs.PlateCarree(),
        fontsize=10, ha="center")

# Customize gridlines
gl = ax.gridlines(draw_labels=True, linewidth=0.5, alpha=0.5)
gl.top_labels = False
gl.right_labels = False

Handling Missing Data

Nereus handles NaN values automatically:

# NaN values in data are preserved in output
data_with_nans = np.where(land_mask, np.nan, data)
nr.plot(data_with_nans, lon, lat)

The influence_radius parameter also creates a natural mask where no source data exists:

# Smaller influence radius = tighter mask around data
nr.plot(data, lon, lat, influence_radius=50000)  # 50 km

# Larger influence radius = fills more gaps
nr.plot(data, lon, lat, influence_radius=200000)  # 200 km