Quickstart¶
Zero-to-working setup for zagg.
Prerequisites¶
- Python >= 3.12
- uv package manager
- NASA Earthdata account (free)
- For Lambda deployment: AWS account, Docker (for building layers)
Installation¶
git clone https://github.com/englacial/zagg.git
cd zagg
uv sync --all-groups
NASA Earthdata Authentication¶
zagg reads ICESat-2 ATL06 data from NSIDC's S3 buckets in AWS us-west-2.
This requires NASA Earthdata credentials. Three methods, in order of precedence:
1. Environment variables
export EARTHDATA_USERNAME=your_username
export EARTHDATA_PASSWORD=your_password
2. ~/.netrc file (recommended for persistent use)
machine urs.earthdata.nasa.gov
login YOUR_USERNAME
password YOUR_PASSWORD
3. Interactive prompt
The earthaccess library will prompt for credentials as a fallback if neither
of the above is configured.
Note
These are read credentials for source data at NSIDC. Output store credentials (for writing results to S3) are separate AWS credentials -- see Writing to S3 below.
Building a Catalog¶
The catalog step queries NASA's CMR-STAC (public, no auth needed) and builds a shard-to-granule mapping for the grid in your config:
uv run python -m zagg.catalog --config atl06.yaml --short-name ATL06 --cycle 22 \
--polygon antarctica.geojson
This produces a JSON file (e.g., shardmap_ATL06_2024-01-06_2024-04-07.json) that maps
parent morton cells to the S3 URLs of HDF5 granules containing data for those
cells. The processing step consumes this file.
To inspect the chunking interactively -- shard outlines, granule footprints,
and a grid that appears on zoom -- use the shard-map viewer
(pip install zagg[viz]). See the
shard-map viewer notebook (archived),
which runs on a synthetic example (no network needed) and includes manual
in-browser verification instructions.
Local Processing¶
The simplest path -- no AWS Lambda needed:
# Process one cell, write to local Zarr:
uv run python -m zagg --config src/zagg/configs/atl06.yaml \
--catalog catalog_ATL06_cycle22_order6.json \
--store ./output.zarr --max-cells 1
# Dry run (shows what would happen, no processing):
uv run python -m zagg --config src/zagg/configs/atl06.yaml \
--catalog catalog_ATL06_cycle22_order6.json --dry-run
Writing to S3 (Optional)¶
To write output to S3, set the store path to an S3 URI:
uv run python -m zagg --config src/zagg/configs/atl06.yaml \
--catalog catalog_ATL06_cycle22_order6.json \
--store s3://my-bucket/output.zarr
This requires AWS credentials configured via one of:
~/.aws/credentials- Environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) - An IAM role (e.g., on EC2 or Lambda)
Lambda Deployment (Optional)¶
For full-scale processing, zagg dispatches work to AWS Lambda. See
Lambda Deployment for details. The short version:
- AWS prerequisites: IAM role with S3 + Lambda permissions, S3 bucket for output
- Build:
deployment/aws/build_function.shanddeployment/aws/build_layer.sh - Deploy:
deployment/aws/deploy.sh - Run:
python deployment/aws/invoke_lambda.py --config atl06.yaml --catalog catalog.json
The function name defaults to process-shard but is configurable via
the ZAGG_LAMBDA_FUNCTION_NAME environment variable.
Configuration¶
Pipeline behavior is defined in a YAML config with three required sections:
data_source:
reader: h5coro
groups: [gt1l, gt1r, gt2l, gt2r, gt3l, gt3r]
coordinates:
latitude: "/{group}/land_ice_segments/latitude"
longitude: "/{group}/land_ice_segments/longitude"
variables:
h_li: "/{group}/land_ice_segments/h_li"
aggregation:
coordinates:
cell_ids:
dtype: uint64
fill_value: 0
variables:
count:
function: len
source: h_li
dtype: int64
fill_value: 0
output:
grid:
type: healpix
indexing_scheme: nested
child_order: 12
# Optional top-level fields:
catalog: catalog_ATL06_cycle22_order6.json
store: ./output.zarr
worker: # lambda backend: pick a pre-provisioned worker size
memory: 2048 # 2048 | 4096 | 8192 (see docs/deployment/lambda.md)
extra_disk: false
HEALPix output writes a morton-hive, sharded store by default: one
self-describing leaf zarr per shard under a morton digit tree, with the shard's
inner chunks bundled into one object per array — see
Hive store layout and Sharded storage.
An explicit output.store_layout: flat (the single shared zarr store) remains
for interop/debug but is deprecated — removal is tracked in
issue #251. The default is
grid-keyed, not pipeline-keyed: HEALPix raster output writes hive leaves too
(issue #247); rectilinear
grids keep the flat shared store.
Where the run writes, and whether it touches what it finds¶
output.store declares the destination, and output.touch declares what the
skip path does to it:
output:
store: s3://us-west-2.opendata.source.coop/englacial/zagg/demo/atl06.zarr
touch: auto # auto (default) | always | never
A run that skips a unit — everything already current — writes nothing for it.
That is correct for the store but invisible to a bucket expiration rule, so
zagg refreshes LastModified across the skipped unit's footprint
(issue #388). S3 has no touch
API, so the refresh is a server-side self-copy, and on a versioned bucket a
self-copy writes a new full-size version rather than updating a timestamp in
place.
Whether that is worth paying depends on whether the destination expires
objects, which zagg cannot read cross-account
(s3:GetLifecycleConfiguration is bucket-owner only) and which no bucket name
encodes. So it is declarable:
| value | behaviour |
|---|---|
auto (default) |
Touch, unless the destination is a known published bucket (zagg.store._PUBLISHED_BUCKETS, Source Cooperative today). Those are archival — no expiration rule to defeat — and versioned, so touching them only mints noncurrent versions (issue #495 phase 4). |
always |
Touch regardless of destination. For an un-negotiated external target whose expiry rule you know about but whose name says nothing. |
never |
Never touch, local paths included. For an archival destination, or any store where version churn outweighs the protection. |
auto infers; the other two override the inference
(issue #501). A path the policy
excludes is reported as not applicable — touch_skipped_paths in the run
record — never as a failure.
See src/zagg/configs/atl06.yaml for a complete example and the
custom aggregations notebook (archived)
for customization examples.
Reading the output¶
For products with per-cell t-digest fields, the zagg.readers package
reconstructs dense, spatially faithful (64, 64, n_bins) tensors — with an
occupancy mask and a shared (offset, gain) z-window per block (the
reader contract)
— or lossless raw value vectors from the stored digests. The
t-digest reader notebook (archived)
walks through read_tensors and read_raw_values end to end on a self-contained
synthetic store.