Skip to content

Commit 4eb217d

Browse files
authored
Merge pull request #69 from d-v-b/feat/multiscales-convention
add multiscales extension
2 parents 5ab750d + 7b10473 commit 4eb217d

10 files changed

Lines changed: 374 additions & 2 deletions

File tree

.pre-commit-config.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,5 @@ repos:
2525
exclude: tests/.*
2626
additional_dependencies:
2727
- types-attrs
28-
- pydantic>=2.11
28+
- pydantic>=2.12
29+
- typing-extensions>=4.15

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ dependencies = [
3535
"numpy>=2.3.1",
3636
"rioxarray>=0.13.0",
3737
"cf-xarray>=0.8.0",
38-
"typing-extensions>=4.0.0",
38+
"typing-extensions>=4.15.0",
3939
"aiohttp>=3.8.1",
4040
"s3fs>=2024.6.0",
4141
"boto3>=1.34.0",
@@ -56,6 +56,7 @@ dev = [
5656
"safety>=2.0.0",
5757
]
5858
test = [
59+
"jsondiff>=2.2.1",
5960
"pytest>=7.0.0",
6061
"pytest-cov>=4.0.0",
6162
"pytest-mock>=3.10.0",
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
from __future__ import annotations
2+
3+
from typing import Literal, Self
4+
5+
from pydantic import BaseModel, Field, model_validator
6+
from pydantic.experimental.missing_sentinel import MISSING
7+
from typing_extensions import TypedDict
8+
9+
ConventionID = Literal["d35379db-88df-4056-af3a-620245f8e347"]
10+
11+
12+
class MultiscaleConvention(BaseModel):
13+
version: Literal["0.1.0"]
14+
schema_url: Literal[
15+
"https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/tags/v0.1.0/schema.json"
16+
] = Field(alias="schema")
17+
name: Literal["multiscales"] = "multiscales"
18+
description: str = "Multiscale layout of zarr datasets"
19+
spec: Literal[
20+
"https://github.com/zarr-conventions/geo-proj/blob/v0.1.0/README.md"
21+
] = "https://github.com/zarr-conventions/geo-proj/blob/v0.1.0/README.md"
22+
23+
24+
MultiscaleConventions = TypedDict( # type: ignore[misc]
25+
"MultiscaleConventions",
26+
{"d35379db-88df-4056-af3a-620245f8e347": MultiscaleConvention},
27+
closed=False,
28+
)
29+
30+
31+
class ConventionAttributes(BaseModel):
32+
zarr_conventions_version: Literal["0.1.0"]
33+
zarr_conventions: MultiscaleConventions
34+
35+
36+
class ScaleLevel(BaseModel):
37+
group: str
38+
from_group: str | MISSING = MISSING
39+
translation: tuple[float, ...] | MISSING = MISSING
40+
factors: tuple[float, ...] | MISSING = MISSING
41+
scale: tuple[float, ...] | MISSING = MISSING
42+
resampling_method: str | MISSING = MISSING
43+
44+
model_config = {"extra": "allow"}
45+
46+
@model_validator(mode="after")
47+
def check_model(self: Self) -> Self:
48+
if self.from_group is not MISSING and self.scale is MISSING:
49+
raise ValueError(
50+
f"from_group was set to {self.from_group}, but scale was unset. This is an error."
51+
)
52+
return self
53+
54+
55+
class Multiscales(BaseModel):
56+
layout: tuple[ScaleLevel, ...]
57+
resampling_method: str | MISSING = MISSING
58+
model_config = {"extra": "allow"}
59+
60+
61+
class MultiscalesAttributes(ConventionAttributes):
62+
multiscales: Multiscales

tests/test_data_api/conftest.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,3 +170,14 @@ def json_code_blocks_from_readme(
170170
readme_path = Path(request.param)
171171
content = readme_path.read_text(encoding="utf-8")
172172
return extract_json_code_blocks(content)
173+
174+
175+
def _load_multiscales_examples() -> dict[str, dict[str, object]]:
176+
"""Load a multiscale example."""
177+
examples_dir = Path(__file__).parent / "multiscales_examples"
178+
return {
179+
path.name: json.loads(path.read_text()) for path in examples_dir.glob("*.json")
180+
}
181+
182+
183+
MULTISCALES_EXAMPLES = _load_multiscales_examples()
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"zarr_format": 3,
3+
"node_type": "group",
4+
"attributes": {
5+
"zarr_conventions_version": "0.1.0",
6+
"zarr_conventions": {
7+
"d35379db-88df-4056-af3a-620245f8e347": {
8+
"version": "0.1.0",
9+
"schema": "https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/tags/v0.1.0/schema.json",
10+
"name": "multiscales",
11+
"description": "Multiscale layout of zarr datasets",
12+
"spec": "https://github.com/zarr-conventions/multiscales/blob/v0.1.0/README.md"
13+
}
14+
},
15+
"multiscales": {
16+
"layout": [
17+
{
18+
"group": "full",
19+
"scale": [1.0, 1.0]
20+
},
21+
{
22+
"group": "half",
23+
"from_group": "full",
24+
"scale": [2.0, 2.0]
25+
},
26+
{
27+
"group": "quarter",
28+
"from_group": "half",
29+
"scale": [4.0, 4.0]
30+
},
31+
{
32+
"group": "eighth",
33+
"from_group": "quarter",
34+
"scale": [8.0, 8.0]
35+
}
36+
]
37+
}
38+
}
39+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
{
2+
"zarr_format": 3,
3+
"node_type": "group",
4+
"attributes": {
5+
"zarr_conventions_version": "0.1.0",
6+
"zarr_conventions": {
7+
"d35379db-88df-4056-af3a-620245f8e347": {
8+
"version": "0.1.0",
9+
"schema": "https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/tags/v0.1.0/schema.json",
10+
"name": "multiscales",
11+
"description": "Multiscale layout of zarr datasets",
12+
"spec": "https://github.com/zarr-conventions/multiscales/blob/v0.1.0/README.md"
13+
},
14+
"f17cb550-5864-4468-aeb7-f3180cfb622f": {
15+
"version": "0.1.0",
16+
"schema": "https://raw.githubusercontent.com/zarr-conventions/geo-proj/refs/tags/v0.1.0/schema.json",
17+
"name": "geo-proj",
18+
"description": "Coordinate reference system information for geospatial data",
19+
"spec": "https://github.com/zarr-conventions/geo-proj/blob/v0.1.0/README.md"
20+
}
21+
},
22+
"multiscales": {
23+
"layout": [
24+
{
25+
"group": "dem_30m",
26+
"factors": [1.0, 1.0],
27+
"scale": [1.0, 1.0]
28+
},
29+
{
30+
"group": "dem_90m",
31+
"from_group": "dem_30m",
32+
"factors": [3.0, 3.0],
33+
"scale": [3.0, 3.0],
34+
"resampling_method": "average"
35+
},
36+
{
37+
"group": "dem_270m",
38+
"from_group": "dem_90m",
39+
"factors": [9.0, 9.0],
40+
"scale": [3.0, 3.0],
41+
"resampling_method": "average"
42+
},
43+
{
44+
"group": "dem_10m_superres",
45+
"from_group": "dem_30m",
46+
"factors": [0.333, 0.333],
47+
"scale": [0.333, 0.333],
48+
"resampling_method": "cubic"
49+
}
50+
],
51+
"resampling_method": "cubic"
52+
},
53+
"proj:code": "EPSG:4326",
54+
"proj:spatial_dimensions": ["latitude", "longitude"],
55+
"proj:transform": [0.000277778, 0.0, -180.0, 0.0, -0.000277778, 90.0],
56+
"proj:bbox": [-180.0, -90.0, 180.0, 90.0]
57+
}
58+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
{
2+
"zarr_format": 3,
3+
"node_type": "group",
4+
"attributes": {
5+
"zarr_conventions_version": "0.1.0",
6+
"zarr_conventions": {
7+
"d35379db-88df-4056-af3a-620245f8e347": {
8+
"version": "0.1.0",
9+
"schema": "https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/tags/v0.1.0/schema.json",
10+
"name": "multiscales",
11+
"description": "Multiscale layout of zarr datasets",
12+
"spec": "https://github.com/zarr-conventions/multiscales/blob/v0.1.0/README.md"
13+
},
14+
"f17cb550-5864-4468-aeb7-f3180cfb622f": {
15+
"version": "0.1.0",
16+
"schema": "https://raw.githubusercontent.com/zarr-conventions/geo-proj/refs/tags/v0.1.0/schema.json",
17+
"name": "geo-proj",
18+
"description": "Coordinate reference system information for geospatial data",
19+
"spec": "https://github.com/zarr-conventions/geo-proj/blob/v0.1.0/README.md"
20+
}
21+
},
22+
"multiscales": {
23+
"version": "0.1.0",
24+
"layout": [
25+
{
26+
"group": "0",
27+
"scale": [1.0, 1.0]
28+
},
29+
{
30+
"group": "1",
31+
"from_group": "0",
32+
"scale": [2.0, 2.0],
33+
"translation": [0.0, 0.0],
34+
"resampling_method": "average"
35+
},
36+
{
37+
"group": "2",
38+
"from_group": "1",
39+
"scale": [4.0, 4.0],
40+
"translation": [0.0, 0.0],
41+
"resampling_method": "average"
42+
}
43+
],
44+
"resampling_method": "average"
45+
},
46+
"proj:code": "EPSG:32632",
47+
"proj:spatial_dimensions": ["Y", "X"],
48+
"proj:transform": [10.0, 0.0, 500000.0, 0.0, -10.0, 5000000.0],
49+
"proj:bbox": [500000.0, 4900000.0, 600000.0, 5000000.0]
50+
}
51+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"zarr_format": 3,
3+
"node_type": "group",
4+
"attributes": {
5+
"zarr_conventions_version": "0.1.0",
6+
"zarr_conventions": {
7+
"d35379db-88df-4056-af3a-620245f8e347": {
8+
"version": "0.1.0",
9+
"schema": "https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/tags/v0.1.0/schema.json",
10+
"name": "multiscales",
11+
"description": "Multiscale layout of zarr datasets",
12+
"spec": "https://github.com/zarr-conventions/multiscales/blob/v0.1.0/README.md"
13+
}
14+
},
15+
"multiscales": {
16+
"layout": [
17+
{
18+
"group": "0",
19+
"scale": [1.0, 1.0]
20+
},
21+
{
22+
"group": "1",
23+
"from_group": "0",
24+
"scale": [2.0, 2.0],
25+
"translation": [0.0, 0.0],
26+
"resampling_method": "average"
27+
},
28+
{
29+
"group": "2",
30+
"from_group": "1",
31+
"scale": [4.0, 4.0],
32+
"translation": [0.0, 0.0],
33+
"resampling_method": "average"
34+
}
35+
],
36+
"resampling_method": "average"
37+
}
38+
}
39+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
"zarr_format": 3,
3+
"node_type": "group",
4+
"attributes": {
5+
"zarr_conventions_version": "0.1.0",
6+
"zarr_conventions": {
7+
"d35379db-88df-4056-af3a-620245f8e347": {
8+
"version": "0.1.0",
9+
"schema": "https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/tags/v0.1.0/schema.json",
10+
"name": "multiscales",
11+
"description": "Multiscale layout of zarr datasets",
12+
"spec": "https://github.com/zarr-conventions/multiscales/blob/v0.1.0/README.md"
13+
},
14+
"f17cb550-5864-4468-aeb7-f3180cfb622f": {
15+
"version": "0.1.0",
16+
"schema": "https://raw.githubusercontent.com/zarr-experimental/geo-proj/refs/tags/v0.1.0/schema.json",
17+
"name": "geo-proj",
18+
"description": "Coordinate reference system information for geospatial data",
19+
"spec": "https://github.com/zarr-experimental/geo-proj/blob/v0.1.0/README.md"
20+
}
21+
},
22+
"multiscales": {
23+
"layout": [
24+
{
25+
"group": "r10m",
26+
"scale": [1.0, 1.0],
27+
"factors": [1.0, 1.0],
28+
"proj:transform": [10.0, 0.0, 500000.0, 0.0, -10.0, 5000000.0]
29+
},
30+
{
31+
"group": "r20m",
32+
"from_group": "r10m",
33+
"scale": [2.0, 2.0],
34+
"translation": [0.0, 0.0],
35+
"factors": [2.0, 2.0],
36+
"proj:transform": [20.0, 0.0, 500000.0, 0.0, -20.0, 5000000.0]
37+
},
38+
{
39+
"group": "r60m",
40+
"from_group": "r10m",
41+
"scale": [6.0, 6.0],
42+
"translation": [0.0, 0.0],
43+
"factors": [6.0, 6.0],
44+
"proj:transform": [60.0, 0.0, 500000.0, 0.0, -60.0, 5000000.0]
45+
},
46+
{
47+
"group": "r120m",
48+
"from_group": "r60m",
49+
"scale": [2.0, 2.0],
50+
"translation": [0.0, 0.0],
51+
"factors": [12.0, 12.0],
52+
"proj:transform": [120.0, 0.0, 500000.0, 0.0, -120.0, 5000000.0]
53+
},
54+
{
55+
"group": "r360m",
56+
"from_group": "r120m",
57+
"scale": [3.0, 3.0],
58+
"translation": [0.0, 0.0],
59+
"factors": [36.0, 36.0],
60+
"proj:transform": [360.0, 0.0, 500000.0, 0.0, -360.0, 5000000.0]
61+
},
62+
{
63+
"group": "r720m",
64+
"from_group": "r360m",
65+
"scale": [2.0, 2.0],
66+
"translation": [0.0, 0.0],
67+
"factors": [72.0, 72.0],
68+
"proj:transform": [720.0, 0.0, 500000.0, 0.0, -720.0, 5000000.0]
69+
}
70+
]
71+
},
72+
"proj:code": "EPSG:32633",
73+
"proj:spatial_dimensions": ["Y", "X"],
74+
"proj:bbox": [500000.0, 4900000.0, 600000.0, 5000000.0]
75+
}
76+
}

0 commit comments

Comments
 (0)