Skip to content

Commit 7efbe3f

Browse files
authored
Merge pull request #31 from CU-ESIIL/codex/redesign-and-expand-climate_cube_math-website
Revamp docs navigation and design
2 parents 1df0519 + 2975d22 commit 7efbe3f

22 files changed

+1003
-54
lines changed

docs/assets/img/cube_axes.png

Lines changed: 1 addition & 0 deletions
Loading

docs/assets/img/lexcube_hero.png

Lines changed: 1 addition & 0 deletions
Loading

docs/concepts/cubes.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# What is a cube?
2+
3+
A **cube** is an `xarray.DataArray` or `xarray.Dataset` whose values are organized along shared space-time axes such as `(time, y, x)` for single-band cubes or `(time, y, x, band)` for multispectral collections. Every pixel stores the value of an environmental variable (e.g., NDVI, temperature, precipitation) measured at `(y, x)` and instant `time`.
4+
5+
![Cube axes diagram](../assets/img/cube_axes.png){ .cube-image }
6+
7+
## Loading a PRISM cube
8+
9+
```python
10+
import cubedynamics as cd
11+
12+
cube = cd.load_prism_cube(
13+
lat=40.0,
14+
lon=-105.25,
15+
start="2000-01-01",
16+
end="2020-12-31",
17+
variable="ppt",
18+
)
19+
20+
cube
21+
```
22+
23+
`load_prism_cube` streams the requested area/time window from PRISM into memory as a cube so you can immediately apply verbs. Swap in `load_gridmet_cube`, `load_sentinel2_ndvi_cube`, or any custom loader that returns an `xarray` object with the standard axes.
24+
25+
## Why cubes?
26+
27+
Satellite constellations (Sentinel-2, Landsat), gridded climate products (gridMET, PRISM), and model reanalyses naturally produce cube-shaped data because measurements are already tied to regular spatiotemporal coordinates. By sticking with `xarray`, CubeDynamics benefits from labeled dimensions, lazy loading (`dask`), and metadata-aware computations.
28+
29+
CubeDynamics focuses on **streaming cubes** instead of requiring large local downloads. Utilities such as `cubedynamics.data.sentinel2.load_s2_cube` wrap remote APIs (e.g., Cubo) so you can request an area/time window and immediately operate on the returned cube in notebooks or scripts.
30+
31+
## Cube processing layers
32+
33+
The original documentation described four conceptual layers that remain relevant today:
34+
35+
1. **Data layer** – load space-time cubes (`load_s2_cube`, `load_prism_cube`, `load_gridmet_cube`).
36+
2. **Indices & anomalies layer** – derive vegetation indices and z-scores (`from cubedynamics import verbs as v`; `v.ndvi_from_s2`, `v.zscore`, `v.anomaly`).
37+
3. **Synchrony layer** – measure rolling correlation and tail dependence versus a reference pixel (`v.correlation_cube`, `rolling_corr_vs_center`, `rolling_tail_dep_vs_center`).
38+
4. **Visualization layer** – explore cubes interactively with the Lexcube widget (`v.show_cube_lexcube`) and QA plots (`plot_median_over_space`).
39+
40+
## Earth System Data Cube context
41+
42+
CubeDynamics builds on the Earth System Data Cube (ESDC) paradigm: treat spatiotemporal grids as analysis-ready cubes that can flow into machine learning or statistical analyses. Unlike infrastructure-focused systems (Open Data Cube, Earth System Data Lab), CubeDynamics emphasizes a **grammar of analysis**. Any cube—PRISM, gridMET, Sentinel-2 NDVI via Cubo, Lexcube outputs, or DeepESDL—becomes a first-class citizen in the same `pipe(cube) | verbs` interface.

docs/concepts/pipe_and_verbs.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Pipe syntax & verbs
2+
3+
CubeDynamics exposes a lightweight `Pipe` object so `xarray` workflows read like recipes. Each verb is a factory that returns a callable, letting you configure parameters upfront and apply them later with the `|` operator.
4+
5+
## The Pipe object
6+
7+
```python
8+
from cubedynamics import pipe
9+
10+
pipe_obj = pipe(cube)
11+
```
12+
13+
`pipe(value)` wraps any `xarray.DataArray` or `xarray.Dataset` without altering it. Use the `|` operator to apply verbs. In notebooks the last `Pipe` expression in a cell auto-displays the wrapped object, so calling `.unwrap()` is optional unless you immediately need the `xarray` object.
14+
15+
## Chaining verbs
16+
17+
```python
18+
from cubedynamics import pipe, verbs as v
19+
20+
out = (
21+
pipe(cube)
22+
| v.anomaly(dim="time")
23+
| v.variance(dim="time")
24+
).unwrap()
25+
```
26+
27+
Each verb receives the previous output. Pipes simply pass the cube along, so as long as the object behaves like an `xarray` structure the chain continues. The pattern mirrors tidyverse pipes, but in Python.
28+
29+
## Core verb categories
30+
31+
Verbs are grouped into focused namespaces:
32+
33+
- **Transform verbs** – reshape, filter, or derive indices (`v.anomaly`, `v.month_filter`, `v.ndvi_from_s2`).
34+
- **Stats verbs** – compute variance, z-scores, correlations, or rolling metrics (`v.variance`, `v.zscore`, `v.correlation_cube`).
35+
- **IO verbs** – persist results without breaking the chain (`v.to_netcdf`).
36+
- **Visualization verbs** – preview cubes inline (`v.show_cube_lexcube`, QA plots) before exporting.
37+
- **Models (coming soon)** – wrappers around ML/statistical models that accept cubes as inputs.
38+
39+
See the [Verbs Reference](../reference/verbs_transforms.md) section for detailed signatures and examples.
40+
41+
## Lexcube integration
42+
43+
Lexcube visualizations follow the same pattern:
44+
45+
```python
46+
pipe(cube) | v.show_cube_lexcube(title="NDVI anomalies")
47+
```
48+
49+
The verb renders the widget as a side effect and returns the original cube. Helpers such as `cd.show_cube_lexcube` are available when you are not inside a pipe chain.
50+
51+
## Define your own verbs
52+
53+
Verbs follow a simple factory pattern—accept configuration parameters now, return a callable that receives the cube later:
54+
55+
```python
56+
def my_custom_op(param):
57+
def _inner(da):
58+
# operate on da (xarray DataArray/Dataset)
59+
return da
60+
return _inner
61+
62+
from cubedynamics import pipe
63+
64+
result = pipe(cube) | my_custom_op(param=42)
65+
```
66+
67+
Register your verb in your own module or import it into notebooks, then use it alongside the built-in operations. Tests under `tests/` cover both direct invocation and pipe usage, so mirror that pattern when adding new verbs.

docs/css/extra.css

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* Extra styling for CubeDynamics site */
2+
3+
:root {
4+
--cube-accent: #1f3b74;
5+
}
6+
7+
body {
8+
background-color: #f5f5f7;
9+
}
10+
11+
/* Buttons */
12+
.md-button {
13+
transition: transform 0.1s ease, box-shadow 0.1s ease, background-color 0.1s ease;
14+
}
15+
16+
.md-button--primary {
17+
font-weight: 600;
18+
background-color: #1f3b74;
19+
color: #ffffff;
20+
}
21+
22+
/* Hover effects for buttons */
23+
.md-button:hover {
24+
transform: translateY(-1px);
25+
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12);
26+
}
27+
28+
/* Cards / callout boxes */
29+
.cube-card {
30+
border-radius: 0.75rem;
31+
padding: 1.25rem 1.5rem;
32+
margin: 1.5rem 0;
33+
background-color: #f5f5f7;
34+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);
35+
border: 1px solid rgba(31, 59, 116, 0.08);
36+
}
37+
38+
.cube-card h3 {
39+
margin-top: 0;
40+
color: #1f3b74;
41+
}
42+
43+
/* Make images look nicer */
44+
.cube-image {
45+
border-radius: 0.5rem;
46+
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.16);
47+
margin: 1rem 0;
48+
}
49+
50+
/* Slightly larger code blocks */
51+
.md-typeset code {
52+
font-size: 0.92em;
53+
background-color: rgba(31, 59, 116, 0.05);
54+
border-radius: 4px;
55+
padding: 0.05rem 0.35rem;
56+
}
57+
58+
/* Carry over legacy heading & image styles */
59+
.md-typeset h1 {
60+
color: var(--cube-accent);
61+
}
62+
63+
.md-content img {
64+
border-radius: 6px;
65+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
66+
}

docs/dev/changelog.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Changelog
2+
3+
## Unreleased
4+
5+
- Rebranded the project as CubeDynamics with a pipe-first API.
6+
- Added docs for `pipe`, `anomaly`, `month_filter`, `variance`, `correlation_cube`, and `to_netcdf` verbs.
7+
- Documented the `Pipe` helper and new operations reference structure.
8+
- Update (2025): reorganized the MkDocs navigation into Concepts, Getting Started, Examples, Verbs Reference, Related Work, and Development sections.
9+
- Added new CSS, hero buttons, and cards to align with the refreshed color palette.
10+
11+
## Earlier work
12+
13+
See the Git history for previous releases and prototype implementations while we stabilize the streaming adapters.

docs/dev/contributing.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Contributing
2+
3+
Thank you for helping build CubeDynamics! This project keeps climate cube math lightweight, streaming-first, and well documented.
4+
5+
## Repository layout
6+
7+
```text
8+
src/
9+
cubedynamics/
10+
__init__.py # re-exports verbs and pipe helpers
11+
piping.py # Pipe class + helpers
12+
ops/
13+
__init__.py
14+
transforms.py # anomaly, month_filter, NDVI helpers
15+
stats.py # variance, correlation_cube, rolling metrics
16+
io.py # to_netcdf and future exporters
17+
docs/ # MkDocs + Material site
18+
```
19+
20+
Tests live under `tests/` and rely on `pytest`. Documentation is built with MkDocs (Material theme); run `mkdocs serve` while editing docs.
21+
22+
## Local setup
23+
24+
1. Fork or branch from `main` and create feature branches for your work.
25+
2. Install dependencies in editable mode (e.g., `python -m pip install -e .[dev]` once extras are defined).
26+
3. Run `pytest` plus any relevant notebooks/scripts before opening a pull request.
27+
28+
## Adding a new verb
29+
30+
1. Create the implementation in `cubedynamics/ops/`.
31+
2. Follow the factory pattern:
32+
33+
```python
34+
def verb_name(...):
35+
def _inner(cube):
36+
...
37+
return _inner
38+
```
39+
40+
3. Re-export the verb in `cubedynamics/verbs/__init__.py` (and optionally `cubedynamics/__init__.py`) so users can `from cubedynamics import verbs as v` and call `v.verb_name` directly.
41+
4. Document the new function under `docs/reference/verbs_*.md` and add examples using the pipe syntax.
42+
5. Write tests that cover direct invocation and pipe usage.
43+
44+
## Streaming philosophy
45+
46+
- **Streaming-first** – functions should operate on iterables, chunked dask arrays, or lazy `xarray` objects whenever possible.
47+
- **Side-effect aware** – avoid downloading entire archives; expose hooks for caching and resuming streams.
48+
- **Composable** – keep verbs pure (no global state) so they chain cleanly in the pipe system.
49+
50+
## Docs + website
51+
52+
- Keep the navigation structure in `mkdocs.yml` aligned with the docs files.
53+
- Use the new section layout (Concepts, Getting Started, Examples, Verbs Reference, Related Work, Development) when adding content.
54+
- Remember that Lexcube widgets do not render on the static site; include screenshots or Binder links where appropriate.
55+
56+
## Opening a pull request
57+
58+
- Describe the change, test coverage, and any data access requirements.
59+
- Update the [Roadmap](roadmap.md) or [Changelog](changelog.md) when user-facing features ship.
60+
- Issues and discussions are welcome for roadmap ideas, new data sources, or Lexcube visualizations.

docs/dev/roadmap.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Roadmap
2+
3+
This roadmap reflects near-term development priorities plus longer-term ambitions for CubeDynamics.
4+
5+
## Near term
6+
7+
- **Stabilize loaders** – polish `load_prism_cube`, `load_gridmet_cube`, and `load_sentinel2_ndvi_cube` with clearer AOI validation and chunk hints.
8+
- **Reference verbs** – fill out stats/transform IO docs and ensure every helper has an example in the new `reference/` section.
9+
- **Sentinel-2 notebooks** – keep the NDVI z-score tutorial in sync with the docs, including Lexcube screenshots and Binder links.
10+
- **Correlation cubes** – graduate `v.correlation_cube` and rolling correlation verbs from stubs to tested implementations.
11+
12+
## Mid term
13+
14+
- **IO expansion** – add `v.to_zarr`/`v.to_geotiff` exporters plus metadata helpers for catalog integration.
15+
- **Visualization palette** – expose more QA plot verbs (`quick_map`, `hist_over_time`) using the new color scheme.
16+
- **Model verbs** – prototype cube-aware regression/classification verbs that emit predictions in cube form.
17+
18+
## Long term
19+
20+
- **Catalog integration** – connect to ESDC, Open Data Cube, and DeepESDL catalogs so users can pull remote cubes via a unified interface.
21+
- **Storage backends** – optional local stores (Zarr + Parquet metadata) for caching repeated AOI requests.
22+
- **Community examples** – curated gallery of climate–vegetation analyses contributed by the community.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Climate–NDVI correlation cube
2+
3+
Correlation cubes capture how vegetation anomalies co-vary with climate drivers. This example merges the earlier correlation notes with the new pipe-first grammar.
4+
5+
## Load climate and NDVI cubes
6+
7+
```python
8+
import cubedynamics as cd
9+
from cubedynamics import pipe, verbs as v
10+
11+
prism_cube = cd.load_prism_cube(
12+
lat=40.0,
13+
lon=-105.25,
14+
start="2000-01-01",
15+
end="2020-12-31",
16+
variable="ppt",
17+
)
18+
19+
ndvi_z = cd.load_sentinel2_ndvi_cube(
20+
lat=40.0,
21+
lon=-105.25,
22+
start="2018-01-01",
23+
end="2020-12-31",
24+
)
25+
```
26+
27+
## Prepare anomalies
28+
29+
```python
30+
climate_anom = (
31+
pipe(prism_cube)
32+
| v.anomaly(dim="time")
33+
).unwrap()
34+
```
35+
36+
## Compute correlation cube
37+
38+
```python
39+
corr = (
40+
pipe(ndvi_z)
41+
| v.correlation_cube(climate_anom, dim="time")
42+
).unwrap()
43+
```
44+
45+
The output stores correlation coefficients per pixel, aligned along the `time` coordinate (full-period or rolling depending on the configuration). Use it to spot areas where vegetation responds strongly to precipitation anomalies.
46+
47+
## Rolling correlation vs anchor pixels
48+
49+
`v.rolling_corr_vs_center` and `v.rolling_tail_dep_vs_center` extend the idea to within-cube synchrony (e.g., NDVI vs center pixel). They inherit the same pipe syntax:
50+
51+
```python
52+
rolling = (
53+
pipe(ndvi_z)
54+
| v.rolling_corr_vs_center(window_days=90, min_t=5)
55+
)
56+
```
57+
58+
## Related documentation
59+
60+
- [Correlation & synchrony cubes](../correlation_cubes.md)
61+
- [Sentinel-2 NDVI z-score cube](s2_ndvi_zscore.md)
62+
- [Verbs – Stats](../reference/verbs_stats.md)

0 commit comments

Comments
 (0)