Every regional SWOT analysis starts with the same question: which passes cross my box, and when? This tutorial shows the two ways to answer it:
- the pass-footprint index — instant, offline, opens no data files;
odsl-swot-swath-map— discovers passes for a time window via NASA CMR and plots the actual data in one command.
Prerequisites: tutorial 1. The running example is
the Gulf of America box -98 18 -80 31.
1. Why footprints work
SWOT flies a fixed 21-day repeat orbit, so a pass number always traces the same ground footprint (tutorial 1). That means pass geometry can be computed once, from a single complete reference cycle, and reused forever.
A whole pass is a long, thin, diagonal ribbon — its overall bounding box is nearly useless for regional queries. So the index instead stores the pass cut into ~120 km along-track segments, each with a tight, near-square bounding box plus its along-track time offset. A plain bbox test against the segments prunes ~90% of passes without opening a single granule, and the precise repeat period turns each segment's time offset into a predicted UTC time for every cycle, past and future.
2. Build the index (once)
odsl-swot-footprints
This reads the true swath lat/lon/time from one complete 584-pass
reference cycle (default: cycle 2) under /spray/swot/versionD and writes a
GeoParquet index (~96k segments) to:
/spray/swot/versionD/footprints/swot_pass_segments_science.parquet
On the lab server, check whether that file already exists first — if it
does, someone has built it and you can go straight to querying. Building takes
a while (it reads a whole cycle) but only ever needs to happen once per data
version. Use --root, --ref-cycle, or --out to override the defaults.
3. Query the index from Python
All queries are functions in swot.pass_footprints and open no data
files — they run in milliseconds.
from swot.pass_footprints import candidate_passes, candidate_cycle_passes
bbox = (-98, 18, -80, 31) # west, south, east, north
# Which pass numbers ever cross the box?
print(sorted(candidate_passes(bbox, "2024-06-01", "2024-06-15")))
# Which (cycle, pass) pairs fly over it in a time window?
for cycle, pass_num in sorted(candidate_cycle_passes(bbox, "2024-06-01", "2024-06-15")):
print(f"cycle {cycle:3d} pass {pass_num:3d}")
For predicted overflight times (e.g. to plan a field campaign or match
in-situ data), use predicted_segments, which returns a pandas DataFrame with
cycle, pass, and predicted start/end times per segment:
from swot.pass_footprints import predicted_segments
seg = predicted_segments(bbox, "2024-06-01", "2024-06-15")
print(seg[["cycle", "pass", "pred_time_start", "pred_time_end"]].drop_duplicates())
The results are candidates from bbox intersection — a pass whose segment
bbox clips the corner of your box may contribute few pixels. The next step
(plotting, subsetting) confirms actual coverage. This same index is what the
colocate package uses internally to skip passes that miss a region.
4. See the data: odsl-swot-swath-map
odsl-swot-swath-map scatter-plots raw swath pixels on a map. It has two
selection modes.
Time-window discovery — give it a window and a box, and it finds the passes itself (via a CMR query, so it needs network):
odsl-swot-swath-map --start 2024-06-01 --end 2024-06-03 \
--bbox -98 18 -80 31 \
--output ~/plots/gulf_swaths.png
Explicit passes — plot exactly the cycle:pass pairs you name (e.g. the ones the footprint query returned; zero-padding optional):
odsl-swot-swath-map --cycle-pass 20:013 20:026 \
--bbox -98 18 -80 31 \
--variable ssha_karin_2 \
--output ~/plots/gulf_pass13_26.png
Useful options:
--variable— anything in the granule (defaultssha_karin_2;sig0variables automatically get a gray colormap and their own quality flag).--max-quality— quality-flag cutoff (default 2; pixels with a higher flag are dropped).--gradient— plot the gradient magnitude instead of the field, which makes fronts and internal waves pop.--product-type Unsmoothed— use the native-resolution product (both swath sides are loaded).--vmin/--vmax,--cmap,--point-size,--figsize,--dpi— cosmetics.
The output is one combined map (--output is required) with all requested
passes overlaid and coastlines clipped to your box.
Troubleshooting
candidate_cycle_passesreturns an empty set — check the bbox order (west, south, east, north) and that your time window overlaps the science phase (mid-2023 onward). The index only covers the science orbit.- Footprint file not found — the index hasn't been built on this data
root yet; run
odsl-swot-footprints(section 2). --start/--endmode finds passes but the plot is empty — the granules for those passes may not be downloaded yet; see tutorial 2. Explicit--cycle-passmode reads only local files.