image1

Subset SWOT data from AVISO’s FTP Server

This notebook explains how to select and retrieve SWOT LR half orbits from AVISO’s FTP Server, and subset data with a geographical area.

Files available through FTP follow the same file tree as in the THREDDS Data Server.

Data

FTP starting point

Browsing files URL

Swot LR L2

/swot_products/l2_karin/l2_lr_ssh

https://tds.aviso.altimetry.fr/thredds/catalog/dataset-l2-swot-karin-lr-ssh-validated/catalog.html

Swot LR L3

/swot_products/l3_karin_nadir/l3_lr_ssh

https://tds.aviso.altimetry.fr/thredds/catalog/dataset-l3-swot-karin-nadir-validated/l3_lr_ssh/catalog.html

Warning

To subset L2 LR SSH Unsmoothed data, which is stored as grouped Netcdf files, please consult this tutorial.

Tutorial Objectives

  • Download files through FTP, with selection by cycle and pass numbers

  • Subset data with geographical selection

Import + code

[1]:
import warnings
warnings.filterwarnings('ignore')
[2]:
import os
from getpass import getpass
import ftplib
import xarray as xr
[3]:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
[4]:
def _download_file(ftp:str, filename:str, target_directory:str):
    print(f"Download file: {filename}")
    try:
        local_filepath = os.path.join(target_directory, filename)
        with open(local_filepath, 'wb') as file:
            ftp.retrbinary('RETR %s' % filename, file.write)
            return local_filepath
    except Exception as e:
        print(f"Error downloading {filename}: {e}")

def _get_last_version_filename(filenames):
    versions = {int(f[-5:-3]): f for f in filenames}
    return versions[max(versions.keys())]

def _select_filename(filenames, only_last):
    if not only_last: return filenames
    return [_get_last_version_filename(filenames)]

def ftp_download_files(ftp_path, level, variant, cycle_numbers, half_orbits, output_dir, only_last=True):
    """ Download half orbits files from AVISO's FTP Server.
    Args:
        ftp_path
            path of the FTP fileset
        level
            L2 or L3
        variant
            Basic, Expert, WindWave or Unsmoothed
        cycle_numbers
            list of cycles numbers
        half_orbits
            list of passes numbers
        output_dir
            output directory
        only_last
            if True (Default), downloads only the last version of a file if several versions exist.
            Downloads all versions otherwise.
    Returns:
        The list of local files.
    """
    # Set up FTP server details
    ftpAVISO = 'ftp-access.aviso.altimetry.fr'
    try:
        # Logging into FTP server using provided credentials
        with ftplib.FTP(ftpAVISO) as ftp:
            ftp.login(username, password)
            ftp.cwd(ftp_path)
            print(f"Connection Established {ftp.getwelcome()}")
            downloaded_files = []

            for cycle in cycle_numbers:
                cycle_str = '{:03d}'.format(cycle)
                cycle_dir = f'cycle_{cycle_str}'
                print(ftp_path+cycle_dir)
                ftp.cwd(cycle_dir)

                for half_orbit in half_orbits:
                    half_orbit_str = '{:03d}'.format(half_orbit)
                    pattern = f'SWOT_{level}_LR_SSH_{variant}_{cycle_str}_{half_orbit_str}'
                    filenames = []
                    try:
                        filenames = ftp.nlst(f'{pattern}_*')
                        # No version in L3 filenames
                        if level=="L3": only_last=False
                        filenames = _select_filename(filenames, only_last)
                    except Exception as e:
                        print(f"No pass {half_orbit}")

                    local_files = [_download_file(ftp, f, output_dir) for f in filenames]
                    downloaded_files += local_files

                ftp.cwd('../')

            return downloaded_files

    except ftplib.error_perm as e:
        print(f"FTP error: {e}")
    except Exception as e:
        print(f"Error: {e}")

def _normalized_ds(ds, lon_min, lon_max):
    lon = ds.longitude.values
    lon[lon < lon_min] += 360
    lon[lon > lon_max] -= 360
    ds.longitude.values = lon
    return ds

def _subset_ds(file, variables, lon_range, lat_range, output_dir):
    print(f"Subset dataset: {file}")
    swot_ds = xr.open_dataset(file)
    swot_ds = swot_ds[variables]
    swot_ds.load()

    ds = _normalized_ds(swot_ds.copy(), -180, 180)

    mask = (
        (ds.longitude <= lon_range[1])
        & (ds.longitude >= lon_range[0])
        & (ds.latitude <= lat_range[1])
        & (ds.latitude >= lat_range[0])
    ).compute()

    swot_ds_area = swot_ds.where(mask, drop=True)

    if swot_ds_area.sizes['num_lines'] == 0:
        print(f'Dataset {file} not matching geographical area.')
        return None

    for var in list(swot_ds_area.keys()):
        swot_ds_area[var].encoding = {'zlib':True, 'complevel':5}

    filename = "subset_"+file[10:]
    print(f"Store subset: {filename}")
    filepath = os.path.join(output_dir, filename)
    swot_ds_area.to_netcdf(filepath)

    return filepath


def subset_files(filenames, variables, lon_range, lat_range, output_dir):
    """ Subset datasets with geographical area.
    Args:
        filenames
            the filenames of datasets to subset
        variables
            variables to select
        lon_range
            the longitude range
        lat_range
            the latitude range
        output_dir
            output directory
    Returns:
        The list of subsets files.
    """
    return [subset_file for subset_file in [_subset_ds(f, variables, lon_range, lat_range, output_dir) for f in filenames] if subset_file is not None]


def plot_datasets(filenames, variable, extent=None):
    cb_args = dict(
        add_colorbar=True,
        cbar_kwargs={"shrink": 0.3}
    )

    plot_kwargs = dict(
        x="longitude",
        y="latitude",
        cmap="Spectral_r",
        vmin=-0.2,
        vmax=0.2,
    )

    fig, ax = plt.subplots(figsize=(12, 8), subplot_kw=dict(projection=ccrs.PlateCarree()))
    if extent: ax.set_extent(extent)

    for filename in filenames:
        ds = xr.open_dataset(filename)
        ds[variable].plot.pcolormesh(
            ax=ax,
            **plot_kwargs,
            **cb_args)
        cb_args=dict(add_colorbar=False)

    ax.coastlines()
    ax.gridlines(draw_labels=True)

Parameters

Define existing output folder to save results

[5]:
output_dir = "downloads"

Authentication parameters

Enter your AVISO+ credentials

[21]:
username = input("Enter username:")
Enter username: aviso-swot@altimetry.fr
[22]:
password = getpass(f"Enter password for {username}:")
Enter password for aviso-swot@altimetry.fr: ········

Data parameters

Define a geographical area

[8]:
# Mediterranean sea
lat_range = 25, 50
lon_range = -15, 40
localbox = [lon_range[0], lon_range[1], lat_range[0], lat_range[1]]
localbox
[8]:
[-15, 40, 25, 50]

Define the FTP filepath

[9]:
ftp_path = '/swot_products/l3_karin_nadir/l3_lr_ssh/v1_0_2/Expert/'

# For selecting files with a regex pattern
# Basic, Expert, WindWave, Unsmoothed
variant = "Expert"
# L2, L3
level = "L3"

Define variables to select

[10]:
variables = ['time', 'ssha']

Define data parameters

Note

Passes matching a geographical area and period can be found using this tutorial

[11]:
# Define cycles and half_orbits numbers to download
cycle_numbers = [19]
pass_numbers = [1, 2, 29, 70, 98, 236, 376]

Download files through FTP

[15]:
downloaded_files = ftp_download_files(ftp_path, level, variant, cycle_numbers, pass_numbers, output_dir, only_last=True)
Connection Established 220 192.168.10.119 FTP server ready
/swot_products/l3_karin_nadir/l3_lr_ssh/v1_0_2/Expert/cycle_019
Download file: SWOT_L3_LR_SSH_Expert_019_001_20240730T190512_20240730T195638_v1.0.2.nc
Download file: SWOT_L3_LR_SSH_Expert_019_002_20240730T195639_20240730T204805_v1.0.2.nc
Download file: SWOT_L3_LR_SSH_Expert_019_029_20240731T190543_20240731T195709_v1.0.2.nc
Download file: SWOT_L3_LR_SSH_Expert_019_070_20240802T061503_20240802T070629_v1.0.2.nc
Download file: SWOT_L3_LR_SSH_Expert_019_098_20240803T061534_20240803T070700_v1.0.2.nc
Download file: SWOT_L3_LR_SSH_Expert_019_236_20240808T043516_20240808T052642_v1.0.2.nc
Download file: SWOT_L3_LR_SSH_Expert_019_376_20240813T043752_20240813T052918_v1.0.2.nc
[16]:
downloaded_files
[16]:
['downloads/SWOT_L3_LR_SSH_Expert_019_001_20240730T190512_20240730T195638_v1.0.2.nc',
 'downloads/SWOT_L3_LR_SSH_Expert_019_002_20240730T195639_20240730T204805_v1.0.2.nc',
 'downloads/SWOT_L3_LR_SSH_Expert_019_029_20240731T190543_20240731T195709_v1.0.2.nc',
 'downloads/SWOT_L3_LR_SSH_Expert_019_070_20240802T061503_20240802T070629_v1.0.2.nc',
 'downloads/SWOT_L3_LR_SSH_Expert_019_098_20240803T061534_20240803T070700_v1.0.2.nc',
 'downloads/SWOT_L3_LR_SSH_Expert_019_236_20240808T043516_20240808T052642_v1.0.2.nc',
 'downloads/SWOT_L3_LR_SSH_Expert_019_376_20240813T043752_20240813T052918_v1.0.2.nc']

Plot downloaded half orbits

[17]:
plot_datasets(downloaded_files, 'ssha', extent=[-180, 180, -80, 80])
../_images/SWOT-Oceanography_ex_download_subset_aviso_ftp_28_0.png

Subset data

[18]:
subset_filenames = subset_files(downloaded_files, variables, lon_range, lat_range, output_dir)
Subset dataset: downloads/SWOT_L3_LR_SSH_Expert_019_001_20240730T190512_20240730T195638_v1.0.2.nc
Store subset: subset_SWOT_L3_LR_SSH_Expert_019_001_20240730T190512_20240730T195638_v1.0.2.nc
Subset dataset: downloads/SWOT_L3_LR_SSH_Expert_019_002_20240730T195639_20240730T204805_v1.0.2.nc
Dataset downloads/SWOT_L3_LR_SSH_Expert_019_002_20240730T195639_20240730T204805_v1.0.2.nc not matching geographical area.
Subset dataset: downloads/SWOT_L3_LR_SSH_Expert_019_029_20240731T190543_20240731T195709_v1.0.2.nc
Store subset: subset_SWOT_L3_LR_SSH_Expert_019_029_20240731T190543_20240731T195709_v1.0.2.nc
Subset dataset: downloads/SWOT_L3_LR_SSH_Expert_019_070_20240802T061503_20240802T070629_v1.0.2.nc
Store subset: subset_SWOT_L3_LR_SSH_Expert_019_070_20240802T061503_20240802T070629_v1.0.2.nc
Subset dataset: downloads/SWOT_L3_LR_SSH_Expert_019_098_20240803T061534_20240803T070700_v1.0.2.nc
Store subset: subset_SWOT_L3_LR_SSH_Expert_019_098_20240803T061534_20240803T070700_v1.0.2.nc
Subset dataset: downloads/SWOT_L3_LR_SSH_Expert_019_236_20240808T043516_20240808T052642_v1.0.2.nc
Store subset: subset_SWOT_L3_LR_SSH_Expert_019_236_20240808T043516_20240808T052642_v1.0.2.nc
Subset dataset: downloads/SWOT_L3_LR_SSH_Expert_019_376_20240813T043752_20240813T052918_v1.0.2.nc
Store subset: subset_SWOT_L3_LR_SSH_Expert_019_376_20240813T043752_20240813T052918_v1.0.2.nc
[19]:
subset_filenames
[19]:
['downloads/subset_SWOT_L3_LR_SSH_Expert_019_001_20240730T190512_20240730T195638_v1.0.2.nc',
 'downloads/subset_SWOT_L3_LR_SSH_Expert_019_029_20240731T190543_20240731T195709_v1.0.2.nc',
 'downloads/subset_SWOT_L3_LR_SSH_Expert_019_070_20240802T061503_20240802T070629_v1.0.2.nc',
 'downloads/subset_SWOT_L3_LR_SSH_Expert_019_098_20240803T061534_20240803T070700_v1.0.2.nc',
 'downloads/subset_SWOT_L3_LR_SSH_Expert_019_236_20240808T043516_20240808T052642_v1.0.2.nc',
 'downloads/subset_SWOT_L3_LR_SSH_Expert_019_376_20240813T043752_20240813T052918_v1.0.2.nc']

Plot subsets

[20]:
plot_datasets(subset_filenames, 'ssha', extent=localbox)
../_images/SWOT-Oceanography_ex_download_subset_aviso_ftp_33_0.png