Altimetry — coastal sea-level anomaly time-series¶
Demonstrates the surface-only multi-mission altimetry pattern: pull
five years of daily DUACS L4 sea-level anomaly (sla) and absolute
dynamic topography (adt) over a coastal box, plot the box-mean SLA
time-series with a 30-day rolling average.
The DUACS L4 dataset
(cmems_obs-sl_glo_phy-ssh_my_allsat-l4-duacs-0.125deg_P1D) merges
every operational altimeter onto a regular 1/8° grid. It is
surface-only — the depth kwargs are silently ignored — so the request
stays compact.
Setup¶
Imports and output directory. pyramids provides NetCDF (reading the
downloaded cube); earthlens provides the unified EarthLens entry point
and the CMEMS Catalog.
import os
from pathlib import Path
import numpy as np
import xarray as xr
import matplotlib.pyplot as plt
from pyramids.netcdf import NetCDF
from earthlens import EarthLens
from earthlens.cmems import Catalog
OUT_DIR = Path('data/cmems-altimetry')
OUT_DIR.mkdir(parents=True, exist_ok=True)
The request¶
We target the DUACS L4 daily product, ask for sla and adt, and crop to a
box over the Iberian Atlantic coast.
DATASET_ID = 'cmems_obs-sl_glo_phy-ssh_my_allsat-l4-duacs-0.125deg_P1D'
VARIABLES = ['sla', 'adt']
BBOX = dict(lat_lim=[36.0, 41.0], lon_lim=[-9.5, -5.0]) # Iberian Atlantic coast
Is the dataset curated?¶
A quick look in the CMEMS Catalog: if the id is curated we print its cadence
and domain, otherwise we note that the request still works against the
uncurated id.
if DATASET_ID in Catalog().datasets:
ds = Catalog().get_dataset(DATASET_ID)
print(f'{DATASET_ID}: cadence={ds.cadence}, domain={ds.domain}')
else:
print(f'{DATASET_ID} is not curated — uncurated id, request will still work')
Download — five years of daily SLA + ADT, coastal box¶
Multi-year × multi-variable × surface-only. Both fields are scalar (time, lat, lon) arrays so the returned NetCDF is small. We build the request
first — source, dates, cadence, dataset, variables, the bounding box, and
the CMEMS service credentials from the environment.
earthlens = EarthLens(
data_source='cmems',
start='2018-01-01',
end='2022-12-31',
cadence='daily',
dataset=DATASET_ID,
variables=VARIABLES,
**BBOX,
path=str(OUT_DIR),
service_username=os.environ.get('COPERNICUSMARINE_SERVICE_USERNAME'),
service_password=os.environ.get('COPERNICUSMARINE_SERVICE_PASSWORD'),
)
Downloading is its own step so it is easy to read and re-run: download()
fetches the subset and returns the list of written NetCDF paths.
paths = earthlens.download()
print(paths)
Box-averaged SLA + ADT time-series¶
Read the downloaded cube with pyramids' NetCDF, then hand it to xarray
(via decode_cf) for labelled (time, latitude, longitude) access.
nc = NetCDF.read_file(str(paths[0]), read_only=True)
ds = xr.decode_cf(nc.to_xarray()) # labelled (time, latitude, longitude)
nc.close()
Average across the box, plus a 30-day rolling mean¶
Average across (lat, lon) per day, then take a 30-day rolling mean of SLA
for the smoothed series. Coastal SLA tracks regional ocean dynamics with a
strong interannual signal.
box = ds.mean(dim=['latitude', 'longitude'])
dates = box['time'].values
series = {v: box[v].values for v in VARIABLES}
sla_smoothed = box['sla'].rolling(time=30, center=True, min_periods=1).mean().values
Quick numeric summary of the two box-mean series.
print(
f'sla mean: {np.nanmean(series["sla"]):.4f} m, std: {np.nanstd(series["sla"]):.4f} m'
)
print(
f'adt mean: {np.nanmean(series["adt"]):.4f} m, std: {np.nanstd(series["adt"]):.4f} m'
)
Plot the SLA time-series¶
Raw daily anomaly plus 30-day rolling mean over the Iberian Atlantic coast — a coastal box that captures both seasonal steric variability and the regional response to North Atlantic forcing.
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(dates, series['sla'], color='tab:gray', alpha=0.4, label='daily SLA')
ax.plot(dates, sla_smoothed, color='tab:blue', label='30-day rolling mean')
ax.axhline(0, color='black', lw=0.5)
ax.set_xlabel('Date')
ax.set_ylabel('Sea level anomaly (m)')
ax.set_title('DUACS L4 SLA — Iberian Atlantic coastal box (2018-2022)')
ax.legend()
ax.grid(alpha=0.3)
fig.tight_layout()
Next steps¶
- Pair the SLA series with an in-situ tide-gauge record (e.g. GLOSS / PSMSL) to validate the regional signal.
- Switch to
ugos/vgos(surface geostrophic velocity) to look at the coastal current field. - Extend the time-window — DUACS L4 covers 1993-present, suitable for multi-decadal trend analysis.