Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Python library for creating and validating [GeoZarr](https://github.com/zarr-developers/geozarr-spec)-compliant metadata. Provides Pydantic models, helper functions, and a CLI for the modular [Zarr conventions](https://github.com/zarr-conventions):

- **[spatial:](https://github.com/zarr-conventions/spatial)** -- Coordinate transforms between array indices and spatial coordinates
- **[proj:](https://github.com/zarr-experimental/geo-proj)** -- Coordinate Reference System (CRS) via EPSG codes, WKT2, or PROJJSON
- **[proj:](https://github.com/zarr-conventions/proj)** -- Coordinate Reference System (CRS) via EPSG codes, WKT2, or PROJJSON
- **[multiscales](https://github.com/zarr-conventions/multiscales)** -- Pyramid structures and resolution levels

## Installation
Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Python library for creating and validating [GeoZarr](https://github.com/zarr-developers/geozarr-spec)-compliant metadata. Provides Pydantic models, helper functions, and a CLI for the modular [Zarr conventions](https://github.com/zarr-conventions):

- **[spatial:](https://github.com/zarr-conventions/spatial)** -- Coordinate transforms between array indices and spatial coordinates
- **[proj:](https://github.com/zarr-experimental/geo-proj)** -- Coordinate Reference System (CRS) via EPSG codes, WKT2, or PROJJSON
- **[proj:](https://github.com/zarr-conventions/proj)** -- Coordinate Reference System (CRS) via EPSG codes, WKT2, or PROJJSON
- **[multiscales](https://github.com/zarr-conventions/multiscales)** -- Pyramid structures and resolution levels

## Quick example
Expand Down
4 changes: 2 additions & 2 deletions metazarr/scripts/compile-validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const OUT_DIR = resolve(__dirname, "../src/generated");

/** Known convention schemas to pre-compile, keyed by schema URL. */
const SCHEMAS = {
"https://raw.githubusercontent.com/zarr-conventions/geo-proj/refs/heads/main/schema.json":
{ id: "geo_proj", draft: "07" },
"https://raw.githubusercontent.com/zarr-conventions/proj/refs/heads/main/schema.json":
{ id: "proj", draft: "07" },
"https://raw.githubusercontent.com/zarr-conventions/spatial/refs/heads/main/schema.json":
{ id: "spatial", draft: "07" },
"https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/heads/main/schema.json":
Expand Down
10 changes: 5 additions & 5 deletions metazarr/src/conventions.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* @typedef {Object} ConventionInfo
* @property {string} uuid - Convention UUID
* @property {string} name - Convention name (e.g. "proj:", "spatial:")
* @property {string} name - Convention name (e.g. "proj", "spatial")
* @property {string} display - Human-readable display name
* @property {string} color - Colorblind-friendly color for UI display
* @property {string|undefined} schemaUrl - URL to JSON Schema
Expand All @@ -16,14 +16,14 @@
*/
const KNOWN_CONVENTIONS = {
"f17cb550-5864-4468-aeb7-f3180cfb622f": {
name: "proj:",
name: "proj",
display: "Geospatial Projection (proj:)",
color: "#0072B2", // blue
schemaUrl: "https://raw.githubusercontent.com/zarr-conventions/geo-proj/refs/heads/main/schema.json",
specUrl: "https://github.com/zarr-conventions/geo-proj/blob/main/README.md",
schemaUrl: "https://raw.githubusercontent.com/zarr-conventions/proj/refs/heads/main/schema.json",
specUrl: "https://github.com/zarr-conventions/proj/blob/main/README.md",
},
"689b58e2-cf7b-45e0-9fff-9cfc0883d6b4": {
name: "spatial:",
name: "spatial",
display: "Spatial Coordinates (spatial:)",
color: "#E69F00", // orange
schemaUrl: "https://raw.githubusercontent.com/zarr-conventions/spatial/refs/heads/main/schema.json",
Expand Down
2 changes: 1 addition & 1 deletion metazarr/test/consolidated-v3.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const SAMPLE_ROOT_ZARR_JSON = {
attributes: {
zarr_conventions: [
{ uuid: "d35379db-88df-4056-af3a-620245f8e347", name: "multiscales" },
{ uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4", name: "spatial:" },
{ uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4", name: "spatial" },
],
"spatial:dimensions": ["Y", "X"],
},
Expand Down
36 changes: 18 additions & 18 deletions metazarr/test/conventions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ describe("detectConventions", () => {
zarr_conventions: [
{
uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f",
name: "proj:",
name: "proj",
schema_url:
"https://raw.githubusercontent.com/zarr-experimental/geo-proj/refs/tags/v1/schema.json",
"https://raw.githubusercontent.com/zarr-conventions/proj/refs/tags/v0.1/schema.json",
},
{
uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4",
name: "spatial:",
name: "spatial",
schema_url:
"https://raw.githubusercontent.com/zarr-conventions/spatial/refs/tags/v1/schema.json",
"https://raw.githubusercontent.com/zarr-conventions/spatial/refs/tags/v0.1/schema.json",
},
],
"proj:code": "EPSG:26711",
Expand All @@ -24,10 +24,10 @@ describe("detectConventions", () => {

const result = detectConventions(attrs);
expect(result).toHaveLength(2);
expect(result[0].name).toBe("proj:");
expect(result[0].name).toBe("proj");
expect(result[0].display).toBe("Geospatial Projection (proj:)");
expect(result[0].schemaUrl).toContain("geo-proj");
expect(result[1].name).toBe("spatial:");
expect(result[0].schemaUrl).toContain("proj");
expect(result[1].name).toBe("spatial");
expect(result[1].display).toBe("Spatial Coordinates (spatial:)");
});

Expand All @@ -40,11 +40,11 @@ describe("detectConventions", () => {
},
{
uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f",
name: "proj:",
name: "proj",
},
{
uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4",
name: "spatial:",
name: "spatial",
},
],
};
Expand All @@ -53,8 +53,8 @@ describe("detectConventions", () => {
expect(result).toHaveLength(3);
expect(result.map((c) => c.name)).toEqual([
"multiscales",
"proj:",
"spatial:",
"proj",
"spatial",
]);
});

Expand Down Expand Up @@ -83,7 +83,7 @@ describe("detectConventions", () => {

const result = detectConventions(attrs);
expect(result).toHaveLength(2);
expect(result.map((c) => c.name).sort()).toEqual(["proj:", "spatial:"]);
expect(result.map((c) => c.name).sort()).toEqual(["proj", "spatial"]);
// Fallback detection still provides schema URLs from the registry
for (const conv of result) {
expect(conv.schemaUrl).toContain("refs/heads/main");
Expand Down Expand Up @@ -133,7 +133,7 @@ describe("detectConventions", () => {
zarr_conventions: [
{
uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f",
name: "proj:",
name: "proj",
schema_url: "https://example.com/old-tagged-schema.json",
spec_url: "https://example.com/spec",
},
Expand All @@ -142,9 +142,9 @@ describe("detectConventions", () => {

const result = detectConventions(attrs);
expect(result[0].schemaUrl).toContain("refs/heads/main");
expect(result[0].schemaUrl).toContain("geo-proj");
expect(result[0].schemaUrl).toContain("proj");
// Registry specUrl takes precedence over data spec_url for known conventions
expect(result[0].specUrl).toContain("geo-proj");
expect(result[0].specUrl).toContain("proj");
expect(result[0].specUrl).toContain("blob/main");
});

Expand All @@ -166,8 +166,8 @@ describe("detectConventions", () => {
it("does not duplicate conventions", () => {
const attrs = {
zarr_conventions: [
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj:" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj:" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj" },
],
};

Expand All @@ -182,7 +182,7 @@ describe("getKnownConvention", () => {
"f17cb550-5864-4468-aeb7-f3180cfb622f",
);
expect(conv).toBeDefined();
expect(conv.name).toBe("proj:");
expect(conv.name).toBe("proj");
});

it("returns undefined for unknown UUID", () => {
Expand Down
4 changes: 2 additions & 2 deletions metazarr/test/report.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ function withZarrConventions(attrs, conventions) {

const SPATIAL_CONV_ENTRY = {
uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4",
name: "spatial:",
name: "spatial",
};

const PROJ_CONV_ENTRY = {
uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f",
name: "proj:",
name: "proj",
};

describe("collectNodes", () => {
Expand Down
18 changes: 9 additions & 9 deletions metazarr/test/validator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { runValidation, buildNodeDocument } from "../src/validator.js";
* for test-only schemas while exercising the same error processing logic.
*/

// Minimal geo-proj schema (Draft-07) for testing
// Minimal proj schema (Draft-07) for testing
const PROJ_SCHEMA = {
$schema: "http://json-schema.org/draft-07/schema#",
type: "object",
Expand Down Expand Up @@ -59,7 +59,7 @@ const CONTAINS_REF_SCHEMA = {
type: "object",
properties: {
uuid: { const: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4" },
name: { const: "spatial:" },
name: { const: "spatial" },
description: { const: "Spatial coordinate information" },
},
anyOf: [{ required: ["uuid"] }, { required: ["name"] }],
Expand All @@ -82,7 +82,7 @@ describe("runValidation", () => {
node_type: "array",
attributes: {
zarr_conventions: [
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj:" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj" },
],
"proj:code": "EPSG:26711",
},
Expand Down Expand Up @@ -133,8 +133,8 @@ describe("runValidation", () => {
node_type: "group",
attributes: {
zarr_conventions: [
{ uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4", name: "spatial:", description: "Wrong desc" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj:" },
{ uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4", name: "spatial", description: "Wrong desc" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj" },
],
"spatial:dimensions": ["Y", "X"],
},
Expand Down Expand Up @@ -170,7 +170,7 @@ describe("runValidation", () => {
node_type: "group",
attributes: {
zarr_conventions: [
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj:" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj" },
],
// spatial:dimensions is missing (required)
},
Expand All @@ -196,8 +196,8 @@ describe("runValidation", () => {
attributes: {
zarr_conventions: [
{ uuid: "d35379db-88df-4056-af3a-620245f8e347", name: "multiscales" },
{ uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4", name: "spatial:", description: "Wrong" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj:" },
{ uuid: "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4", name: "spatial", description: "Wrong" },
{ uuid: "f17cb550-5864-4468-aeb7-f3180cfb622f", name: "proj" },
],
"spatial:dimensions": ["Y", "X"],
},
Expand All @@ -210,7 +210,7 @@ describe("runValidation", () => {
const failure = result.containsFailures[0];
// Best match should be index 1 (spatial entry)
expect(failure.bestMatchIndex).toBe(1);
expect(failure.bestMatchItem.name).toBe("spatial:");
expect(failure.bestMatchItem.name).toBe("spatial");

// Should NOT have errors about multiscales (index 0) or proj (index 2)
expect(result.errors).toEqual([]);
Expand Down
11 changes: 8 additions & 3 deletions src/geozarr_toolkit/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,10 +227,15 @@ def info_command(args: argparse.Namespace) -> int:
for level in layout:
asset = level.get("asset", "?")
derived = level.get("derived_from", "")
chunk_shape = level.get("read_chunk_shape")
parts = [asset]
if derived:
print(f" - {asset} (from {derived})")
else:
print(f" - {asset}")
parts.append(f"from {derived}")
if chunk_shape:
parts.append(f"read_chunk_shape={chunk_shape}")
print(
f" - {parts[0]}" + (f" ({', '.join(parts[1:])})" if len(parts) > 1 else "")
)
print()

if args.verbose:
Expand Down
2 changes: 0 additions & 2 deletions src/geozarr_toolkit/conventions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
PROJ_SCHEMA_URL,
PROJ_SPEC_URL,
PROJ_UUID,
GeoProj,
Proj,
ProjConventionMetadata,
)
Expand All @@ -70,7 +69,6 @@
"SPATIAL_SCHEMA_URL",
"SPATIAL_SPEC_URL",
"SPATIAL_UUID",
"GeoProj",
"Multiscales",
"MultiscalesAttrs",
"MultiscalesConventionMetadata",
Expand Down
5 changes: 3 additions & 2 deletions src/geozarr_toolkit/conventions/multiscales.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
"d35379db-88df-4056-af3a-620245f8e347"
)
MULTISCALES_SCHEMA_URL: Final[str] = (
"https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/tags/v1/schema.json"
"https://raw.githubusercontent.com/zarr-conventions/multiscales/refs/tags/v0.1/schema.json"
)
MULTISCALES_SPEC_URL: Final[str] = (
"https://github.com/zarr-conventions/multiscales/blob/v1/README.md"
"https://github.com/zarr-conventions/multiscales/blob/v0.1/README.md"
)


Expand Down Expand Up @@ -83,6 +83,7 @@ class ScaleLevel(BaseModel):
derived_from: str | MISSING = MISSING
transform: Transform | MISSING = MISSING
resampling_method: str | MISSING = MISSING
read_chunk_shape: tuple[int, ...] | MISSING = MISSING

model_config = {"extra": "allow"}

Expand Down
14 changes: 5 additions & 9 deletions src/geozarr_toolkit/conventions/proj.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
for geospatial data. It focuses solely on "what coordinate system" rather
than "how to transform" (which is handled by the spatial convention).

Specification: https://github.com/zarr-experimental/geo-proj
Specification: https://github.com/zarr-conventions/proj
"""

from __future__ import annotations
Expand All @@ -21,16 +21,16 @@
"f17cb550-5864-4468-aeb7-f3180cfb622f"
)
PROJ_SCHEMA_URL: Final[str] = (
"https://raw.githubusercontent.com/zarr-experimental/geo-proj/refs/tags/v1/schema.json"
"https://raw.githubusercontent.com/zarr-conventions/proj/refs/tags/v0.1/schema.json"
)
PROJ_SPEC_URL: Final[str] = "https://github.com/zarr-experimental/geo-proj/blob/v1/README.md"
PROJ_SPEC_URL: Final[str] = "https://github.com/zarr-conventions/proj/blob/v0.1/README.md"


class ProjConventionMetadata(ZarrConventionMetadata):
"""Metadata for the proj: convention in zarr_conventions array."""
"""Metadata for the proj convention in zarr_conventions array."""

uuid: Literal["f17cb550-5864-4468-aeb7-f3180cfb622f"] = PROJ_UUID
name: Literal["proj:"] = "proj:"
name: Literal["proj"] = "proj"
schema_url: str = PROJ_SCHEMA_URL
spec_url: str = PROJ_SPEC_URL
description: str = "Coordinate reference system information for geospatial data"
Expand Down Expand Up @@ -93,7 +93,3 @@ def validate_code_resolves(self) -> Proj:
except Exception:
raise ValueError(f"proj:code '{self.code}' does not resolve to a known CRS") from None
return self


# Backwards compatibility alias
GeoProj = Proj
8 changes: 4 additions & 4 deletions src/geozarr_toolkit/conventions/spatial.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,16 @@
"689b58e2-cf7b-45e0-9fff-9cfc0883d6b4"
)
SPATIAL_SCHEMA_URL: Final[str] = (
"https://raw.githubusercontent.com/zarr-conventions/spatial/refs/tags/v1/schema.json"
"https://raw.githubusercontent.com/zarr-conventions/spatial/refs/tags/v0.1/schema.json"
)
SPATIAL_SPEC_URL: Final[str] = "https://github.com/zarr-conventions/spatial/blob/v1/README.md"
SPATIAL_SPEC_URL: Final[str] = "https://github.com/zarr-conventions/spatial/blob/v0.1/README.md"


class SpatialConventionMetadata(ZarrConventionMetadata):
"""Metadata for the spatial: convention in zarr_conventions array."""
"""Metadata for the spatial convention in zarr_conventions array."""

uuid: Literal["689b58e2-cf7b-45e0-9fff-9cfc0883d6b4"] = SPATIAL_UUID
name: Literal["spatial:"] = "spatial:"
name: Literal["spatial"] = "spatial"
schema_url: str = SPATIAL_SCHEMA_URL
spec_url: str = SPATIAL_SPEC_URL
description: str = "Spatial coordinate information"
Expand Down
4 changes: 4 additions & 0 deletions src/geozarr_toolkit/helpers/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,16 @@ def create_multiscales_layout(
translation=tuple(transform_data.get("translation", [])) or MISSING,
)

read_chunk_shape_data = level.get("read_chunk_shape")
read_chunk_shape = tuple(read_chunk_shape_data) if read_chunk_shape_data else MISSING

scale_levels.append(
ScaleLevel(
asset=level["asset"],
derived_from=level.get("derived_from", MISSING),
transform=transform,
resampling_method=level.get("resampling_method", MISSING),
read_chunk_shape=read_chunk_shape,
)
)

Expand Down
Loading
Loading