Metadata-Version: 2.5
Name: umbra-py
Version: 0.1.0
Summary: A Python-first toolkit to make Umbra SAR open data easy to discover, load, download, and analyze.
Project-URL: Homepage, https://umbra-py.space/
Project-URL: Documentation, https://umbra-py.space/
Project-URL: Repository, https://github.com/reesehammer/umbra-py
Project-URL: Issues, https://github.com/reesehammer/umbra-py/issues
Project-URL: Umbra Open Data, https://umbra.space/open-data/
Author: umbra-py contributors
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: earth-observation,geospatial,gis,remote-sensing,sar,stac,synthetic-aperture-radar,umbra
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Scientific/Engineering :: Image Processing
Requires-Python: >=3.10
Requires-Dist: click>=8.1
Requires-Dist: defusedxml>=0.7
Requires-Dist: requests>=2.28
Provides-Extra: ai
Requires-Dist: requests>=2.28; extra == 'ai'
Provides-Extra: all
Requires-Dist: folium>=0.14; extra == 'all'
Requires-Dist: matplotlib>=3.6; extra == 'all'
Requires-Dist: numpy>=1.23; extra == 'all'
Requires-Dist: rasterio>=1.3; extra == 'all'
Requires-Dist: sarpy>=1.3; extra == 'all'
Requires-Dist: stac-geoparquet>=0.6; extra == 'all'
Requires-Dist: xarray>=2023.1; extra == 'all'
Provides-Extra: convert
Requires-Dist: numpy>=1.23; extra == 'convert'
Requires-Dist: rasterio>=1.3; extra == 'convert'
Requires-Dist: sarpy>=1.3; extra == 'convert'
Provides-Extra: dask
Requires-Dist: dask[array]>=2023.5; extra == 'dask'
Requires-Dist: numpy>=1.23; extra == 'dask'
Requires-Dist: rasterio>=1.3; extra == 'dask'
Requires-Dist: xarray>=2023.1; extra == 'dask'
Provides-Extra: dev
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: jsonschema>=4.18; extra == 'dev'
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-cov>=4.1; extra == 'dev'
Requires-Dist: pytest>=7.4; extra == 'dev'
Requires-Dist: pyyaml>=6; extra == 'dev'
Requires-Dist: responses>=0.23; extra == 'dev'
Requires-Dist: ruff<0.16,>=0.4; extra == 'dev'
Requires-Dist: types-defusedxml>=0.7; extra == 'dev'
Requires-Dist: types-requests>=2.28; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-click>=0.8; extra == 'docs'
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.25; extra == 'docs'
Provides-Extra: export
Requires-Dist: stac-geoparquet>=0.6; extra == 'export'
Provides-Extra: langchain
Requires-Dist: folium>=0.14; extra == 'langchain'
Requires-Dist: langchain-core>=0.3; extra == 'langchain'
Requires-Dist: matplotlib>=3.6; extra == 'langchain'
Requires-Dist: numpy>=1.23; extra == 'langchain'
Requires-Dist: rasterio>=1.3; extra == 'langchain'
Provides-Extra: llamaindex
Requires-Dist: folium>=0.14; extra == 'llamaindex'
Requires-Dist: llama-index-core>=0.11; extra == 'llamaindex'
Requires-Dist: matplotlib>=3.6; extra == 'llamaindex'
Requires-Dist: numpy>=1.23; extra == 'llamaindex'
Requires-Dist: rasterio>=1.3; extra == 'llamaindex'
Provides-Extra: load
Requires-Dist: numpy>=1.23; extra == 'load'
Requires-Dist: rasterio>=1.3; extra == 'load'
Requires-Dist: xarray>=2023.1; extra == 'load'
Provides-Extra: mcp
Requires-Dist: folium>=0.14; extra == 'mcp'
Requires-Dist: matplotlib>=3.6; extra == 'mcp'
Requires-Dist: mcp>=2; extra == 'mcp'
Requires-Dist: numpy>=1.23; extra == 'mcp'
Requires-Dist: rasterio>=1.3; extra == 'mcp'
Provides-Extra: serve
Requires-Dist: fastapi>=0.110; extra == 'serve'
Requires-Dist: uvicorn>=0.27; extra == 'serve'
Provides-Extra: viz
Requires-Dist: folium>=0.14; extra == 'viz'
Requires-Dist: matplotlib>=3.6; extra == 'viz'
Requires-Dist: numpy>=1.23; extra == 'viz'
Requires-Dist: rasterio>=1.3; extra == 'viz'
Description-Content-Type: text/markdown

# umbra-py

[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![Python](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/)
[![CI](https://github.com/reesehammer/umbra-py/actions/workflows/ci.yml/badge.svg)](https://github.com/reesehammer/umbra-py/actions/workflows/ci.yml)
[![codecov](https://codecov.io/gh/reesehammer/umbra-py/branch/main/graph/badge.svg)](https://codecov.io/gh/reesehammer/umbra-py)
[![Docs](https://img.shields.io/badge/docs-umbra--py.space-informational.svg)](https://umbra-py.space/)

**A Python toolkit to discover, preview, load, and analyze [Umbra](https://umbra.space/open-data/) open SAR data.**

Umbra publishes 16–25 cm synthetic aperture radar as CC BY 4.0 open data, but
there is no search API — only a 17+ TB S3 bucket and a static STAC tree.
`umbra-py` is the missing layer: search, preview, download, and analysis-ready
arrays without writing the same 500 lines of glue first.

📖 **Docs:** [umbra-py.space](https://umbra-py.space/)
· **Showcase:** [browse the archive in the browser](https://umbra-py.space/showcase/)
(no install)

> **Status:** v0.1.0, first public release. Discovery, download, xarray loading,
> SICD → geocoded COG, change/timescan composites, chips, a local STAC API, and
> an MCP server all ship. This is **not** an InSAR toolbox (phase is not
> preserved through convert). Not affiliated with Umbra Lab, Inc.

## Install

```bash
pip install umbra-py              # core: search + download + metadata
pip install "umbra-py[load]"      # + xarray / rasterio
pip install "umbra-py[viz]"       # + quicklooks, maps, galleries
pip install "umbra-py[convert]"   # + SICD → geocoded COG
pip install "umbra-py[all]"       # convert + load + viz + export
```

Python 3.10+. Other extras (`dask`, `serve`, `mcp`, `ai`, `langchain`,
`llamaindex`) are listed in the [install guide](https://umbra-py.space/install/).

## Five minutes to a scene

Fetch the weekly catalog snapshot, then search and preview offline. A live
walk of the bucket (`umbra search` without `--local`) works but is slow.

```bash
pip install "umbra-py[viz,load]"
umbra index fetch
umbra search --local --area Centerfield --product GEC --limit 3
umbra gallery --local --area Centerfield --limit 6 --out gallery.html --db
```

```python
from umbra_py import CatalogIndex, to_xarray

with CatalogIndex.from_release() as index:
    item = next(iter(index.search(area="Centerfield", product_types=["GEC"], limit=1)))

# Stream a downsampled window over HTTP — no multi-GB download. Needs [load].
da = to_xarray(item, max_size=1024, db=True)
print(item.summary())
```

If the snapshot is missing, the same search against the live bucket is
`UmbraCatalog().search(...)` / `umbra search --area Centerfield`.

## What you can do

More detail, options, and caveats live in the
[docs](https://umbra-py.space/).

**Search** by bbox, place name, polygon, or Umbra task (`area=`).
`--local` reads the snapshot; omit it to walk S3.

```python
from umbra_py import UmbraCatalog

for item in UmbraCatalog().search(area="Centerfield", product_types=["GEC"], limit=5):
    print(item.summary())
```

**Preview** without downloading the scene: `umbra gallery`, `umbra quicklook
<stac-url> --out scene.png --db`, `umbra view <stac-url>` (full-res tiles),
or `umbra change --area Centerfield --out change.png`.

**Load** a geocoded GEC into xarray or a GeoTIFF (`to_xarray`, `to_geotiff`,
`to_stack`). Needs `[load]`.

**Convert** a SICD to a north-up COG (`sicd_to_geocoded_cog`, `umbra convert`).
Needs `[convert]`. Open products generally have no radiometric metadata, so
`--calibrate` / `--noise-model measured` refuse rather than invent numbers.
See [limitations](https://umbra-py.space/guides/limitations/).

**Chip** scenes into georeferenced ML tiles: `umbra chips --area Centerfield
--out chips/`.

**Drive it from an agent.** Zero-install MCP server:

```bash
uvx --from 'umbra-py[mcp]' umbra-mcp
```

```json
{
  "mcpServers": {
    "umbra": {
      "command": "uvx",
      "args": ["--from", "umbra-py[mcp]", "umbra-mcp"]
    }
  }
}
```

That command is published to the [MCP registry](https://registry.modelcontextprotocol.io/)
as `io.github.reesehammer/umbra-mcp`. A local STAC API is `umbra serve` (needs
`[serve]`); `docker compose up` is the one-command form. There is no public
hosted instance yet.

<!-- mcp-name: io.github.reesehammer/umbra-mcp -->

## What the data looks like

| Asset | What it is | Use it for |
|-------|------------|------------|
| `GEC`  | Geocoded cloud-optimized GeoTIFF | Map-ready imagery. **Start here.** |
| `CSI`  | Color sub-aperture GeoTIFF | Quick-look RGB, not a measurement |
| `SIDD` | Geocoded detected image (NITF) | Detected imagery in a standard format |
| `SICD` | Complex data in the radar slant plane (NITF) | Phase-preserving work, InSAR *inputs* |
| `CPHD` | Compensated phase history | Custom image formation |

`umbra-py` downloads SICD/CPHD and can geocode a SICD to amplitude. It does
not form interferograms or compute coherence.

## Data license & attribution

Umbra's imagery is **CC BY 4.0**. If you use or redistribute the data or
derived products you must attribute Umbra, e.g.:

> Contains Umbra open data, licensed under CC BY 4.0.

`umbra-py` itself is **Apache 2.0** ([LICENSE](LICENSE)). The two licenses
are independent and compatible.

## Citing umbra-py

Machine-readable metadata lives in [CITATION.cff](CITATION.cff). GitHub
renders it as a **"Cite this repository"** button. Please also honor the
CC BY 4.0 line above for any Umbra data you use.

## Community

- [Contributing](CONTRIBUTING.md) · [Code of Conduct](CODE_OF_CONDUCT.md) · [Security](SECURITY.md)
- [Example notebooks](examples/) · [Limitations](https://umbra-py.space/guides/limitations/)

## What's next

Launch work, not new surface: list the package on the
[AWS Open Data registry](https://registry.opendata.aws/umbra-open-data/) and
[STAC Index](https://stacindex.org/); talk to Umbra about the name and
offering the weekly index upstream; mint a Zenodo DOI with the first tag.
A public `umbra serve` instance waits on that conversation. Live verification
of the Canopy backend and of inferred noise models against a real calibrated
product wait on a token / a scene that carries the metadata.

See [CONTRIBUTING.md](CONTRIBUTING.md) to get involved.

## Acknowledgements

Built on the SAR open-source community, including
[`sarpy`](https://github.com/ngageoint/sarpy) and Umbra's open data program.
**Not affiliated with or endorsed by Umbra Lab, Inc.**
