Catching the ocean spectrum.
Anequim is a sensor-independent Python framework for retrieving ocean-color remote sensing reflectance (Rrs) and associated atmospheric products from local Level-2 satellite granules — fast, reliable, and without silently resampling a sensor's native spectral bands.
Retrieve the spectrum first. Everything else builds on that.
from anequim import Anequim
cube = Anequim.retrieve(
files="PACE_OCI.20240615T144200.L2.OC_AOP.nc",
longitude=-70.5,
latitude=41.3,
time="2024-06-15T15:00:00Z",
sensor="OCI",
)
print(cube)
print(cube.spectrum_dataframe())- Sensor independence — one API (
Anequim.get_rrs(...)) across PACE OCI, Sentinel-3 OLCI, VIIRS, and MODIS. You interact with the same object regardless of sensor. - Preserve native spectral bands — anequim never interpolates
automatically. Each sensor keeps its own wavelengths and spectral
response; harmonization is opt-in (
anequim.harmonization). - Fast spectral retrieval — answer "what is the ocean reflectance spectrum at this location?" in one call.
- Atmospheric products included — AOT, Ångström exponent, solar/ sensor zenith, relative azimuth, wind speed, and quality flags ride alongside Rrs through the same interface, when available.
- Region-of-interest tools — single pixel, circular, rectangular, and lon/lat bounding-box ROIs today; polygon ROI is planned.
SpectralCube— every sensor returns the same container: Rrs, wavelengths, metadata, navigation, atmospheric products, and quality flags.- Scientific reproducibility — every result carries a
Provenancerecord: package version, acquisition time, sensor identity, processing version, and the exact ROI/QC configuration used.
| Module | Status |
|---|---|
core (config, QC, flags, SpectralCube, Provenance, Anequim) |
Working |
readers — PACE OCI (sensor="OCI") |
Working |
readers — Sentinel-3 OLCI (sensor="OLCI") |
Working (directory-based SAFE granules; see caveats below) |
readers — VIIRS / MODIS |
Registered, raise NotImplementedSensorError (see module docstrings for the planned design — nearly identical file layout to PACE OCI, so these are the next-easiest to add) |
roi — pixel, rectangular, circular, bounding box |
Working |
roi — polygon |
Stub (NotImplementedError) |
geometry (haversine distance, nearest-pixel search) |
Working |
statistics (mean, median, std, percentile, covariance, correlation) |
Working |
download — PACE OCI via NASA Earthdata (earthaccess) |
Working (pip install anequim[download]) |
download — Sentinel-3 OLCI via Copernicus Data Space Ecosystem |
Working (pip install anequim[download]) |
harmonization (wavelength interpolation, SRF convolution) |
Stub, opt-in by design |
algorithms (QAA, GIOP, GSM) |
Stub — future bio-optical inversion |
plot |
plot_spectrum working; comparison/map plots stubbed |
Anequim can now find and download PACE OCI and Sentinel-3 OLCI granules
for you (Anequim.retrieve_online(...)), or you can still point it at
files you already have (Anequim.retrieve(files=..., ...)). For any
other sensor, download granules yourself and hand the resulting paths
to Anequim(files=...).
For a point/time query, Anequim follows the spirit of the widely-used satellite ocean-color match-up protocol described by Bailey & Werdell (2006):
- Locate the pixel nearest the target longitude/latitude, then extract
an odd-sized
N x Nbox around it (default5x5) — or use a circular / bounding-box ROI instead. - Only use granules whose overpass time is within a configurable window of the target time (default ±3 hours).
- Drop pixels flagged by the sensor's own quality flags (cloud, land, glint, extreme geometry, stray light, navigation failure, ...).
- Require a minimum valid-pixel fraction in the ROI (default 50%), and flag (but don't silently discard) ROIs with excessive spatial heterogeneity via coefficient of variation (default threshold 0.15).
- Report the mean or median of the valid pixels as the representative spectrum, with full statistics attached so you can apply your own, stricter or looser, acceptance criteria.
See docs/METHODOLOGY.md for the full write-up and references, and
docs/ARCHITECTURE.md for how the modules fit together.
pip install -e ".[all]" # editable install with plotting + xarray + test extrasCore runtime dependencies: numpy, netCDF4, pandas.
The interaction is the same regardless of sensor: give a point, a time,
and a sensor; anequim finds the granule, downloads it, and returns a
SpectralCube. Only the one-time credential setup differs, since PACE
OCI and Sentinel-3 OLCI are distributed by different agencies.
Requires pip install anequim[download].
PACE OCI — via NASA Earthdata (earthaccess):
from anequim import Anequim
from anequim.download import login
login(strategy="netrc") # or strategy="interactive", persist=True the first time
cube = Anequim.retrieve_online(
longitude=-70.5, latitude=41.3, time="2024-06-15T15:00:00Z", sensor="OCI",
)One-time setup: create a free NASA Earthdata Login account
(https://urs.earthdata.nasa.gov/), then either run
anequim.download.login(strategy="interactive", persist=True) once (it
saves credentials to ~/.netrc), or write ~/.netrc yourself.
Sentinel-3 OLCI — via the Copernicus Data Space Ecosystem (CDSE):
import os
os.environ["CDSE_USERNAME"] = "you@example.com"
os.environ["CDSE_PASSWORD"] = "..."
from anequim import Anequim
cube = Anequim.retrieve_online(
longitude=-70.5, latitude=41.3, time="2024-06-15T15:00:00Z", sensor="OLCI",
)One-time setup: create a free CDSE account
(https://dataspace.copernicus.eu/), then set CDSE_USERNAME /
CDSE_PASSWORD as environment variables (anequim exchanges them for a
short-lived access token automatically — no separate API key to
generate or manage).
Both calls search the respective agency's catalog for granules covering
your point and time window, download to ~/.anequim/cache/... (or
cache_dir= of your choosing), and run the same retrieval pipeline as
Anequim.retrieve(files=..., ...). Note: PACE OCI and Sentinel-3 OLCI
are distributed by different space agencies (NASA vs. ESA/EU
Copernicus) via genuinely separate catalogs and credential systems —
that's a fact about the data providers, not a design choice of
anequim's, but it's why two accounts are needed rather than one.
anequim --files data/*.nc --sensor OCI --lon -70.5 --lat 41.3 \
--time 2024-06-15T15:00:00Z --window-hours 3 --box-size 5 \
--output matchup.csvEvery SpectralCube reports the actual ground pixel size, measured
directly from the granule's own geolocation grid at the matched
location — not a hardcoded nominal constant, since real pixel size
varies substantially off-nadir:
cube.pixel_size_km # {'along_track_km':.., 'cross_track_km':.., 'mean_km':.., 'area_km2':..}
cube.roi_footprint_km # {'n_rows':.., 'n_cols':.., 'along_track_km':.., 'cross_track_km':.., 'diagonal_km':..}
cube.provenance.nominal_pixel_size_m # sensor's documented nadir spec, for reference (300 for OLCI, ~1000 for PACE OCI)OLCI L2 WFR granules are a directory (ESA SAFE format,
S3?_OL_2_WFR____....SEN3), not a single file — pass the directory path
itself to files=. A couple of things to know:
- Units: EUMETSAT's Collection 4 (effective Feb 2026) switched the
per-band product from water-leaving reflectance ρw to Rrs directly.
The reader checks each band's own
unitsattribute and converts automatically either way — no action needed. - Atmospheric geometry: solar/sensor zenith and relative azimuth
live on a coarser "tie-point" grid in OLCI files and aren't
interpolated to the full-resolution grid yet, so
cube.atmosphericmay be sparse (AOT/Ångström are included when present; geometry is not, for now). - Default QC flags: OLCI's WQSF flags have a different vocabulary
than PACE/MODIS/VIIRS's
l2_flags; a WQSF-appropriate default exclusion set is used automatically (seeanequim.readers.olci.DEFAULT_OLCI_EXCLUDED_FLAGS) — verify it against your own file'sflag_meaningsfor rigorous work.
examples/make_synthetic_pace_file.py and
examples/make_synthetic_olci_directory.py generate small, structurally
realistic synthetic granules (correct group/variable or directory
layout, a plausible phytoplankton-like Rrs spectral shape, and a
scattering of flagged pixels) so the whole pipeline can be exercised
offline for either sensor. The test suite (tests/) uses them as
pytest fixtures (synthetic_pace_file, synthetic_olci_directory).
pytest
python examples/quickstart.pyAnequim aims to become a reference open-source library for ocean-color spectral retrieval: one consistent interface across sensors, preserving each sensor's scientific integrity. A companion project, Anequim Lab, is planned as a collection of notebooks for tutorials, validation exercises, inter-sensor comparisons, and advanced bio-optical workflows.
- Bailey, S. W., & Werdell, P. J. (2006). A multi-sensor approach for the on-orbit validation of ocean color satellite data products. Remote Sensing of Environment, 102(1-2), 12-23.
- Lee, Z., Carder, K. L., & Arnone, R. A. (2002). Deriving inherent optical properties from water color: a multiband quasi-analytical algorithm for optically deep waters. Applied Optics, 41(27), 5755-5772.
- Werdell, P. J., et al. (2013). Generalized ocean color inversion model for retrieving marine inherent optical properties. Applied Optics, 52(10), 2019-2037.
- Maritorena, S., Siegel, D. A., & Peterson, A. R. (2002). Optimization of a semianalytical ocean color model for global-scale applications. Applied Optics, 41(15), 2705-2714.
- NASA Ocean Biology Processing Group, PACE OCI Level-2 Data Format and
l2_flagsbit definitions (oceancolor.gsfc.nasa.gov).
MIT.
