Metadata-Version: 2.4
Name: earthlens
Version: 0.15.0
Summary: remote sensing package
Author-email: Mostafa Farrag <moah.farag@gmail.com>
License-Expression: GPL-3.0-only
Project-URL: homepage, https://github.com/serapeum-org/earthlens
Project-URL: repository, https://github.com/serapeum-org/earthlens
Project-URL: documentation, https://serapeum-org.github.io/earthlens/
Project-URL: Changelog, https://github.com/serapeum-org/earthlens/blob/main/docs/change-log.md
Keywords: remote sensing,ecmwf
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Natural Language :: English
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Requires-Python: <4,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE.md
Requires-Dist: earthlens-core==0.15.0
Requires-Dist: earthlens-atmosphere==0.15.0
Requires-Dist: earthlens-ocean==0.15.0
Requires-Dist: earthlens-imagery==0.15.0
Requires-Dist: earthlens-land==0.15.0
Requires-Dist: earthlens-hazards==0.15.0
Provides-Extra: argo
Requires-Dist: earthlens-ocean[argo]; extra == "argo"
Provides-Extra: asf
Requires-Dist: earthlens-imagery[asf]; extra == "asf"
Provides-Extra: cmems
Requires-Dist: earthlens-ocean[cmems]; extra == "cmems"
Provides-Extra: cmip6
Requires-Dist: earthlens-atmosphere[cmip6]; extra == "cmip6"
Provides-Extra: dem
Requires-Dist: earthlens-land[dem]; extra == "dem"
Provides-Extra: earthdata
Requires-Dist: earthlens-imagery[earthdata]; extra == "earthdata"
Provides-Extra: ecmwf
Requires-Dist: earthlens-atmosphere[ecmwf]; extra == "ecmwf"
Provides-Extra: ecmwf-modern
Requires-Dist: earthlens-atmosphere[ecmwf-modern]; extra == "ecmwf-modern"
Provides-Extra: eea-aq
Requires-Dist: earthlens-atmosphere[eea_aq]; extra == "eea-aq"
Provides-Extra: emdat
Requires-Dist: earthlens-hazards[emdat]; extra == "emdat"
Provides-Extra: erddap
Requires-Dist: earthlens-ocean[erddap]; extra == "erddap"
Provides-Extra: eumetsat
Requires-Dist: earthlens-imagery[eumetsat]; extra == "eumetsat"
Provides-Extra: fdsn
Requires-Dist: earthlens-hazards[fdsn]; extra == "fdsn"
Provides-Extra: gbif
Requires-Dist: earthlens-land[gbif]; extra == "gbif"
Provides-Extra: gee
Requires-Dist: earthlens-imagery[gee]; extra == "gee"
Provides-Extra: ghsl
Requires-Dist: earthlens-land[ghsl]; extra == "ghsl"
Provides-Extra: goes
Requires-Dist: earthlens-atmosphere[goes]; extra == "goes"
Provides-Extra: hdx
Requires-Dist: earthlens-hazards[hdx]; extra == "hdx"
Provides-Extra: isimip
Requires-Dist: earthlens-atmosphere[isimip]; extra == "isimip"
Provides-Extra: jaxa
Requires-Dist: earthlens-imagery[jaxa]; extra == "jaxa"
Provides-Extra: mswep
Requires-Dist: earthlens-atmosphere[mswep]; extra == "mswep"
Provides-Extra: nwm
Requires-Dist: earthlens-ocean[nwm]; extra == "nwm"
Provides-Extra: nwp
Requires-Dist: earthlens-atmosphere[nwp]; extra == "nwp"
Provides-Extra: obis
Requires-Dist: earthlens-ocean[obis]; extra == "obis"
Provides-Extra: openeo
Requires-Dist: earthlens-imagery[openeo]; extra == "openeo"
Provides-Extra: osm
Requires-Dist: earthlens-hazards[osm]; extra == "osm"
Provides-Extra: osm-pbf
Requires-Dist: earthlens-hazards[osm-pbf]; extra == "osm-pbf"
Provides-Extra: overture
Requires-Dist: earthlens-hazards[overture]; extra == "overture"
Provides-Extra: radar
Requires-Dist: earthlens-atmosphere[radar]; extra == "radar"
Provides-Extra: s3
Requires-Dist: earthlens-atmosphere[s3]; extra == "s3"
Provides-Extra: sentinel-hub
Requires-Dist: earthlens-imagery[sentinel-hub]; extra == "sentinel-hub"
Provides-Extra: stac
Requires-Dist: earthlens-imagery[stac]; extra == "stac"
Provides-Extra: tropycal
Requires-Dist: earthlens-atmosphere[tropycal]; extra == "tropycal"
Provides-Extra: usgs-water
Requires-Dist: earthlens-ocean[usgs-water]; extra == "usgs-water"
Provides-Extra: worldpop
Requires-Dist: earthlens-land[worldpop]; extra == "worldpop"
Provides-Extra: all
Requires-Dist: earthlens[asf]; extra == "all"
Requires-Dist: earthlens[cmems]; extra == "all"
Requires-Dist: earthlens[cmip6]; extra == "all"
Requires-Dist: earthlens[dem]; extra == "all"
Requires-Dist: earthlens[earthdata]; extra == "all"
Requires-Dist: earthlens[ecmwf]; extra == "all"
Requires-Dist: earthlens[ecmwf-modern]; extra == "all"
Requires-Dist: earthlens[eea_aq]; extra == "all"
Requires-Dist: earthlens[emdat]; extra == "all"
Requires-Dist: earthlens[erddap]; extra == "all"
Requires-Dist: earthlens[eumetsat]; extra == "all"
Requires-Dist: earthlens[fdsn]; extra == "all"
Requires-Dist: earthlens[gbif]; extra == "all"
Requires-Dist: earthlens[gee]; extra == "all"
Requires-Dist: earthlens[ghsl]; extra == "all"
Requires-Dist: earthlens[goes]; extra == "all"
Requires-Dist: earthlens[hdx]; extra == "all"
Requires-Dist: earthlens[isimip]; extra == "all"
Requires-Dist: earthlens[jaxa]; extra == "all"
Requires-Dist: earthlens[mswep]; extra == "all"
Requires-Dist: earthlens[nwm]; extra == "all"
Requires-Dist: earthlens[nwp]; extra == "all"
Requires-Dist: earthlens[obis]; extra == "all"
Requires-Dist: earthlens[openeo]; extra == "all"
Requires-Dist: earthlens[osm]; extra == "all"
Requires-Dist: earthlens[overture]; extra == "all"
Requires-Dist: earthlens[radar]; extra == "all"
Requires-Dist: earthlens[s3]; extra == "all"
Requires-Dist: earthlens[sentinel-hub]; extra == "all"
Requires-Dist: earthlens[stac]; extra == "all"
Requires-Dist: earthlens[tropycal]; extra == "all"
Requires-Dist: earthlens[usgs-water]; extra == "all"
Requires-Dist: earthlens[worldpop]; extra == "all"
Dynamic: license-file

<p align="center">
  <img src="docs/_images/branding/earthlens-brand-kit/docs/readme-banner.png" width="820"
       alt="earthlens — 48 Earth-observation providers · one facade · modular install">
</p>

[![Tests](https://github.com/serapeum-org/earthlens/actions/workflows/tests.yml/badge.svg)](https://github.com/serapeum-org/earthlens/actions/workflows/tests.yml)
[![Wheel](https://github.com/serapeum-org/earthlens/actions/workflows/wheel-test.yml/badge.svg)](https://github.com/serapeum-org/earthlens/actions/workflows/wheel-test.yml)
[![Docs](https://github.com/serapeum-org/earthlens/actions/workflows/github-pages-mkdocs.yml/badge.svg)](https://serapeum-org.github.io/earthlens/)
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/earthlens)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit)
[![conda-forge feedstock](https://img.shields.io/badge/conda--forge-feedstock-blue?logo=condaforge&logoColor=white)](https://github.com/conda-forge/earthlens-feedstock)


[![codecov](https://codecov.io/gh/serapeum-org/earthlens/branch/main/graph/badge.svg)](https://codecov.io/gh/serapeum-org/earthlens)
![GitHub last commit](https://img.shields.io/github/last-commit/serapeum-org/earthlens)
![GitHub forks](https://img.shields.io/github/forks/serapeum-org/earthlens?style=social)
![GitHub Repo stars](https://img.shields.io/github/stars/serapeum-org/earthlens?style=social)


Current release info
====================

| Name                                                                                                               | Downloads                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Version                                                                                                                                                                                                                                                                                                                                           | Platforms                                                                                                               |
|--------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------|
| [![Conda Recipe](https://img.shields.io/badge/recipe-earthlens-green.svg)](https://anaconda.org/conda-forge/earthlens) | [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/earthlens.svg)](https://anaconda.org/conda-forge/earthlens) [![Downloads](https://pepy.tech/badge/earthlens)](https://pepy.tech/project/earthlens) [![Downloads](https://pepy.tech/badge/earthlens/month)](https://pepy.tech/project/earthlens) [![Downloads](https://pepy.tech/badge/earthlens/week)](https://pepy.tech/project/earthlens) ![PyPI - Downloads](https://img.shields.io/pypi/dd/earthlens?color=blue&style=flat-square) ![GitHub all releases](https://img.shields.io/github/downloads/serapeum-org/earthlens/total) | [![Conda Version](https://img.shields.io/conda/vn/conda-forge/earthlens.svg)](https://anaconda.org/conda-forge/earthlens) [![PyPI version](https://badge.fury.io/py/earthlens.svg)](https://badge.fury.io/py/earthlens) [![Anaconda-Server Badge](https://anaconda.org/conda-forge/earthlens/badges/version.svg)](https://anaconda.org/conda-forge/earthlens) | [![Conda Platforms](https://img.shields.io/conda/pn/conda-forge/earthlens.svg)](https://anaconda.org/conda-forge/earthlens) |

earthlens — a unified Python client for satellite & climate data
=====================================================================

**earthlens** gives you one consistent Python API for downloading satellite,
climate, and geospatial data from **48 providers** — climate reanalysis,
satellite imagery, ocean models, weather forecasts, natural-hazard feeds, air
quality, biodiversity, population, and more — and turning the results into
analysis-ready GeoTIFFs, GeoDataFrames, or tables.

It is part of the [serapeum-org](https://github.com/serapeum-org)
open-source ecosystem and is built on top of
[`pyramids-gis`](https://github.com/serapeum-org/pyramids) for raster I/O.


Why earthlens?
------------

<p align="center">
  <img src="docs/_images/branding/earthlens-brand-kit/animation/earthlens-logo-orbit.gif" width="600"
       alt="A satellite orbiting the earthlens globe">
</p>

Every provider speaks its own dialect: CHIRPS is anonymous FTP with date-coded
filenames, ERA5-on-S3 is unsigned object storage with a per-month layout, the
ECMWF CDS expects a JSON request body validated against a constraints graph,
Google Earth Engine is a server-side image-collection model, and the other 44
each have their own. **earthlens** flattens all of it into one call:

```python
from earthlens.core import EarthLens

earthlens = EarthLens(
    data_source="ecmwf",          # or "chc" (alias "chirps"), "amazon-s3", "gee"
    temporal_resolution="monthly",
    start="2022-01-01",
    end="2022-12-01",
    variables={
        "reanalysis-era5-single-levels-monthly-means": [
            "2m-temperature",
            "total-precipitation",
        ],
    },
    lat_lim=[37.0, 38.0],
    lon_lim=[23.0, 24.0],
    path="data/era5",
)
earthlens.download()
```

You get back per-date, per-variable GeoTIFFs in `data/era5/` — clipped to your
bbox, ready to feed into a hydrology model, a PV-yield notebook, a heat-wave
study, or anything else downstream.


Features
--------

- **48 backends, one facade.** `EarthLens(data_source=...)` routes to any
  provider without changing the rest of your code. Backends are discovered
  through entry points and imported lazily, so the SDK for a provider you never
  touch is never loaded.
- **Cross-provider discovery.** `find("precipitation")` tells you which of the
  48 providers serve a dataset, offline, before you commit to one; `search(...)`
  dry-runs a request and lists exactly what it would fetch.
- **YAML variable catalogs** per provider — every variable carries metadata:
  NetCDF name, units, accumulation semantics (`is_flux`), allowed pressure
  levels, monthly counterparts. Browseable with `Catalog().get_variable(...)`.
- **Pre-flight request validation** against the live CDS `constraints.json`
  graph. Bad date / area / variable combinations are rejected before bytes
  go over the wire, with actionable error messages.
- **Temporal aggregation built in.** Pass an `AggregationConfig` to
  `download()` and earthlens emits aggregated GeoTIFFs alongside the raw
  NetCDFs. `op="auto"` reduces **state** variables (temperature, SST, soil
  moisture) by mean and **flux** variables (precipitation, radiation,
  evaporation) by sum — the physically correct choice driven by catalog
  metadata.
- **Pressure-level support.** ERA5 pressure-level fields (4-D NetCDFs) can be
  sliced to a specific level on download.
- **Bbox cropping & NetCDF→GeoTIFF conversion** are handled by `pyramids-gis`
  under the hood.
- **Modular install extras** — only install the SDK for the backend you need
  (`pip install earthlens[ecmwf]`, `[s3]`, `[gee]`).
- **Bounded by design.** Streamed, atomic downloads; pooled HTTP connections
  with `Retry-After`-aware back-off; an exact `limit=` cap for vector/tabular
  requests; re-runs skip artefacts that are already complete.
- **Strictly typed.** Pydantic v2 models for catalog rows and request specs;
  modern PEP 585/604 type hints; Python 3.11 – 3.14 tested in CI.


Supported data sources
----------------------

`earthlens` wraps 48 providers behind the one `EarthLens(data_source=..., ...)` facade —
pass the `data_source` value below and everything else (auth, request shaping, output
format) is handled per-backend. See [Data Sources](https://serapeum-org.github.io/earthlens/examples/data-sources/)
for the full walkthrough of each one.

**Air quality**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/airnow.svg" height="20"> | [AirNow (US EPA)](https://www.airnow.gov/) | `airnow` |
| <img src="docs/_images/logos/eea-aq.svg" height="20"> | [European Environment Agency](https://www.eea.europa.eu/) | `eea-aq` |
| <img src="docs/_images/logos/openaq.svg" height="20"> | [OpenAQ](https://openaq.org/) | `openaq` |
| <img src="docs/_images/logos/sensor-community.png" height="20"> | [Sensor.Community](https://sensor.community/) | `sensor-community` |

**Biodiversity & protected areas**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/gbif.svg" height="20"> | [GBIF](https://www.gbif.org/) | `gbif` |
| <img src="docs/_images/logos/iucn.svg" height="20"> | [IUCN Red List](https://www.iucnredlist.org/) | `iucn` |
| <img src="docs/_images/logos/obis.png" height="20"> | [OBIS](https://obis.org/) | `obis` |
| <img src="docs/_images/logos/wdpa.png" height="20"> | [Protected Planet (UNEP-WCMC)](https://www.protectedplanet.net/) | `wdpa` |

**Climate reanalysis & projections**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/ecmwf.png" height="20"> | [Copernicus Climate Data Store (ECMWF)](https://cds.climate.copernicus.eu) | `ecmwf` |
| <img src="docs/_images/logos/climate_indices.svg" height="20"> | [NOAA Physical Sciences Laboratory](https://psl.noaa.gov/data/climateindices/) | `climate-indices` |
| <img src="docs/_images/logos/cmip6.svg" height="20"> | [WCRP CMIP6](https://wcrp-cmip.org/) | `cmip6` |

**Disasters & risk**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/fdsn.png" height="20"> | [FDSN](https://www.fdsn.org/) | `fdsn` |
| <img src="docs/_images/logos/gdacs.png" height="20"> | [GDACS](https://www.gdacs.org/) | `gdacs` |
| <img src="docs/_images/logos/firms.png" height="20"> | [NASA FIRMS](https://firms.modaps.eosdis.nasa.gov/) | `firms` |
| <img src="docs/_images/logos/risk_indicators.svg" height="20"> | [ThinkHazard! (GFDRR/World Bank)](https://thinkhazard.org) | `risk-indicators` |

**Elevation & bathymetry**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/dem.svg" height="20"> | [Copernicus DEM (ESA)](https://spacedata.copernicus.eu/collections/copernicus-digital-elevation-model) | `dem` |
| <img src="docs/_images/logos/bathymetry.png" height="20"> | [GEBCO](https://www.gebco.net/) | `bathymetry` |

**Glaciers & cryosphere**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/glaciers.png" height="20"> | [NSIDC Randolph Glacier Inventory](https://nsidc.org/data/nsidc-0770/versions/7) | `glaciers` |

**Humanitarian data**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/hdx.png" height="20"> | [Humanitarian Data Exchange (UN OCHA)](https://data.humdata.org) | `hdx` |

**Hydrology**

| | Provider | `data_source` |
|---|---|---|
|   | [NOAA National Water Model](https://water.noaa.gov/about/nwm) | `nwm` |
| <img src="docs/_images/logos/usgs-water.svg" height="20"> | [USGS National Water Information System](https://waterdata.usgs.gov/) | `usgs-water` |

**Multi-mission imagery & data platforms**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/s3.png" height="20"> | [AWS Open Data](https://registry.opendata.aws/) | `amazon-s3` |
| <img src="docs/_images/logos/eumetsat.svg" height="20"> | [EUMETSAT](https://www.eumetsat.int/) | `eumetsat` |
| <img src="docs/_images/logos/gee.png" height="20"> | [Google Earth Engine](https://earthengine.google.com/) | `gee` |
| <img src="docs/_images/logos/jaxa.svg" height="20"> | [JAXA](https://www.jaxa.jp/) | `jaxa` |
| <img src="docs/_images/logos/earthdata.png" height="20"> | [NASA Earthdata](https://www.earthdata.nasa.gov/) | `earthdata` |
| <img src="docs/_images/logos/goes.png" height="20"> | [NOAA GOES-R](https://www.goes-r.gov/) | `goes` |
| <img src="docs/_images/logos/stac.png" height="20"> | [STAC (SpatioTemporal Asset Catalog)](https://stacspec.org/) | `stac` |
| <img src="docs/_images/logos/sentinel-hub.png" height="20"> | [Sentinel Hub](https://www.sentinel-hub.com/) | `sentinel-hub` |
| <img src="docs/_images/logos/openeo.png" height="20"> | [openEO](https://openeo.org) | `openeo` |

**Ocean**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/argo.png" height="20"> | [Argo Program](https://argo.ucsd.edu/) | `argo` |
| <img src="docs/_images/logos/cmems.svg" height="20"> | [Copernicus Marine Service](https://marine.copernicus.eu/) | `cmems` |
| <img src="docs/_images/logos/erddap.svg" height="20"> | [NOAA ERDDAP](https://www.ncei.noaa.gov/erddap/information.html) | `erddap` |

**Population & human settlement**

| | Provider | `data_source` |
|---|---|---|
|   | [European Commission Joint Research Centre (GHSL)](https://ghsl.jrc.ec.europa.eu/) | `ghsl` |
| <img src="docs/_images/logos/worldpop.png" height="20"> | [WorldPop](https://hub.worldpop.org) | `worldpop` |

**Precipitation & drought**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/chc.png" height="20"> | [Climate Hazards Center (UCSB)](https://www.chc.ucsb.edu/) | `chc` |
| <img src="docs/_images/logos/drought.svg" height="20"> | [Copernicus European Drought Observatory / NDMC](https://drought.emergency.copernicus.eu/) | `drought` |

**Renewable energy**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/solar_wind_atlas.svg" height="20"> | [Global Solar Atlas / Global Wind Atlas (World Bank/ESMAP)](https://globalsolaratlas.info/) | `solar-wind-atlas` |
| <img src="docs/_images/logos/nrel.svg" height="20"> | [National Laboratory of the Rockies (formerly NREL)](https://www.nlr.gov/) | `nrel` |
| <img src="docs/_images/logos/pvgis.svg" height="20"> | [PVGIS (EU JRC)](https://re.jrc.ec.europa.eu/pvg_tools/) | `pvgis` |

**SAR / radar imagery**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/asf.png" height="20"> | [Alaska Satellite Facility (ASF)](https://asf.alaska.edu/) | `asf` |

**Soil**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/soilgrids.svg" height="20"> | [ISRIC SoilGrids](https://www.isric.org/explore/soilgrids) | `soilgrids` |

**Tropical cyclones**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/tropycal.png" height="20"> | [Tropycal](https://tropycal.github.io/tropycal/) | `tropycal` |

**Vector basemaps & boundaries**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/osm.svg" height="20"> | [OpenStreetMap](https://www.openstreetmap.org/) | `osm` |
| <img src="docs/_images/logos/overture.svg" height="20"> | [Overture Maps Foundation](https://overturemaps.org/) | `overture` |
|   | [geoBoundaries](https://www.geoboundaries.org/) | `admin` |

**Weather forecast (NWP)**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/nwp.png" height="20"> | [Herbie (NWP archive access)](https://herbie.readthedocs.io) | `nwp` |

**Weather radar**

| | Provider | `data_source` |
|---|---|---|
| <img src="docs/_images/logos/radar.svg" height="20"> | [NOAA NEXRAD](https://www.roc.noaa.gov/) | `radar` |

Logos are each provider's own mark, used only to identify which service a backend talks to
(not an endorsement of earthlens by that provider) — see
[docs/_images/logos/ATTRIBUTION.md](docs/_images/logos/ATTRIBUTION.md) for sourcing and rights
notes on every logo, including the handful of providers with no distinct mark of their own.


Installation
------------

`earthlens` is published on conda-forge and PyPI.

```bash
# pip — latest release
pip install earthlens

# conda
conda install -c conda-forge earthlens

# pip — bleeding edge
pip install git+https://github.com/serapeum-org/earthlens
```

To list all available versions on your platform:

```bash
conda search earthlens --channel conda-forge
```

A plain `pip install` is enough — earthlens pulls in everything it needs.

Backend SDKs are optional and pulled in by extras:

```bash
pip install earthlens[ecmwf]   # cdsapi
pip install earthlens[s3]      # boto3 + botocore
pip install earthlens[gee]     # earthengine-api
pip install earthlens[all]     # every backend SDK
```

`[all]` deliberately omits exactly two extras — `argo` and `osm-pbf`. `argopy` requires
`xarray>=2025.7` while `openeo` (which *is* in `all`) caps `xarray<2025.1.2`, and `pyrosm`
pulls the sdist-only `cykhash`, which would make `[all]` need a C compiler. `osm` itself
**is** included. Both excluded extras install fine on their own — see
[what `[all]` excludes](https://serapeum-org.github.io/earthlens/installation/#what-earthlensall-excludes-and-why).

For a development environment the repo is a [uv](https://docs.astral.sh/uv/)
workspace — `dev` and `docs` are dependency **groups**, not extras:

```bash
uv sync --extra all --group dev
```

See [Contributing](https://serapeum-org.github.io/earthlens/contributing/) for
the full setup.


Quick examples per backend
--------------------------

**CHIRPS daily rainfall** — anonymous FTP, no credentials.

```python
from earthlens.core import EarthLens

EarthLens(
    data_source="chc",
    temporal_resolution="daily",
    start="2009-01-01",
    end="2009-01-10",
    variables=["precipitation"],
    lat_lim=[4.19, 4.64],
    lon_lim=[-75.65, -74.73],
    path="data/chirps",
).download(cores=4)  # parallel FTP fetch
```

**ERA5 monthly via AWS public S3** — unsigned, fast, no API key.

```python
EarthLens(
    data_source="amazon-s3",
    temporal_resolution="monthly",
    start="2020-01-01",
    end="2020-12-01",
    variables=["air_temperature_at_2_metres", "precipitation_amount_1hour_Accumulation"],
    lat_lim=[30.0, 35.0],
    lon_lim=[28.0, 35.0],
    path="data/era5-s3",
).download()
```

**ECMWF CDS with on-the-fly aggregation.** Downloads daily ERA5, then writes
monthly GeoTIFFs aggregated with the right reduction per variable (mean for
temperature, sum for precipitation):

```python
from earthlens.core import EarthLens, AggregationConfig

EarthLens(
    data_source="ecmwf",
    temporal_resolution="daily",
    start="2022-06-01",
    end="2022-08-31",
    variables={
        "reanalysis-era5-single-levels": [
            "2m-temperature",
            "total-precipitation",
        ],
    },
    lat_lim=[37.0, 38.0],
    lon_lim=[23.0, 24.0],
    path="data/athens-summer",
).download(aggregate=AggregationConfig(freq="1MS", op="auto"))
```

**Google Earth Engine** — server-side collection, downloaded as GeoTIFFs. The
request is `{asset_id: [band, ...]}`, and GEE needs a service account:

```python
EarthLens(
    data_source="gee",
    temporal_resolution="monthly",       # one composite image per month
    start="2020-06-01",
    end="2020-08-31",
    variables={"UCSB-CHG/CHIRPS/DAILY": ["precipitation"]},
    lat_lim=[28.0, 32.0],
    lon_lim=[30.0, 34.0],
    path="data/gee",
    scale=5566,                          # output pixel size in metres
).authenticate(
    service_account="my-sa@my-project.iam.gserviceaccount.com",
    service_key="/path/to/key.json",
).download()
```


Aggregation: state vs flux
--------------------------

ERA5 mixes two physically distinct kinds of variables:

- **State** variables are instantaneous samples — temperature, SST, soil
  moisture, snow depth. Aggregating in time means **averaging**.
- **Flux** variables are accumulated over each timestep — precipitation,
  radiation, evaporation, surface heat fluxes. Aggregating in time means
  **summing**.

Mixing those up produces silently wrong results (a "monthly mean" of
precipitation under-reports rainfall by ~30×). earthlens's catalog tags every
variable with `is_flux`, and `op="auto"` reads that flag to pick the right
reduction:

```python
from earthlens.ecmwf import Catalog
spec = Catalog().get_variable(
    "reanalysis-era5-single-levels", "total-precipitation"
)
print(spec.is_flux)  # True  -> auto-aggregate by SUM
```

You can override with `op="mean" | "sum" | "max" | "min"` when you know
better than the catalog.


Authentication
--------------

Roughly half the backends need no credentials at all. Common ones:

| Source | What you need |
|---|---|
| CHIRPS / CHC | Nothing — anonymous FTP. |
| Amazon S3, Copernicus DEM, GOES, NWM, NEXRAD | Nothing — unsigned, public buckets. |
| GDACS, GHSL, Overture, HDX, SoilGrids, PVGIS, admin | Nothing — public HTTP. |
| ECMWF / CDS | A free CDS account and a `~/.cdsapirc` with your API key. |
| GEE | A Google Earth Engine project and a service-account JSON key. |
| CMEMS, Earthdata, ASF, EUMETSAT, Sentinel Hub, openEO | A provider login. |
| OpenAQ, AirNow, FIRMS, WDPA, IUCN, NREL, GFW | A free API key or token. |

Where credentials go is **backend-specific**: some take them as constructor keywords (CMEMS's
`service_username=` / `service_password=`), others in `authenticate(...)` (GEE's `service_account=`, FIRMS's
`api_key=`), and most fall back to an environment variable. Each backend's page says which. The full
per-provider matrix is in
[Supported providers](https://serapeum-org.github.io/earthlens/reference/providers/).


Documentation
-------------

Full docs, API reference, architecture diagrams, and a gallery of domain-specific
example notebooks (hydrology, oceanography, agriculture, solar/wind energy,
heat waves, drought, snow & cryosphere, climate-change anomalies) live at:

> **<https://serapeum-org.github.io/earthlens/>**

Start here:

| Page | What it covers |
|---|---|
| [Getting started](https://serapeum-org.github.io/earthlens/getting-started/) | Install to first file on disk. |
| [Discovering datasets](https://serapeum-org.github.io/earthlens/discovery/) | `sources()` / `find()` / `search()` across all 48 providers. |
| [Supported providers](https://serapeum-org.github.io/earthlens/reference/providers/) | Keys, output kinds, auth, and extras for every backend. |
| [Temporal aggregation](https://serapeum-org.github.io/earthlens/aggregation/) | Reduce a stack into windowed composites. |
| [Troubleshooting](https://serapeum-org.github.io/earthlens/troubleshooting/) | When a download fails, and what to change. |
| [Migration guide](https://serapeum-org.github.io/earthlens/migration/) | Breaking changes by release. |
| [Architecture](https://serapeum-org.github.io/earthlens/overview/architecture/) | How the facade, registry, and backends fit together. |


Contributing
------------

Issues, PRs, and discussions are welcome on
[GitHub](https://github.com/serapeum-org/earthlens). The repo uses pre-commit
with **ruff** (`ruff-check` + `ruff-format`), mypy, and bandit — install the
hooks once with `pre-commit install`.

See the [contributing guide](https://serapeum-org.github.io/earthlens/contributing/)
for the workspace layout, how to run the tests, and how to add a new provider
backend.


License
-------

GPL v3. See [LICENSE](LICENSE).
