Skip to content

Commit e357c42

Browse files
vinisalazarclaude
andcommitted
docs: add dedicated "The constraints dictionary" reference page
Explicitly document the constraints dictionary: its anatomy (the <dim>>= / <dim><= / <dim>_step keys), value types per dimension, strides, how to edit it by hand, partial constraints (omitted dims = full range), and what happens with no constraints (full global layer; download_layers prompts, load_layer/get_layer_url do not). Wire it into the nav and cross-link from the quickstart and the downloading/subsetting tutorial. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 601b948 commit e357c42

5 files changed

Lines changed: 155 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
1818
validation, and download flows are now covered without hitting the server.
1919
- CI enforces a minimum coverage of 90% on the offline test run.
2020

21+
### Documentation
22+
23+
- Added a dedicated "The constraints dictionary" page documenting the dictionary
24+
structure (keys, value types, strides), how to edit it manually, partial
25+
constraints, and what happens when no constraints are given.
26+
2127
## [1.0.0] - 2026-06-02
2228

2329
First stable release. Modernizes dependencies, reaches feature parity with the

docs/constraints.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# The constraints dictionary
2+
3+
Every request that subsets a layer — [`download_layers`][pyo_oracle.download_layers],
4+
[`load_layer`][pyo_oracle.load_layer], and [`get_layer_url`][pyo_oracle.get_layer_url]
5+
accepts a `constraints` dictionary. It tells the Bio-ORACLE ERDDAP server **which
6+
slice of a layer to return** along each of its dimensions.
7+
8+
You can build it the easy way with
9+
[`build_constraints`][pyo_oracle.build_constraints], or write/edit it by hand.
10+
This page describes its exact structure so you can do either with confidence.
11+
12+
## Anatomy
13+
14+
A layer is a grid with dimensions — typically `time`, `latitude`, `longitude`,
15+
and sometimes `depth`. For each dimension you want to subset, the dictionary
16+
holds **three keys**:
17+
18+
| Key pattern | Meaning | Example value |
19+
|-------------|---------|---------------|
20+
| `"<dim>>="` | Lower bound (inclusive) | `"latitude>=": -40` |
21+
| `"<dim><="` | Upper bound (inclusive) | `"latitude<=": -10` |
22+
| `"<dim>_step"` | Stride — take every *n*-th grid cell | `"latitude_step": 1` |
23+
24+
So a fully specified request looks like this:
25+
26+
```python
27+
constraints = {
28+
"time>=": "2000-01-01T00:00:00Z",
29+
"time<=": "2010-01-01T00:00:00Z",
30+
"time_step": 1,
31+
"latitude>=": -40,
32+
"latitude<=": -10,
33+
"latitude_step": 1,
34+
"longitude>=": 110,
35+
"longitude<=": 155,
36+
"longitude_step": 1,
37+
}
38+
```
39+
40+
This maps directly onto ERDDAP's griddap selection syntax
41+
`[(start):(stride):(stop)]` for each dimension.
42+
43+
### Value types
44+
45+
- **`time`** bounds are **ISO 8601 strings** with a trailing `Z`, e.g.
46+
`"2010-01-01T00:00:00Z"`. (Some datasets contain a single time step — check
47+
with [`info_layer`][pyo_oracle.info_layer].)
48+
- **`latitude` / `longitude` / `depth`** bounds are **numbers** in the
49+
dataset's units (degrees, metres).
50+
- **`<dim>_step`** is a positive **integer** stride. `1` keeps every cell; `10`
51+
keeps every tenth, downsampling the result.
52+
53+
!!! tip "Find the valid ranges first"
54+
The bounds must fall inside the layer's actual extent. Inspect it before
55+
building constraints:
56+
57+
```python
58+
info = pyo.info_layer("thetao_baseline_2000_2019_depthsurf")
59+
info["dimensions"] # {'time': (min, max), 'latitude': (min, max), ...}
60+
```
61+
62+
## Editing it manually
63+
64+
The dictionary is plain Python, so you can edit it like any `dict`. A few common
65+
operations:
66+
67+
```python
68+
# Start from a helper-built dict (or write one from scratch)
69+
constraints = pyo.build_constraints(
70+
"thetao_baseline_2000_2019_depthsurf",
71+
latitude=(-40, -10),
72+
longitude=(110, 155),
73+
)
74+
75+
# Widen the longitude range
76+
constraints["longitude<="] = 180
77+
78+
# Downsample latitude/longitude to every 5th cell
79+
constraints["latitude_step"] = 5
80+
constraints["longitude_step"] = 5
81+
82+
# Drop a dimension entirely (it then defaults to the full range — see below)
83+
for key in ("longitude>=", "longitude<=", "longitude_step"):
84+
constraints.pop(key, None)
85+
```
86+
87+
### Partial constraints
88+
89+
You do **not** have to specify every dimension. **Any dimension you omit is
90+
returned in full.** For example, constraining only latitude and longitude
91+
returns *all* time steps within that spatial box:
92+
93+
```python
94+
constraints = {
95+
"latitude>=": 0,
96+
"latitude<=": 10,
97+
"longitude>=": 0,
98+
"longitude<=": 10,
99+
}
100+
```
101+
102+
Internally, your keys are merged on top of the layer's full-range defaults, so
103+
unspecified bounds and strides fall back to "everything, every cell".
104+
105+
## What happens if you don't specify constraints
106+
107+
Omitting `constraints` (or passing `None`) means **no subsetting** — the request
108+
covers the entire global layer, all dimensions, every cell. These layers can be
109+
**several gigabytes**.
110+
111+
- [`download_layers`][pyo_oracle.download_layers] guards against this: with no
112+
constraints it prints a warning and asks for confirmation before downloading.
113+
Pass `skip_confirmation=True` (or set it in the config) to proceed without the
114+
prompt — useful in scripts, but make sure you really want the whole layer.
115+
116+
```python
117+
# Prompts: "No constraints have been set. This will download the full
118+
# dataset, which may be a few GBs in size. ... y/N"
119+
pyo.download_layers("thetao_baseline_2000_2019_depthsurf")
120+
```
121+
122+
- [`load_layer`][pyo_oracle.load_layer] and
123+
[`get_layer_url`][pyo_oracle.get_layer_url] do **not** prompt. Calling
124+
`load_layer` with no constraints will attempt to pull the **entire layer into
125+
memory**, which can exhaust RAM. Always pass constraints when loading into
126+
memory.
127+
128+
!!! warning
129+
A request with no constraints is the full global layer. Prefer at least a
130+
spatial or temporal bound, and use [`info_layer`][pyo_oracle.info_layer] to
131+
confirm the ranges first.
132+
133+
## See also
134+
135+
- [`build_constraints`][pyo_oracle.build_constraints] — build the dictionary
136+
from friendly `(min, max)` bounds and strides, with optional validation.
137+
- [Downloading and subsetting](tutorials/downloading-and-subsetting.md) — the
138+
workflow in context.

docs/quickstart.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ constraints = pyo.build_constraints(
4848
When the `dataset_id` is supplied, requested bounds are checked against the
4949
dataset's real ranges and a warning is emitted if they fall outside.
5050

51+
`build_constraints` just returns a plain dictionary, which you can also write or
52+
edit by hand. For its full structure — and what happens when you pass no
53+
constraints — see [The constraints dictionary](constraints.md).
54+
5155
## 4. Load into memory or download to disk
5256

5357
```python

docs/tutorials/downloading-and-subsetting.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ constraints = {
5252
`*_step` arguments take every *n*-th grid cell along a dimension — a quick way
5353
to downsample a large region.
5454

55+
For the full anatomy of this dictionary — every key, value types, partial
56+
constraints, and how to edit it by hand — see
57+
[The constraints dictionary](../constraints.md).
58+
5559
## Restrict variables
5660

5761
Most layers ship several statistics (`_mean`, `_min`, `_max`, `_range`, ...).
@@ -80,4 +84,5 @@ pyo.list_local_data()
8084
!!! warning
8185
Calling `download_layers` without constraints downloads the **entire global
8286
layer**. `pyo_oracle` will ask for confirmation first; pass
83-
`skip_confirmation=True` to bypass the prompt in scripts.
87+
`skip_confirmation=True` to bypass the prompt in scripts. See
88+
[What happens if you don't specify constraints](../constraints.md#what-happens-if-you-dont-specify-constraints).

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ markdown_extensions:
4949
nav:
5050
- Home: index.md
5151
- Quickstart: quickstart.md
52+
- The constraints dictionary: constraints.md
5253
- Tutorials:
5354
- Listing and filtering: tutorials/listing-and-filtering.md
5455
- Downloading and subsetting: tutorials/downloading-and-subsetting.md

0 commit comments

Comments
 (0)