Swot L3_LR_SSH#

This chapter will present the functionalities specific to the Level 3 SWOT Low Rate products.

from fcollections.implementations import NetcdfFilesDatabaseSwotLRL3

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

Data samples#

We will illustrate the functionalities using a data sample from AVISO. You can use the altimetry-downloader-aviso tool to run the following script.

import logging
from pathlib import Path

from altimetry_downloader_aviso import get

logging.basicConfig()
logging.getLogger("altimetry_downloader_aviso").setLevel("INFO")

DATA_DIR = Path(__file__).resolve().parent.parent / "data"
DATA_DIR.mkdir(exist_ok=True)

if __name__ == "__main__":

    get(
        "SWOT_L3_LR_SSH_Basic",
        output_dir=DATA_DIR,
        version="2.0.1",
        cycle_number=[1, 2, 3],
        pass_number=[10, 11],
    )

Query overview#

Detailed information on the filters and reading arguments can be found in the query API description

fcollections.implementations.NetcdfFilesDatabaseSwotLRL3.query()

The following examples can be used to build complex queries

  • A unique half orbit

    fc.query(cycle_number=1, pass_number=1)
    
  • One half orbit repeating over all cycles

    fc.query(pass_number=1)
    
  • A list of half orbits, over multiple cycles

    fc.query(cycle_number=slice(1, 4), pass_number=[1, 3])
    
  • A time stamp

    fc.query(time='2024-01-01')
    
  • A period

    fc.query(time=('2024-01-01', '2024-03-31'))
    
  • Zoom over an area selection

    fc.query(bbox=(-10, 5, 35, 40))
    
  • Nadir and KaRIn data (Basic, Expert only)

    fc.query(nadir=True)
    
  • Nadir data only (Basic, Expert only)

    fc.query(swath=False, nadir=True)
    
  • Stacking over cycles (Basic, Expert, Technical only)

    fc.query(stack='CYCLES')
    
  • Stacking over both cycles and passes (Basic, Expert, Technical only)

    fc.query(stack='CYCLES_PASSES')
    
  • Fix a version

    fc.query(version='2.0.1')
    
  • Choose one dataset

    fc.query(subset='Expert')
    

Stack for temporal analysis#

The most prominent functionality is the ability to stack the half orbits when the grid is fixed (Basic, Expert and Technical subsets). This allows to work along the cycle_number dimension and compute temporal analysis (mean, standard deviation, …).

There are currently three modes for stacking the half orbits

  • NOSTACK: do not stack the half orbits

  • CYCLES: concatenate the half orbits of one cycle along the num_lines dimension, and stack the cycles along a new cycle_number dimension

  • CYCLES_PASSES: stack the half orbits along the cycle_number and pass_number dimensions. Useful for regional analysis where the half orbits are cropped and we need an additional dimension to reflect the spatial jump

fc = NetcdfFilesDatabaseSwotLRL3("data")
ds = fc.query(stack='CYCLES', version='2.0.1', cycle_number=[1, 2, 3], pass_number=10, subset='Basic')
ds.ssha_filtered.data
Array Chunk
Bytes 10.38 MiB 5.19 MiB
Shape (2, 9860, 69) (1, 9860, 69)
Dask graph 2 chunks in 11 graph layers
Data type float64 numpy.ndarray
69 9860 2
ds = fc.query(stack='CYCLES_PASSES', version='2.0.1', cycle_number=[1, 2, 3], pass_number=[10, 11], subset='Basic')
ds.ssha_filtered.data
Array Chunk
Bytes 20.76 MiB 5.19 MiB
Shape (2, 2, 9860, 69) (1, 1, 9860, 69)
Dask graph 4 chunks in 25 graph layers
Data type float64 numpy.ndarray
2 1 69 9860 2

Note

Incomplete cycles are completed with invalids

Area selection#

It is possible to select data crossing a specific region by providing bbox parameter to query or list_files method.

The bounding box is represented by a tuple of 4 float numbers, such as : (longitude_min, latitude_min, longitude_max, latitude_max). Its longitude must follow one of the known conventions: [0, 360[ or [-180, 180[.

If bbox’s longitude crosses -180/180, data around the crossing and matching the bbox will be selected. (e.g. for an interval [170, -170] -> both [170, 180[ and [-180, -170] intervals will be used to list/subset data).

To list files corresponding to half orbits crossing the bounding box:

bbox = -126, 32, -120, 40
fc.list_files(
    version='2.0.1',
    subset='Basic',
    bbox=bbox)
cycle_number pass_number time level subset version filename
0 2 11 [2023-08-11T10:53:21.000000, 2023-08-11T11:44:... ProductLevel.L3 ProductSubset.Basic 2.0.1 /home/runner/work/fcollections/fcollections/do...
1 3 11 [2023-09-01T07:38:26.000000, 2023-09-01T08:29:... ProductLevel.L3 ProductSubset.Basic 2.0.1 /home/runner/work/fcollections/fcollections/do...

To query a subset of Swot LR L3 data crossing the bounding box:

Note

Lines of the swath crossing the bounding box will be entirely selected.

bbox = -126, 32, -120, 40
ds_area = fc.query(version='2.0.1', subset="Basic", cycle_number=2, pass_number=11, bbox=bbox)

# Figure
localbox_cartopy = bbox[0] - 1, bbox[2] + 1, bbox[1] - 1, bbox[3] + 1
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(projection=ccrs.PlateCarree()))
ax.set_extent(localbox_cartopy)

plot_kwargs = dict(
    x="longitude",
    y="latitude",
    cmap="Spectral_r",
    vmin=-0.2,
    vmax=0.2,
    cbar_kwargs={"shrink": 0.3},)

# SWOT KaRIn SLA plots
ds_area.ssha_filtered.plot.pcolormesh(ax=ax, **plot_kwargs)
ax.set_title("SLA KaRIn and selection box (in red)")
ax.coastlines()
ax.gridlines(draw_labels=['left', 'bottom'])


# Add the patch to the Axes
rect = patches.Rectangle((bbox[0], bbox[1]), bbox[2] - bbox[0], bbox[3] - bbox[1], linewidth=1.5, edgecolor='r', facecolor='none')
ax.add_patch(rect)
<matplotlib.patches.Rectangle at 0x7f04914ac4a0>
../_images/48e02c2ebf0faf9e9f65afd29abf610d0cb9ce91d092d316b6a7413bfea5cfcf.png

Handling nadir clipped data in Level-3 Basic and Expert subsets#

The L3_LR_SSH Basic and Expert subsets have the Nadir instrument data clipped in the Sea Level Anomaly fields. The indexes where the nadir data has been introduced are stored along num_nadir dimension. The SWOT implementation offers various choices for handling this clipped data:

  • nadir=False and swath=True: remove the nadir data clipped. This is the default behavior

  • nadir=True and swath=True: do nothing and keep both the KaRIn and Nadir instruments data

  • nadir=True and swath=False: extract the Nadir instrument data only. This will give a dataset indexed along the num_nadir dimension. Because it returns the nadir data only, we lose the possibility of stacking multiple half orbits

ds_full = fc.query(version='2.0.1', subset="Basic", cycle_number=2, pass_number=11, nadir=True)
ds_swath = fc.query(version='2.0.1', subset="Basic", cycle_number=2, pass_number=11, nadir=False)

# set figures
localbox = 224.5, 228.5, -27, -23
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 9), subplot_kw=dict(projection=ccrs.PlateCarree()))
ax1.set_extent(localbox)
ax2.set_extent(localbox)

plot_kwargs = dict(
    x="longitude",
    y="latitude",
    cmap="Spectral_r",
    vmin=-0.2,
    vmax=0.2,
    cbar_kwargs={"shrink": 0.3},)

# SWOT KaRIn SLA plots
ds_full.ssha_filtered.plot.pcolormesh(ax=ax1, **plot_kwargs)
ds_swath.ssha_filtered.plot.pcolormesh(ax=ax2, **plot_kwargs)

ax1.set_title("SLA KaRIn + Nadir")
ax1.coastlines()
ax1.gridlines(draw_labels=['left', 'bottom'])
ax2.set_title("SLA KaRIn only")
ax2.coastlines()
ax2.gridlines(draw_labels=['left', 'bottom'])
<cartopy.mpl.gridliner.Gridliner at 0x7f048700f320>
../_images/ca7eedbfb47350b513a3d73a60e61aa977f519dc59233530e245f43a604d4e71.png
ds_nadir = fc.query(version='2.0.1', subset="Basic", cycle_number=2, pass_number=11, nadir=True, swath=False)

plt.plot(ds_nadir.latitude.values, ds_nadir.ssha_filtered.values)
plt.ylabel(f'{ds_nadir.ssha_filtered.attrs["standard_name"]} [{ds_nadir.ssha_filtered.attrs["units"]}]')
plt.xlabel(f'{ds_nadir.latitude.attrs["standard_name"]} [{ds_nadir.latitude.attrs["units"]}]')
plt.title("SLA Nadir")
Text(0.5, 1.0, 'SLA Nadir')
../_images/d06ebb698507f4cc4ec9398fe92ee7216ba50d45a18e1c9d501b586a4c8be2de.png