From 60fddbc1cee3a1f6abef6408bbf5fc2c58bc9211 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Mon, 8 Sep 2025 23:02:40 +0200 Subject: [PATCH 01/38] add: create specifications for Projection Attribute Extension and its schema --- README.md | 1 + attributes/README.md | 30 +++++++ attributes/projection/README.md | 142 ++++++++++++++++++++++++++++++ attributes/projection/schema.json | 108 +++++++++++++++++++++++ 4 files changed, 281 insertions(+) create mode 100644 attributes/README.md create mode 100644 attributes/projection/README.md create mode 100644 attributes/projection/schema.json diff --git a/README.md b/README.md index 6d23ffd..8f431bf 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ It is the normative source for registering names of Zarr v3 extensions. To register an extension, open a new PR with a new extension directory under the relevant extension point: + * [Attributes](./attributes/README.md) * [Codecs](./codecs/README.md) * [Data Types](./data-types/README.md) * [Chunk Key Encoding](./chunk-key-encodings/README.md) diff --git a/attributes/README.md b/attributes/README.md new file mode 100644 index 0000000..81623c2 --- /dev/null +++ b/attributes/README.md @@ -0,0 +1,30 @@ +# Attributes Extensions + +This directory contains specifications for Zarr v3 attribute extensions. + +## What are Attribute Extensions? + +Attribute extensions define standardized schemas and semantics for metadata stored in the `attributes` field of Zarr arrays and groups. These extensions enable interoperability by establishing common conventions for domain-specific metadata. + +## Registered Extensions + +| Extension | Version | Description | +|-----------|---------|-------------| +| [projection](./projection/) | 1.0.0 | Coordinate reference system metadata for geospatial data | + +## Creating an Attribute Extension + +When creating an attribute extension, consider: + +1. **Namespace**: Use a unique prefix to avoid conflicts (e.g., `proj:` for projection) +2. **Schema**: Provide a JSON schema for validation +3. **Inheritance**: Define behavior when attributes are set at group vs array level +4. **Compatibility**: Consider interoperability with existing tools and standards + +## Extension Requirements + +Each attribute extension MUST: +- Define the attribute key(s) and structure +- Provide a JSON schema for validation +- Include examples of usage +- Document any inheritance or precedence rules diff --git a/attributes/projection/README.md b/attributes/projection/README.md new file mode 100644 index 0000000..b6baaaf --- /dev/null +++ b/attributes/projection/README.md @@ -0,0 +1,142 @@ +# Projection Attribute Extension for Zarr + +- **Extension Name**: Projection Attribute Extension +- **Version**: 1.0.0 +- **Extension Type**: Attribute +- **Status**: Proposed +- **Owners**: @emmanuelmathot + +## Description + +This extension defines a standardized way to encode coordinate reference system (CRS) information for geospatial Zarr arrays and groups using the `proj:crs` attribute. + +## Motivation + +- Provides simple, standardized CRS encoding without complex nested structures +- Addresses issues identified in GeoZarr discussions regarding CF convention complexity +- Compatible with existing geospatial tools (GDAL, rasterio, pyproj) +- Based on the proven STAC Projection Extension model + +## Specification + +The `proj:crs` attribute can be added to Zarr arrays or groups to define projection information. + +### Required Fields + +At least one of the following MUST be provided: + +- `proj:code`: Authority and code identifier (e.g., "EPSG:4326") +- `proj:wkt2`: WKT2 string representation of the CRS +- `proj:projjson`: PROJJSON object representation of the CRS + +### Optional Fields + +- `proj:bbox`: Bounding box in the CRS coordinates +- `proj:transform`: Affine transformation coefficients (6 or 9 elements) +- `proj:shape`: Shape of the spatial dimensions [y, x] +- `proj:spatial_dims`: Names of spatial dimensions in the array + +### Spatial Dimension Identification + +The extension identifies spatial dimensions through: + +1. **Explicit Declaration** (recommended): Use `proj:spatial_dims` to specify dimension names +2. **Convention-Based** (fallback): Automatically detect standard spatial dimension names + +#### Explicit Declaration + +```json +{ + "proj:crs": { + "proj:shape": [2048, 2048], + "proj:spatial_dims": ["latitude", "longitude"] + } +} +``` + +#### Convention-Based Detection + +If `proj:spatial_dims` is not provided, implementations should scan `dimension_names` for these patterns (in order): + +- ["y", "x"] or ["Y", "X"] +- ["lat", "lon"] or ["latitude", "longitude"] +- ["northing", "easting"] +- ["row", "col"] or ["line", "sample"] + +The first matching pair determines the spatial dimensions. + +### Validation Rules + +- When `proj:shape` is provided, values MUST match the sizes of identified spatial dimensions +- The order in `proj:shape` is always [y/lat/northing, x/lon/easting] +- If spatial dimensions cannot be identified and `proj:shape` is provided, implementations MUST raise an error +- When multiple CRS representations are provided, precedence is: `proj:projjson` > `proj:wkt2` > `proj:code` + +## Examples + +### Example 1: Simple EPSG Code + +```json +{ + "zarr_format": 3, + "shape": [2048, 2048], + "dimension_names": ["y", "x"], + "attributes": { + "proj:crs": { + "proj:code": "EPSG:3857" + } + } +} +``` + +### Example 2: With Multiple Dimensions and Transform + +```json +{ + "zarr_format": 3, + "shape": [365, 100, 2048, 2048, 4], + "dimension_names": ["time", "height", "latitude", "longitude", "band"], + "attributes": { + "proj:crs": { + "proj:code": "EPSG:4326", + "proj:spatial_dims": ["latitude", "longitude"], + "proj:shape": [2048, 2048], + "proj:transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], + "proj:bbox": [-180.0, -90.0, 180.0, 90.0] + } + } +} +``` + +### Example 3: WKT2 Representation + +```json +{ + "zarr_format": 3, + "shape": [1000, 1000], + "dimension_names": ["northing", "easting"], + "attributes": { + "proj:crs": { + "proj:wkt2": "PROJCRS[\"WGS 84 / UTM zone 33N\",BASEGEOGCRS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]]],CONVERSION[\"UTM zone 33N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1]]],CS[Cartesian,2],AXIS[\"easting\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],AXIS[\"northing\",north,ORDER[2],LENGTHUNIT[\"metre\",1]]]", + "proj:shape": [1000, 1000], + "proj:transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0] + } + } +} +``` + +## Inheritance + +When `proj:crs` is defined at the group level, it applies to all arrays within that group unless overridden at the array level. + +## Compatibility Notes + +- The `proj:code` field follows the "authority:code" format used by PROJ library +- The `proj:wkt2` field should conform to OGC WKT2 (ISO 19162) standard +- The `proj:transform` field follows the same ordering as GDAL's GeoTransform and STAC's projection extension + +## References + +- [STAC Projection Extension v2.0.0](https://github.com/stac-extensions/projection) +- [PROJJSON Specification](https://proj.org/specifications/projjson.html) +- [OGC WKT2 Standard](https://www.ogc.org/standards/wkt-crs) diff --git a/attributes/projection/schema.json b/attributes/projection/schema.json new file mode 100644 index 0000000..7725073 --- /dev/null +++ b/attributes/projection/schema.json @@ -0,0 +1,108 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://zarr-specs.readthedocs.io/en/latest/extensions/attributes/projection/v1.0.0/schema.json", + "title": "Zarr Projection Attribute Extension", + "description": "Projection attribute extension for Zarr arrays and groups", + "type": "object", + "definitions": { + "projectionMetadata": { + "type": "object", + "properties": { + "proj:code": { + "type": ["string", "null"], + "description": "Authority:code identifier (e.g., EPSG:4326)", + "pattern": "^[A-Z]+:[0-9]+$" + }, + "proj:wkt2": { + "type": ["string", "null"], + "description": "WKT2 (ISO 19162) CRS representation" + }, + "proj:projjson": { + "oneOf": [ + { + "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" + }, + { + "type": "null" + } + ], + "description": "PROJJSON CRS representation" + }, + "proj:bbox": { + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + }, + "description": "Bounding box in CRS coordinates" + }, + "proj:transform": { + "type": "array", + "oneOf": [ + { + "minItems": 6, + "maxItems": 6 + }, + { + "minItems": 9, + "maxItems": 9 + } + ], + "items": { + "type": "number" + }, + "description": "Affine transformation coefficients" + }, + "proj:shape": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer", + "minimum": 1 + }, + "description": "Shape of spatial dimensions [y, x]" + }, + "proj:spatial_dims": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + }, + "description": "Names of spatial dimensions [y_name, x_name]" + } + }, + "oneOf": [ + { + "required": ["proj:code"] + }, + { + "required": ["proj:wkt2"] + }, + { + "required": ["proj:projjson"] + } + ] + } + }, + "properties": { + "attributes": { + "type": "object", + "properties": { + "proj:crs": { + "$ref": "#/definitions/projectionMetadata" + } + } + } + } +} From f09f67aaa417ad6668c8ad3bbed8a72603017a63 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Tue, 9 Sep 2025 08:58:38 +0200 Subject: [PATCH 02/38] fix: update attribute naming from `proj` to `geo` in Projection Attribute Extension documentation and schema --- attributes/geo:proj/README.md | 142 ++++++++++++++++++ .../{projection => geo:proj}/schema.json | 22 +-- attributes/projection/README.md | 142 ------------------ 3 files changed, 153 insertions(+), 153 deletions(-) create mode 100644 attributes/geo:proj/README.md rename attributes/{projection => geo:proj}/schema.json (88%) delete mode 100644 attributes/projection/README.md diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md new file mode 100644 index 0000000..df92130 --- /dev/null +++ b/attributes/geo:proj/README.md @@ -0,0 +1,142 @@ +# Projection Attribute Extension for Zarr + +- **Extension Name**: Projection Attribute Extension +- **Version**: 1.0.0 +- **Extension Type**: Attribute +- **Status**: Proposed +- **Owners**: @emmanuelmathot + +## Description + +This extension defines a standardized way to encode coordinate reference system (CRS) information for geospatial Zarr arrays and groups using the `geo:proj` attribute. + +## Motivation + +- Provides simple, standardized CRS encoding without complex nested structures +- Addresses issues identified in GeoZarr discussions regarding CF convention complexity +- Compatible with existing geospatial tools (GDAL, rasterio, pyproj) +- Based on the proven STAC Projection Extension model + +## Specification + +The `geo:proj` attribute can be added to Zarr arrays or groups to define projection information. + +### Required Fields + +At least one of the following MUST be provided: + +- `code`: Authority and code identifier (e.g., "EPSG:4326") +- `wkt2`: WKT2 string representation of the CRS +- `projjson`: PROJJSON object representation of the CRS + +### Optional Fields + +- `bbox`: Bounding box in the CRS coordinates +- `transform`: Affine transformation coefficients (6 or 9 elements) +- `shape`: Shape of the spatial dimensions [y, x] +- `spatial_dims`: Names of spatial dimensions in the array + +### Spatial Dimension Identification + +The extension identifies spatial dimensions through: + +1. **Explicit Declaration** (recommended): Use `spatial_dims` to specify dimension names +2. **Convention-Based** (fallback): Automatically detect standard spatial dimension names + +#### Explicit Declaration + +```json +{ + "geo:proj": { + "shape": [2048, 2048], + "spatial_dims": ["latitude", "longitude"] + } +} +``` + +#### Convention-Based Detection + +If `spatial_dims` is not provided, implementations should scan `dimension_names` for these patterns (in order): + +- ["y", "x"] or ["Y", "X"] +- ["lat", "lon"] or ["latitude", "longitude"] +- ["northing", "easting"] +- ["row", "col"] or ["line", "sample"] + +The first matching pair determines the spatial dimensions. + +### Validation Rules + +- When `shape` is provided, values MUST match the sizes of identified spatial dimensions +- The order in `shape` is always [y/lat/northing, x/lon/easting] +- If spatial dimensions cannot be identified and `shape` is provided, implementations MUST raise an error +- When multiple CRS representations are provided, precedence is: `projjson` > `wkt2` > `code` + +## Examples + +### Example 1: Simple EPSG Code + +```json +{ + "zarr_format": 3, + "shape": [2048, 2048], + "dimension_names": ["y", "x"], + "attributes": { + "geo:proj": { + "code": "EPSG:3857" + } + } +} +``` + +### Example 2: With Multiple Dimensions and Transform + +```json +{ + "zarr_format": 3, + "shape": [365, 100, 2048, 2048, 4], + "dimension_names": ["time", "height", "latitude", "longitude", "band"], + "attributes": { + "geo:proj": { + "code": "EPSG:4326", + "spatial_dims": ["latitude", "longitude"], + "shape": [2048, 2048], + "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], + "bbox": [-180.0, -90.0, 180.0, 90.0] + } + } +} +``` + +### Example 3: WKT2 Representation + +```json +{ + "zarr_format": 3, + "shape": [1000, 1000], + "dimension_names": ["northing", "easting"], + "attributes": { + "geo:proj": { + "wkt2": "PROJCRS[\"WGS 84 / UTM zone 33N\",BASEGEOGCRS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]]],CONVERSION[\"UTM zone 33N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1]]],CS[Cartesian,2],AXIS[\"easting\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],AXIS[\"northing\",north,ORDER[2],LENGTHUNIT[\"metre\",1]]]", + "shape": [1000, 1000], + "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0] + } + } +} +``` + +## Inheritance + +When `geo:proj` is defined at the group level, it applies to all arrays within that group unless overridden at the array level. + +## Compatibility Notes + +- The `code` field follows the "authority:code" format used by PROJ library +- The `wkt2` field should conform to OGC WKT2 (ISO 19162) standard +- The `transform` field follows the same ordering as GDAL's GeoTransform and STAC's projection extension + +## References + +- [STAC Projection Extension v2.0.0](https://github.com/stac-extensions/projection) +- [PROJJSON Specification](https://proj.org/specifications/projjson.html) +- [OGC WKT2 Standard](https://www.ogc.org/standards/wkt-crs) diff --git a/attributes/projection/schema.json b/attributes/geo:proj/schema.json similarity index 88% rename from attributes/projection/schema.json rename to attributes/geo:proj/schema.json index 7725073..ac7e7ab 100644 --- a/attributes/projection/schema.json +++ b/attributes/geo:proj/schema.json @@ -8,16 +8,16 @@ "projectionMetadata": { "type": "object", "properties": { - "proj:code": { + "code": { "type": ["string", "null"], "description": "Authority:code identifier (e.g., EPSG:4326)", "pattern": "^[A-Z]+:[0-9]+$" }, - "proj:wkt2": { + "wkt2": { "type": ["string", "null"], "description": "WKT2 (ISO 19162) CRS representation" }, - "proj:projjson": { + "projjson": { "oneOf": [ { "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" @@ -28,7 +28,7 @@ ], "description": "PROJJSON CRS representation" }, - "proj:bbox": { + "bbox": { "type": "array", "oneOf": [ { @@ -45,7 +45,7 @@ }, "description": "Bounding box in CRS coordinates" }, - "proj:transform": { + "transform": { "type": "array", "oneOf": [ { @@ -62,7 +62,7 @@ }, "description": "Affine transformation coefficients" }, - "proj:shape": { + "shape": { "type": "array", "minItems": 2, "maxItems": 2, @@ -72,7 +72,7 @@ }, "description": "Shape of spatial dimensions [y, x]" }, - "proj:spatial_dims": { + "spatial_dims": { "type": "array", "minItems": 2, "maxItems": 2, @@ -84,13 +84,13 @@ }, "oneOf": [ { - "required": ["proj:code"] + "required": ["code"] }, { - "required": ["proj:wkt2"] + "required": ["wkt2"] }, { - "required": ["proj:projjson"] + "required": ["projjson"] } ] } @@ -99,7 +99,7 @@ "attributes": { "type": "object", "properties": { - "proj:crs": { + "geo:proj": { "$ref": "#/definitions/projectionMetadata" } } diff --git a/attributes/projection/README.md b/attributes/projection/README.md deleted file mode 100644 index b6baaaf..0000000 --- a/attributes/projection/README.md +++ /dev/null @@ -1,142 +0,0 @@ -# Projection Attribute Extension for Zarr - -- **Extension Name**: Projection Attribute Extension -- **Version**: 1.0.0 -- **Extension Type**: Attribute -- **Status**: Proposed -- **Owners**: @emmanuelmathot - -## Description - -This extension defines a standardized way to encode coordinate reference system (CRS) information for geospatial Zarr arrays and groups using the `proj:crs` attribute. - -## Motivation - -- Provides simple, standardized CRS encoding without complex nested structures -- Addresses issues identified in GeoZarr discussions regarding CF convention complexity -- Compatible with existing geospatial tools (GDAL, rasterio, pyproj) -- Based on the proven STAC Projection Extension model - -## Specification - -The `proj:crs` attribute can be added to Zarr arrays or groups to define projection information. - -### Required Fields - -At least one of the following MUST be provided: - -- `proj:code`: Authority and code identifier (e.g., "EPSG:4326") -- `proj:wkt2`: WKT2 string representation of the CRS -- `proj:projjson`: PROJJSON object representation of the CRS - -### Optional Fields - -- `proj:bbox`: Bounding box in the CRS coordinates -- `proj:transform`: Affine transformation coefficients (6 or 9 elements) -- `proj:shape`: Shape of the spatial dimensions [y, x] -- `proj:spatial_dims`: Names of spatial dimensions in the array - -### Spatial Dimension Identification - -The extension identifies spatial dimensions through: - -1. **Explicit Declaration** (recommended): Use `proj:spatial_dims` to specify dimension names -2. **Convention-Based** (fallback): Automatically detect standard spatial dimension names - -#### Explicit Declaration - -```json -{ - "proj:crs": { - "proj:shape": [2048, 2048], - "proj:spatial_dims": ["latitude", "longitude"] - } -} -``` - -#### Convention-Based Detection - -If `proj:spatial_dims` is not provided, implementations should scan `dimension_names` for these patterns (in order): - -- ["y", "x"] or ["Y", "X"] -- ["lat", "lon"] or ["latitude", "longitude"] -- ["northing", "easting"] -- ["row", "col"] or ["line", "sample"] - -The first matching pair determines the spatial dimensions. - -### Validation Rules - -- When `proj:shape` is provided, values MUST match the sizes of identified spatial dimensions -- The order in `proj:shape` is always [y/lat/northing, x/lon/easting] -- If spatial dimensions cannot be identified and `proj:shape` is provided, implementations MUST raise an error -- When multiple CRS representations are provided, precedence is: `proj:projjson` > `proj:wkt2` > `proj:code` - -## Examples - -### Example 1: Simple EPSG Code - -```json -{ - "zarr_format": 3, - "shape": [2048, 2048], - "dimension_names": ["y", "x"], - "attributes": { - "proj:crs": { - "proj:code": "EPSG:3857" - } - } -} -``` - -### Example 2: With Multiple Dimensions and Transform - -```json -{ - "zarr_format": 3, - "shape": [365, 100, 2048, 2048, 4], - "dimension_names": ["time", "height", "latitude", "longitude", "band"], - "attributes": { - "proj:crs": { - "proj:code": "EPSG:4326", - "proj:spatial_dims": ["latitude", "longitude"], - "proj:shape": [2048, 2048], - "proj:transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], - "proj:bbox": [-180.0, -90.0, 180.0, 90.0] - } - } -} -``` - -### Example 3: WKT2 Representation - -```json -{ - "zarr_format": 3, - "shape": [1000, 1000], - "dimension_names": ["northing", "easting"], - "attributes": { - "proj:crs": { - "proj:wkt2": "PROJCRS[\"WGS 84 / UTM zone 33N\",BASEGEOGCRS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]]],CONVERSION[\"UTM zone 33N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1]]],CS[Cartesian,2],AXIS[\"easting\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],AXIS[\"northing\",north,ORDER[2],LENGTHUNIT[\"metre\",1]]]", - "proj:shape": [1000, 1000], - "proj:transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0] - } - } -} -``` - -## Inheritance - -When `proj:crs` is defined at the group level, it applies to all arrays within that group unless overridden at the array level. - -## Compatibility Notes - -- The `proj:code` field follows the "authority:code" format used by PROJ library -- The `proj:wkt2` field should conform to OGC WKT2 (ISO 19162) standard -- The `proj:transform` field follows the same ordering as GDAL's GeoTransform and STAC's projection extension - -## References - -- [STAC Projection Extension v2.0.0](https://github.com/stac-extensions/projection) -- [PROJJSON Specification](https://proj.org/specifications/projjson.html) -- [OGC WKT2 Standard](https://www.ogc.org/standards/wkt-crs) From 83a5f931e52caf47e55b065d7212236482eac2b4 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Tue, 9 Sep 2025 09:01:50 +0200 Subject: [PATCH 03/38] fix: remove `shape` field from Projection Attribute Extension schema and update documentation for spatial dimension identification --- attributes/geo:proj/README.md | 21 ++++++++++++++------- attributes/geo:proj/schema.json | 10 ---------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index df92130..0e5f49d 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -33,9 +33,10 @@ At least one of the following MUST be provided: - `bbox`: Bounding box in the CRS coordinates - `transform`: Affine transformation coefficients (6 or 9 elements) -- `shape`: Shape of the spatial dimensions [y, x] - `spatial_dims`: Names of spatial dimensions in the array +Note: The shape of spatial dimensions is obtained directly from the Zarr array metadata once the spatial dimensions are identified. + ### Spatial Dimension Identification The extension identifies spatial dimensions through: @@ -48,7 +49,6 @@ The extension identifies spatial dimensions through: ```json { "geo:proj": { - "shape": [2048, 2048], "spatial_dims": ["latitude", "longitude"] } } @@ -67,11 +67,20 @@ The first matching pair determines the spatial dimensions. ### Validation Rules -- When `shape` is provided, values MUST match the sizes of identified spatial dimensions -- The order in `shape` is always [y/lat/northing, x/lon/easting] -- If spatial dimensions cannot be identified and `shape` is provided, implementations MUST raise an error +- Once spatial dimensions are identified (either explicitly through `spatial_dims` or through convention-based detection), their sizes are obtained from the Zarr array's shape metadata +- The spatial dimension order is always [y/lat/northing, x/lon/easting] +- If spatial dimensions cannot be identified through either method, implementations MUST raise an error - When multiple CRS representations are provided, precedence is: `projjson` > `wkt2` > `code` +### Shape Reconciliation + +The shape of spatial dimensions is determined by: +1. Identifying the spatial dimensions using either `spatial_dims` or convention-based detection +2. Looking up these dimension names in the Zarr array's `dimension_names` +3. Using the corresponding sizes from the array's `shape` attribute + +This approach avoids redundancy and ensures consistency by using the array's own metadata rather than duplicating shape information. + ## Examples ### Example 1: Simple EPSG Code @@ -100,7 +109,6 @@ The first matching pair determines the spatial dimensions. "geo:proj": { "code": "EPSG:4326", "spatial_dims": ["latitude", "longitude"], - "shape": [2048, 2048], "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], "bbox": [-180.0, -90.0, 180.0, 90.0] } @@ -118,7 +126,6 @@ The first matching pair determines the spatial dimensions. "attributes": { "geo:proj": { "wkt2": "PROJCRS[\"WGS 84 / UTM zone 33N\",BASEGEOGCRS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]]],CONVERSION[\"UTM zone 33N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1]]],CS[Cartesian,2],AXIS[\"easting\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],AXIS[\"northing\",north,ORDER[2],LENGTHUNIT[\"metre\",1]]]", - "shape": [1000, 1000], "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0] } } diff --git a/attributes/geo:proj/schema.json b/attributes/geo:proj/schema.json index ac7e7ab..a86f9f1 100644 --- a/attributes/geo:proj/schema.json +++ b/attributes/geo:proj/schema.json @@ -62,16 +62,6 @@ }, "description": "Affine transformation coefficients" }, - "shape": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": { - "type": "integer", - "minimum": 1 - }, - "description": "Shape of spatial dimensions [y, x]" - }, "spatial_dims": { "type": "array", "minItems": 2, From 77bae320fc0db67b8a2be2700de334bb113875d7 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Tue, 9 Sep 2025 09:09:16 +0200 Subject: [PATCH 04/38] fix: rename `spatial_dims` to `spatial_dimensions` in README and schema for consistency --- attributes/geo:proj/README.md | 14 +++++++------- attributes/geo:proj/schema.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 0e5f49d..d8e7316 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -33,7 +33,7 @@ At least one of the following MUST be provided: - `bbox`: Bounding box in the CRS coordinates - `transform`: Affine transformation coefficients (6 or 9 elements) -- `spatial_dims`: Names of spatial dimensions in the array +- `spatial_dimensions`: Names of spatial dimensions in the array Note: The shape of spatial dimensions is obtained directly from the Zarr array metadata once the spatial dimensions are identified. @@ -41,7 +41,7 @@ Note: The shape of spatial dimensions is obtained directly from the Zarr array m The extension identifies spatial dimensions through: -1. **Explicit Declaration** (recommended): Use `spatial_dims` to specify dimension names +1. **Explicit Declaration** (recommended): Use `spatial_dimensions` to specify dimension names 2. **Convention-Based** (fallback): Automatically detect standard spatial dimension names #### Explicit Declaration @@ -49,14 +49,14 @@ The extension identifies spatial dimensions through: ```json { "geo:proj": { - "spatial_dims": ["latitude", "longitude"] + "spatial_dimensions": ["latitude", "longitude"] } } ``` #### Convention-Based Detection -If `spatial_dims` is not provided, implementations should scan `dimension_names` for these patterns (in order): +If `spatial_dimensions` is not provided, implementations should scan `dimension_names` for these patterns (in order): - ["y", "x"] or ["Y", "X"] - ["lat", "lon"] or ["latitude", "longitude"] @@ -67,7 +67,7 @@ The first matching pair determines the spatial dimensions. ### Validation Rules -- Once spatial dimensions are identified (either explicitly through `spatial_dims` or through convention-based detection), their sizes are obtained from the Zarr array's shape metadata +- Once spatial dimensions are identified (either explicitly through `spatial_dimensions` or through convention-based detection), their sizes are obtained from the Zarr array's shape metadata - The spatial dimension order is always [y/lat/northing, x/lon/easting] - If spatial dimensions cannot be identified through either method, implementations MUST raise an error - When multiple CRS representations are provided, precedence is: `projjson` > `wkt2` > `code` @@ -75,7 +75,7 @@ The first matching pair determines the spatial dimensions. ### Shape Reconciliation The shape of spatial dimensions is determined by: -1. Identifying the spatial dimensions using either `spatial_dims` or convention-based detection +1. Identifying the spatial dimensions using either `spatial_dimensions` or convention-based detection 2. Looking up these dimension names in the Zarr array's `dimension_names` 3. Using the corresponding sizes from the array's `shape` attribute @@ -108,7 +108,7 @@ This approach avoids redundancy and ensures consistency by using the array's own "attributes": { "geo:proj": { "code": "EPSG:4326", - "spatial_dims": ["latitude", "longitude"], + "spatial_dimensions": ["latitude", "longitude"], "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], "bbox": [-180.0, -90.0, 180.0, 90.0] } diff --git a/attributes/geo:proj/schema.json b/attributes/geo:proj/schema.json index a86f9f1..4bb7f39 100644 --- a/attributes/geo:proj/schema.json +++ b/attributes/geo:proj/schema.json @@ -62,7 +62,7 @@ }, "description": "Affine transformation coefficients" }, - "spatial_dims": { + "spatial_dimensions": { "type": "array", "minItems": 2, "maxItems": 2, From caee06b51f601c1a5796c727a7dd6ef34e3788e7 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Tue, 9 Sep 2025 09:14:50 +0200 Subject: [PATCH 05/38] fix: enhance spatial dimension identification section in README with detailed examples and clarifications --- attributes/geo:proj/README.md | 41 +++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index d8e7316..82f2515 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -39,7 +39,14 @@ Note: The shape of spatial dimensions is obtained directly from the Zarr array m ### Spatial Dimension Identification -The extension identifies spatial dimensions through: +In this extension, "spatial dimensions" refer to the array dimensions (axes) along which the data is organized spatially. These dimensions correspond to the array's shape and dimension names, not to the spatial coordinates themselves, which may be stored separately (e.g., as coordinate variables in the case of irregular grids). + +For example: +- Regular grid: dimensions like "y" and "x" directly map to spatial axes +- Irregular grid: may use a single dimension (e.g., "node") with explicit x(node) and y(node) coordinates +- Structured but non-regular grid: may use dimensions like "latitude" and "longitude" with 2D coordinate variables + +The extension identifies these array dimensions through: 1. **Explicit Declaration** (recommended): Use `spatial_dimensions` to specify dimension names 2. **Convention-Based** (fallback): Automatically detect standard spatial dimension names @@ -116,7 +123,37 @@ This approach avoids redundancy and ensures consistency by using the array's own } ``` -### Example 3: WKT2 Representation +### Example 3: Irregular Grid + +In this case, the spatial coordinates are stored in separate coordinate variables (`x` and `y`) that share the same dimension as the data array: + +```json +{ + "zarr_format": 3, + "shape": [1000], + "dimension_names": ["node"], + "attributes": { + "geo:proj": { + "code": "EPSG:4326", + "spatial_dimensions": ["node"] + } + }, + "coordinates": { + "x": { + "shape": [1000], + "dimension_names": ["node"] + }, + "y": { + "shape": [1000], + "dimension_names": ["node"] + } + } +} +``` + +Note: The coordinate variables contain the actual spatial positions in the CRS for each node. + +### Example 4: WKT2 Representation ```json { From 9daf0d36367ba9ca1b3aa06b5f0def8b258713c3 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Tue, 9 Sep 2025 09:51:14 +0200 Subject: [PATCH 06/38] fix: clarify inheritance model for `geo:proj` attribute in README --- attributes/geo:proj/README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 82f2515..35fdce4 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -171,7 +171,17 @@ Note: The coordinate variables contain the actual spatial positions in the CRS f ## Inheritance -When `geo:proj` is defined at the group level, it applies to all arrays within that group unless overridden at the array level. +The `geo:proj` attribute follows a simple group-to-array inheritance model: + +1. Inheritance Scope: + - Only applies from a group directly to its immediate array members + - Does not cascade through nested groups + - Groups cannot inherit from parent groups + +2. Override Rules: + - An array can completely override the group's `geo:proj` attribute with its own definition + - Partial inheritance (overriding only some fields while inheriting others) is not allowed + - If an array defines `geo:proj`, it must provide a complete definition ## Compatibility Notes From 7e77fa12e50f5f4d19dba98a4d819ded319015eb Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Tue, 9 Sep 2025 09:58:46 +0200 Subject: [PATCH 07/38] fix: clarify spatial dimension identification section in README with additional examples and explanations --- attributes/geo:proj/README.md | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 35fdce4..8107532 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -39,13 +39,29 @@ Note: The shape of spatial dimensions is obtained directly from the Zarr array m ### Spatial Dimension Identification -In this extension, "spatial dimensions" refer to the array dimensions (axes) along which the data is organized spatially. These dimensions correspond to the array's shape and dimension names, not to the spatial coordinates themselves, which may be stored separately (e.g., as coordinate variables in the case of irregular grids). +In this extension, "spatial dimensions" refer to the array dimensions along which the data is organized spatially. These dimensions are identified by arbitrary names in the array's metadata and correspond to the array's shape. They should not be confused with spatial coordinates or axes (X, Y), which represent the actual spatial positions and may be stored separately as coordinate variables. For example: + - Regular grid: dimensions like "y" and "x" directly map to spatial axes -- Irregular grid: may use a single dimension (e.g., "node") with explicit x(node) and y(node) coordinates +- Irregular grid: may use a single dimension (e.g., "node") with explicit x(node) and y(node) coordinates storing spatial positions - Structured but non-regular grid: may use dimensions like "latitude" and "longitude" with 2D coordinate variables +A relevant example from CF conventions 1.12 (example 5.2) shows this distinction: + +```cdm + float xc(xc) ; + xc:axis = "X" ; + xc:long_name = "x-coordinate in Cartesian system" ; + xc:units = "m" ; + float yc(yc) ; + yc:axis = "Y" ; + yc:long_name = "y-coordinate in Cartesian system" ; + yc:units = "m" ; +``` + +Here, "xc" and "yc" are arbitrary dimension names, while "X" and "Y" identify the spatial axes they represent. + The extension identifies these array dimensions through: 1. **Explicit Declaration** (recommended): Use `spatial_dimensions` to specify dimension names From 01537ec67065c27cdad8fbf27a8b4e70477fabfe Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Thu, 11 Sep 2025 08:10:09 +0200 Subject: [PATCH 08/38] fix: add version field to geo:proj schema and update README for compatibility notes --- attributes/geo:proj/README.md | 2 ++ attributes/geo:proj/schema.json | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 8107532..bdc5ce0 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -28,6 +28,7 @@ At least one of the following MUST be provided: - `code`: Authority and code identifier (e.g., "EPSG:4326") - `wkt2`: WKT2 string representation of the CRS - `projjson`: PROJJSON object representation of the CRS +- `version`: Version of the `geo:proj` extension being used ### Optional Fields @@ -201,6 +202,7 @@ The `geo:proj` attribute follows a simple group-to-array inheritance model: ## Compatibility Notes +- The `version` field allows tracking of changes and ensures compatibility with future updates - The `code` field follows the "authority:code" format used by PROJ library - The `wkt2` field should conform to OGC WKT2 (ISO 19162) standard - The `transform` field follows the same ordering as GDAL's GeoTransform and STAC's projection extension diff --git a/attributes/geo:proj/schema.json b/attributes/geo:proj/schema.json index 4bb7f39..36d1582 100644 --- a/attributes/geo:proj/schema.json +++ b/attributes/geo:proj/schema.json @@ -8,6 +8,11 @@ "projectionMetadata": { "type": "object", "properties": { + "version": { + "type": "string", + "enum": ["1.0"], + "description": "Static version of the geo:proj schema" + }, "code": { "type": ["string", "null"], "description": "Authority:code identifier (e.g., EPSG:4326)", From 17a0700771866adf8714ef9a3e3dd64e6e5f92cf Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:01:55 +0200 Subject: [PATCH 09/38] fix: update geo:proj schema to include version field and restructure projection metadata --- attributes/geo:proj/schema.json | 83 ++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 7 deletions(-) diff --git a/attributes/geo:proj/schema.json b/attributes/geo:proj/schema.json index 36d1582..15f31ab 100644 --- a/attributes/geo:proj/schema.json +++ b/attributes/geo:proj/schema.json @@ -5,14 +5,9 @@ "description": "Projection attribute extension for Zarr arrays and groups", "type": "object", "definitions": { - "projectionMetadata": { + "projectionMetadataV1_0": { "type": "object", "properties": { - "version": { - "type": "string", - "enum": ["1.0"], - "description": "Static version of the geo:proj schema" - }, "code": { "type": ["string", "null"], "description": "Authority:code identifier (e.g., EPSG:4326)", @@ -95,7 +90,81 @@ "type": "object", "properties": { "geo:proj": { - "$ref": "#/definitions/projectionMetadata" + "anyOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "string", + "enum": ["1.0"] + }, + "code": { + "type": ["string", "null"], + "description": "Authority:code identifier (e.g., EPSG:4326)", + "pattern": "^[A-Z]+:[0-9]+$" + }, + "wkt2": { + "type": ["string", "null"], + "description": "WKT2 (ISO 19162) CRS representation" + }, + "projjson": { + "oneOf": [ + { + "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" + }, + { + "type": "null" + } + ], + "description": "PROJJSON CRS representation" + }, + "bbox": { + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + }, + "description": "Bounding box in CRS coordinates" + }, + "transform": { + "type": "array", + "oneOf": [ + { + "minItems": 6, + "maxItems": 6 + }, + { + "minItems": 9, + "maxItems": 9 + } + ], + "items": { + "type": "number" + }, + "description": "Affine transformation coefficients" + }, + "spatial_dimensions": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + }, + "description": "Names of spatial dimensions [y_name, x_name]" + } + }, + "required": ["version"] + } + ] } } } From 289f774beb7196fe82c867e2a5464f15eff5decc Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:13:26 +0200 Subject: [PATCH 10/38] Update attributes/geo:proj/README.md Co-authored-by: Ryan Abernathey --- attributes/geo:proj/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index bdc5ce0..d7fbd34 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -40,7 +40,7 @@ Note: The shape of spatial dimensions is obtained directly from the Zarr array m ### Spatial Dimension Identification -In this extension, "spatial dimensions" refer to the array dimensions along which the data is organized spatially. These dimensions are identified by arbitrary names in the array's metadata and correspond to the array's shape. They should not be confused with spatial coordinates or axes (X, Y), which represent the actual spatial positions and may be stored separately as coordinate variables. +In this extension, "spatial dimensions" refers to the dimensions names of 2D / 3D arrays within this group to which the projection definition applies. For example: From cd14f535c30173980eb8a77260e508eab28f1e4a Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:14:26 +0200 Subject: [PATCH 11/38] Update attributes/README.md Co-authored-by: Davis Bennett --- attributes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/README.md b/attributes/README.md index 81623c2..573b150 100644 --- a/attributes/README.md +++ b/attributes/README.md @@ -4,7 +4,7 @@ This directory contains specifications for Zarr v3 attribute extensions. ## What are Attribute Extensions? -Attribute extensions define standardized schemas and semantics for metadata stored in the `attributes` field of Zarr arrays and groups. These extensions enable interoperability by establishing common conventions for domain-specific metadata. +Attribute extensions define standardized schemas and semantics for metadata stored in the attributes of Zarr arrays and groups. These extensions enable interoperability by establishing common conventions for domain-specific metadata. ## Registered Extensions From 09322436187b7002d8e8605aea514cc056e5817a Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:15:04 +0200 Subject: [PATCH 12/38] Update attributes/README.md Co-authored-by: Davis Bennett --- attributes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/README.md b/attributes/README.md index 573b150..e8bc2d0 100644 --- a/attributes/README.md +++ b/attributes/README.md @@ -20,7 +20,7 @@ When creating an attribute extension, consider: 2. **Schema**: Provide a JSON schema for validation 3. **Inheritance**: Define behavior when attributes are set at group vs array level 4. **Compatibility**: Consider interoperability with existing tools and standards - +5. **Example data**: Where possible, consider including a complete Zarr hierarchy that implements the extension. ## Extension Requirements Each attribute extension MUST: From cd58d28050b88c8620f65cbc82509782a7efaef5 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:16:00 +0200 Subject: [PATCH 13/38] Update attributes/geo:proj/README.md Co-authored-by: Davis Bennett --- attributes/geo:proj/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index d7fbd34..6738e30 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -13,7 +13,6 @@ This extension defines a standardized way to encode coordinate reference system ## Motivation - Provides simple, standardized CRS encoding without complex nested structures -- Addresses issues identified in GeoZarr discussions regarding CF convention complexity - Compatible with existing geospatial tools (GDAL, rasterio, pyproj) - Based on the proven STAC Projection Extension model From c317a4a2b1a251a27aaf49216ac8ceda48598ce4 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:16:26 +0200 Subject: [PATCH 14/38] Update attributes/geo:proj/README.md Co-authored-by: Davis Bennett --- attributes/geo:proj/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 6738e30..cf43b9d 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -8,7 +8,7 @@ ## Description -This extension defines a standardized way to encode coordinate reference system (CRS) information for geospatial Zarr arrays and groups using the `geo:proj` attribute. +This specification defines a JSON object that encodes coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention in which this object is stored under the `"geo:proj"` key in the attributes of Zarr arrays or groups. ## Motivation From 8a606969d2c8bc4849788a2ca83686b4f9b38767 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:20:40 +0200 Subject: [PATCH 15/38] Remove redundant examples for irregular grid and update WKT2 representation section in geo:proj README.md --- attributes/geo:proj/README.md | 33 ++------------------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index cf43b9d..c067e9f 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -139,37 +139,7 @@ This approach avoids redundancy and ensures consistency by using the array's own } ``` -### Example 3: Irregular Grid - -In this case, the spatial coordinates are stored in separate coordinate variables (`x` and `y`) that share the same dimension as the data array: - -```json -{ - "zarr_format": 3, - "shape": [1000], - "dimension_names": ["node"], - "attributes": { - "geo:proj": { - "code": "EPSG:4326", - "spatial_dimensions": ["node"] - } - }, - "coordinates": { - "x": { - "shape": [1000], - "dimension_names": ["node"] - }, - "y": { - "shape": [1000], - "dimension_names": ["node"] - } - } -} -``` - -Note: The coordinate variables contain the actual spatial positions in the CRS for each node. - -### Example 4: WKT2 Representation +### Example 3: WKT2 Representation ```json { @@ -185,6 +155,7 @@ Note: The coordinate variables contain the actual spatial positions in the CRS f } ``` + ## Inheritance The `geo:proj` attribute follows a simple group-to-array inheritance model: From 96b5db142bfd902d981aa6c7a1967c46c70e5efb Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:33:36 +0200 Subject: [PATCH 16/38] Refine spatial dimension identification section to focus on regular grids and remove redundant examples --- attributes/geo:proj/README.md | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index c067e9f..d091e2b 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -39,28 +39,7 @@ Note: The shape of spatial dimensions is obtained directly from the Zarr array m ### Spatial Dimension Identification -In this extension, "spatial dimensions" refers to the dimensions names of 2D / 3D arrays within this group to which the projection definition applies. - -For example: - -- Regular grid: dimensions like "y" and "x" directly map to spatial axes -- Irregular grid: may use a single dimension (e.g., "node") with explicit x(node) and y(node) coordinates storing spatial positions -- Structured but non-regular grid: may use dimensions like "latitude" and "longitude" with 2D coordinate variables - -A relevant example from CF conventions 1.12 (example 5.2) shows this distinction: - -```cdm - float xc(xc) ; - xc:axis = "X" ; - xc:long_name = "x-coordinate in Cartesian system" ; - xc:units = "m" ; - float yc(yc) ; - yc:axis = "Y" ; - yc:long_name = "y-coordinate in Cartesian system" ; - yc:units = "m" ; -``` - -Here, "xc" and "yc" are arbitrary dimension names, while "X" and "Y" identify the spatial axes they represent. +In this extension, "spatial dimensions" refers to the dimension names of 2D/3D arrays within this group to which the projection definition applies. This extension is designed for regular grids where dimensions directly correspond to spatial axes. The extension identifies these array dimensions through: From e9e754e78da6c814a8048c95ff48670d6b44d6bb Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:35:27 +0200 Subject: [PATCH 17/38] Refactor inheritance model section for clarity and remove redundant rules in geo:proj README.md --- attributes/geo:proj/README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index d091e2b..689afce 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -16,6 +16,19 @@ This specification defines a JSON object that encodes coordinate reference syste - Compatible with existing geospatial tools (GDAL, rasterio, pyproj) - Based on the proven STAC Projection Extension model +## Inheritance Model + +The `geo:proj` attribute follows a simple group-to-array inheritance model that should be understood first: + +### Inheritance Rules + +1. **Group-level definition** (recommended): When `geo:proj` is defined at the group level, it applies to all arrays within that group +2. **Array-level override**: An array can completely override the group's `geo:proj` attribute with its own definition +3. **Complete replacement only**: Partial inheritance (overriding only some fields while inheriting others) is not allowed +4. **No cascading**: Inheritance only applies from a group directly to its immediate array members, not through nested groups + +Most use cases will use group-level definitions without array overrides. + ## Specification The `geo:proj` attribute can be added to Zarr arrays or groups to define projection information. @@ -135,19 +148,6 @@ This approach avoids redundancy and ensures consistency by using the array's own ``` -## Inheritance - -The `geo:proj` attribute follows a simple group-to-array inheritance model: - -1. Inheritance Scope: - - Only applies from a group directly to its immediate array members - - Does not cascade through nested groups - - Groups cannot inherit from parent groups - -2. Override Rules: - - An array can completely override the group's `geo:proj` attribute with its own definition - - Partial inheritance (overriding only some fields while inheriting others) is not allowed - - If an array defines `geo:proj`, it must provide a complete definition ## Compatibility Notes From 1893b7beb4a9ea1c58a8f5597ca5a56c67482e83 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:37:20 +0200 Subject: [PATCH 18/38] Update examples in geo:proj README.md to enhance clarity and provide additional context for projection attributes --- attributes/geo:proj/README.md | 36 ++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 689afce..5b98884 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -98,32 +98,50 @@ This approach avoids redundancy and ensures consistency by using the array's own ## Examples -### Example 1: Simple EPSG Code +### Example 1: Simple Web Mercator Raster (Group Level) ```json { "zarr_format": 3, - "shape": [2048, 2048], - "dimension_names": ["y", "x"], + "node_type": "group", "attributes": { "geo:proj": { - "code": "EPSG:3857" + "code": "EPSG:3857", + "transform": [156543.03392804097, 0.0, -20037508.342789244, 0.0, -156543.03392804097, 20037508.342789244], + "bbox": [-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244] } } } ``` -### Example 2: With Multiple Dimensions and Transform +### Example 2: Multi-band Satellite Image ```json { "zarr_format": 3, - "shape": [365, 100, 2048, 2048, 4], - "dimension_names": ["time", "height", "latitude", "longitude", "band"], + "shape": [4, 2048, 2048], + "dimension_names": ["band", "y", "x"], + "attributes": { + "geo:proj": { + "code": "EPSG:32633", + "spatial_dimensions": ["y", "x"], + "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0], + "bbox": [500000.0, 4900000.0, 561440.0, 4961440.0] + } + } +} +``` + +### Example 3: Geographic Coordinates with Transform + +```json +{ + "zarr_format": 3, + "shape": [1800, 3600], + "dimension_names": ["lat", "lon"], "attributes": { "geo:proj": { "code": "EPSG:4326", - "spatial_dimensions": ["latitude", "longitude"], "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], "bbox": [-180.0, -90.0, 180.0, 90.0] } @@ -131,7 +149,7 @@ This approach avoids redundancy and ensures consistency by using the array's own } ``` -### Example 3: WKT2 Representation +### Example 4: WKT2 Representation ```json { From a244d908e735102fe94311f872a81119541d3413 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:40:48 +0200 Subject: [PATCH 19/38] Clarify spatial dimension interpretation in geo:proj README.md to emphasize lat/lon convention for "X" and "Y" dimensions --- attributes/geo:proj/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 5b98884..f790a0d 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -78,7 +78,7 @@ If `spatial_dimensions` is not provided, implementations should scan `dimension_ - ["northing", "easting"] - ["row", "col"] or ["line", "sample"] -The first matching pair determines the spatial dimensions. +The first matching pair determines the spatial dimensions. **Important**: When dimensions like "X" and "Y" are found, they are always interpreted as [Y, X] (following lat/lon convention), regardless of their actual order in the Zarr array's `dimension_names`. ### Validation Rules From 1cffe00eab3e78410d6771ecc149fe3013aaa718 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:51:05 +0200 Subject: [PATCH 20/38] Clarify terminology and improve consistency in geo:proj README.md regarding spatial dimension detection methods and recommended usage --- attributes/geo:proj/README.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index f790a0d..7cfc815 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -8,7 +8,9 @@ ## Description -This specification defines a JSON object that encodes coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention in which this object is stored under the `"geo:proj"` key in the attributes of Zarr arrays or groups. +This specification defines a JSON object that encodes coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention in which this object is stored under the `"geo:proj"` key in the attributes of Zarr groups or arrays. + +**Recommended usage**: Define `geo:proj` at the **group level** to apply CRS information to all arrays within that group. This matches the common geospatial pattern of storing multiple arrays with the same coordinates in a single group. Array-level definitions are supported for override cases but are less common. ## Motivation @@ -57,7 +59,7 @@ In this extension, "spatial dimensions" refers to the dimension names of 2D/3D a The extension identifies these array dimensions through: 1. **Explicit Declaration** (recommended): Use `spatial_dimensions` to specify dimension names -2. **Convention-Based** (fallback): Automatically detect standard spatial dimension names +2. **Pattern-Based Detection** (fallback): Automatically detect spatial dimensions using patterns defined by this extension #### Explicit Declaration @@ -69,9 +71,9 @@ The extension identifies these array dimensions through: } ``` -#### Convention-Based Detection +#### Pattern-Based Detection -If `spatial_dimensions` is not provided, implementations should scan `dimension_names` for these patterns (in order): +If `spatial_dimensions` is not provided, implementations should scan `dimension_names` for these patterns defined by this extension (in order): - ["y", "x"] or ["Y", "X"] - ["lat", "lon"] or ["latitude", "longitude"] @@ -82,7 +84,7 @@ The first matching pair determines the spatial dimensions. **Important**: When d ### Validation Rules -- Once spatial dimensions are identified (either explicitly through `spatial_dimensions` or through convention-based detection), their sizes are obtained from the Zarr array's shape metadata +- Once spatial dimensions are identified (either explicitly through `spatial_dimensions` or through pattern-based detection), their sizes are obtained from the Zarr array's shape metadata - The spatial dimension order is always [y/lat/northing, x/lon/easting] - If spatial dimensions cannot be identified through either method, implementations MUST raise an error - When multiple CRS representations are provided, precedence is: `projjson` > `wkt2` > `code` @@ -90,7 +92,7 @@ The first matching pair determines the spatial dimensions. **Important**: When d ### Shape Reconciliation The shape of spatial dimensions is determined by: -1. Identifying the spatial dimensions using either `spatial_dimensions` or convention-based detection +1. Identifying the spatial dimensions using either `spatial_dimensions` or pattern-based detection 2. Looking up these dimension names in the Zarr array's `dimension_names` 3. Using the corresponding sizes from the array's `shape` attribute From d903aa7426890518c94dc9e11720fd33b9871bf7 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 15:52:14 +0200 Subject: [PATCH 21/38] Remove the Registered Extensions section from attributes README.md to streamline content and focus on creating attribute extensions. --- attributes/README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/attributes/README.md b/attributes/README.md index e8bc2d0..f2ecd50 100644 --- a/attributes/README.md +++ b/attributes/README.md @@ -6,11 +6,6 @@ This directory contains specifications for Zarr v3 attribute extensions. Attribute extensions define standardized schemas and semantics for metadata stored in the attributes of Zarr arrays and groups. These extensions enable interoperability by establishing common conventions for domain-specific metadata. -## Registered Extensions - -| Extension | Version | Description | -|-----------|---------|-------------| -| [projection](./projection/) | 1.0.0 | Coordinate reference system metadata for geospatial data | ## Creating an Attribute Extension From f4364e4c6adb0b94c14734d6523934ccbaab313d Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 16:06:24 +0200 Subject: [PATCH 22/38] Update attributes/geo:proj/README.md Co-authored-by: Davis Bennett --- attributes/geo:proj/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 7cfc815..f8de191 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -37,12 +37,13 @@ The `geo:proj` attribute can be added to Zarr arrays or groups to define project ### Required Fields -At least one of the following MUST be provided: +- `version`: Version of the `geo:proj` extension being used + +and at least one of the following: - `code`: Authority and code identifier (e.g., "EPSG:4326") - `wkt2`: WKT2 string representation of the CRS - `projjson`: PROJJSON object representation of the CRS -- `version`: Version of the `geo:proj` extension being used ### Optional Fields From cc9f91351262ef527cf87ef05fa9d61b740327ce Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 16:46:32 +0200 Subject: [PATCH 23/38] Enhance README.md and schema.json for geo:proj extension by adding detailed property descriptions and improving schema documentation --- attributes/geo:proj/README.md | 76 +++++++++++++++++++++++++++------ attributes/geo:proj/schema.json | 1 + 2 files changed, 65 insertions(+), 12 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index f8de191..b8e0a9f 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -35,21 +35,75 @@ Most use cases will use group-level definitions without array overrides. The `geo:proj` attribute can be added to Zarr arrays or groups to define projection information. -### Required Fields + +**`geo:proj` Properties** -- `version`: Version of the `geo:proj` extension being used +| |Type|Description|Required| +|---|---|---|---| +|**version**|`string`|Projection metadata version| ✓ Yes| +|**code**|`["string", "null"]`|Authority:code identifier (e.g., EPSG:4326)|No| +|**wkt2**|`["string", "null"]`|WKT2 (ISO 19162) CRS representation|No| +|**projjson**|`any`|PROJJSON CRS representation|No| +|**bbox**|`number` `[]`|Bounding box in CRS coordinates|No| +|**transform**|`number` `[]`|Affine transformation coefficients|No| +|**spatial_dimensions**|`string` `[2]`|Names of spatial dimensions [y_name, x_name]|No| -and at least one of the following: +### Field Details -- `code`: Authority and code identifier (e.g., "EPSG:4326") -- `wkt2`: WKT2 string representation of the CRS -- `projjson`: PROJJSON object representation of the CRS +Additional properties are allowed. -### Optional Fields +#### geo:proj.version -- `bbox`: Bounding box in the CRS coordinates -- `transform`: Affine transformation coefficients (6 or 9 elements) -- `spatial_dimensions`: Names of spatial dimensions in the array +Projection metadata version + +* **Type**: `string` +* **Required**: ✓ Yes +* **Allowed values**: + * `"1.0"` + +#### geo:proj.code + +Authority:code identifier (e.g., EPSG:4326) + +* **Type**: `["string", "null"]` +* **Required**: No +* **Pattern**: `^[A-Z]+:[0-9]+$` + +#### geo:proj.wkt2 + +WKT2 (ISO 19162) CRS representation + +* **Type**: `["string", "null"]` +* **Required**: No + +#### geo:proj.projjson + +PROJJSON CRS representation + +* **Type**: `any` +* **Required**: No + +#### geo:proj.bbox + +Bounding box in CRS coordinates + +* **Type**: `number` `[]` +* **Required**: No + +#### geo:proj.transform + +Affine transformation coefficients + +* **Type**: `number` `[]` +* **Required**: No + +#### geo:proj.spatial_dimensions + +Names of spatial dimensions [y_name, x_name] + +* **Type**: `string` `[2]` +* **Required**: No + Note: The shape of spatial dimensions is obtained directly from the Zarr array metadata once the spatial dimensions are identified. @@ -168,8 +222,6 @@ This approach avoids redundancy and ensures consistency by using the array's own } ``` - - ## Compatibility Notes - The `version` field allows tracking of changes and ensures compatibility with future updates diff --git a/attributes/geo:proj/schema.json b/attributes/geo:proj/schema.json index 15f31ab..6555c47 100644 --- a/attributes/geo:proj/schema.json +++ b/attributes/geo:proj/schema.json @@ -96,6 +96,7 @@ "properties": { "version": { "type": "string", + "description": "Projection attribute extension version", "enum": ["1.0"] }, "code": { From 21b0d1c46fe94c718aad01b87d146cbce5824ced Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 14 Sep 2025 18:17:50 +0200 Subject: [PATCH 24/38] Refine validation rules in geo:proj README.md to clarify shape inference, spatial dimension order, error handling, and semantic identity requirements for CRS representations. --- attributes/geo:proj/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index b8e0a9f..a605cb6 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -139,14 +139,15 @@ The first matching pair determines the spatial dimensions. **Important**: When d ### Validation Rules -- Once spatial dimensions are identified (either explicitly through `spatial_dimensions` or through pattern-based detection), their sizes are obtained from the Zarr array's shape metadata -- The spatial dimension order is always [y/lat/northing, x/lon/easting] -- If spatial dimensions cannot be identified through either method, implementations MUST raise an error -- When multiple CRS representations are provided, precedence is: `projjson` > `wkt2` > `code` +- **Shape Inference**: Once spatial dimensions are identified (either explicitly through `spatial_dimensions` or through pattern-based detection), their sizes are obtained from the Zarr array's shape metadata +- **Spatial Dimension Order**: The spatial dimension order is always [y/lat/northing, x/lon/easting] +- **Error Handling**: If spatial dimensions cannot be identified through either method, implementations MUST raise an error +- **Semantic Identity Requirement**: If more than one CRS representation (`code`, `wkt2`, `projjson`) is provided, they MUST be semantically identical (i.e., describe the same coordinate reference system). Implementations SHOULD validate this consistency and raise an error if the representations describe different CRS ### Shape Reconciliation The shape of spatial dimensions is determined by: + 1. Identifying the spatial dimensions using either `spatial_dimensions` or pattern-based detection 2. Looking up these dimension names in the Zarr array's `dimension_names` 3. Using the corresponding sizes from the array's `shape` attribute From 1bde1bcf06d185b16f02dbb519777d8ca8e4aa37 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Wed, 17 Sep 2025 08:08:22 +0200 Subject: [PATCH 25/38] Update attributes/README.md Co-authored-by: Max Jones <14077947+maxrjones@users.noreply.github.com> --- attributes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/README.md b/attributes/README.md index f2ecd50..a4c0a15 100644 --- a/attributes/README.md +++ b/attributes/README.md @@ -11,7 +11,7 @@ Attribute extensions define standardized schemas and semantics for metadata stor When creating an attribute extension, consider: -1. **Namespace**: Use a unique prefix to avoid conflicts (e.g., `proj:` for projection) +1. **Namespace**: Use a unique prefix to avoid conflicts (e.g., `proj` for projection). Choose namespace characters that are compatible with all operating systems by avoiding special characters like colons (:) 2. **Schema**: Provide a JSON schema for validation 3. **Inheritance**: Define behavior when attributes are set at group vs array level 4. **Compatibility**: Consider interoperability with existing tools and standards From 190ad6022eba6f06ab6f4e6f992e4bf80ba4de8d Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Wed, 17 Sep 2025 08:11:47 +0200 Subject: [PATCH 26/38] Update versioning in geo:proj README.md and schema.json to 0.1.0 --- attributes/geo:proj/README.md | 4 ++-- attributes/geo:proj/schema.json | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index a605cb6..c023922 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -1,7 +1,7 @@ # Projection Attribute Extension for Zarr - **Extension Name**: Projection Attribute Extension -- **Version**: 1.0.0 +- **Version**: 0.1.0 - **Extension Type**: Attribute - **Status**: Proposed - **Owners**: @emmanuelmathot @@ -59,7 +59,7 @@ Projection metadata version * **Type**: `string` * **Required**: ✓ Yes * **Allowed values**: - * `"1.0"` + * `"0.1"` #### geo:proj.code diff --git a/attributes/geo:proj/schema.json b/attributes/geo:proj/schema.json index 6555c47..ddb2774 100644 --- a/attributes/geo:proj/schema.json +++ b/attributes/geo:proj/schema.json @@ -1,11 +1,11 @@ { "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://zarr-specs.readthedocs.io/en/latest/extensions/attributes/projection/v1.0.0/schema.json", + "$id": "https://zarr-specs.readthedocs.io/en/latest/extensions/attributes/projection/v0.1.0/schema.json", "title": "Zarr Projection Attribute Extension", "description": "Projection attribute extension for Zarr arrays and groups", "type": "object", "definitions": { - "projectionMetadataV1_0": { + "projectionMetadataV0_1": { "type": "object", "properties": { "code": { @@ -97,7 +97,7 @@ "version": { "type": "string", "description": "Projection attribute extension version", - "enum": ["1.0"] + "enum": ["0.1"] }, "code": { "type": ["string", "null"], From a4b2befe981ee66be718bdae6f655d884b29e81a Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Wed, 17 Sep 2025 08:15:30 +0200 Subject: [PATCH 27/38] Update attributes/geo:proj/README.md Co-authored-by: Max Jones <14077947+maxrjones@users.noreply.github.com> --- attributes/geo:proj/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index c023922..3ec2aa5 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -24,10 +24,9 @@ The `geo:proj` attribute follows a simple group-to-array inheritance model that ### Inheritance Rules -1. **Group-level definition** (recommended): When `geo:proj` is defined at the group level, it applies to all arrays within that group +1. **Group-level definition** (recommended): When `geo:proj` is defined at the group level, it applies to all arrays that are direct children of that group. It does not apply to groups or arrays deeper in the hierarchy (e.g., grandchildren). 2. **Array-level override**: An array can completely override the group's `geo:proj` attribute with its own definition 3. **Complete replacement only**: Partial inheritance (overriding only some fields while inheriting others) is not allowed -4. **No cascading**: Inheritance only applies from a group directly to its immediate array members, not through nested groups Most use cases will use group-level definitions without array overrides. From 544687d72462cf1c03b008cd788095fd4625258d Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Wed, 17 Sep 2025 08:19:59 +0200 Subject: [PATCH 28/38] Update attributes/geo:proj/README.md Co-authored-by: Max Jones <14077947+maxrjones@users.noreply.github.com> --- attributes/geo:proj/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 3ec2aa5..6662ed3 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -8,7 +8,7 @@ ## Description -This specification defines a JSON object that encodes coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention in which this object is stored under the `"geo:proj"` key in the attributes of Zarr groups or arrays. +This specification defines a JSON object that encodes datum and coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention for storing this object under the `"geo:proj"` key in the attributes of Zarr groups or arrays. **Recommended usage**: Define `geo:proj` at the **group level** to apply CRS information to all arrays within that group. This matches the common geospatial pattern of storing multiple arrays with the same coordinates in a single group. Array-level definitions are supported for override cases but are less common. From 4582d2ab424b2a88534f7b1ee9d48b7cb1cbe07c Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Wed, 17 Sep 2025 08:39:34 +0200 Subject: [PATCH 29/38] Enhance README.md with projection authority examples and clarify CRS null cases; simplify schema.json bbox definition. --- attributes/geo:proj/README.md | 72 +++++++++++++++++++++++++++++++++ attributes/geo:proj/schema.json | 13 ++---- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 6662ed3..e18a5d9 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -68,6 +68,24 @@ Authority:code identifier (e.g., EPSG:4326) * **Required**: No * **Pattern**: `^[A-Z]+:[0-9]+$` +Projection codes are identified by a string. The [proj](https://proj.org/) library defines projections +using "authority:code", e.g., "EPSG:4326" or "IAU_2015:30100". Different projection authorities may define +different string formats. Examples of known projection authorities, where when can find well known codes that +clients are likely to support are listed in the following table. + +| Authority Name | URL | +| --------------------------------------- | ---------------------------------------------------------- | +| European Petroleum Survey Groups (EPSG) | or | +| International Astronomical Union (IAU) | | +| Open Geospatial Consortium (OGC) | | +| ESRI | | + +The `geo:proj.code` field SHOULD be set to `null` in the following cases: + +- The data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. +- A CRS exists, but there is no valid EPSG code for it. In this case, the CRS should be provided in `geo:proj.wkt2` and/or `geo:proj.projjson`. + Clients can prefer to take either, although there may be discrepancies in how each might be interpreted. + #### geo:proj.wkt2 WKT2 (ISO 19162) CRS representation @@ -75,6 +93,14 @@ WKT2 (ISO 19162) CRS representation * **Type**: `["string", "null"]` * **Required**: No +A Coordinate Reference System (CRS) is the data reference system (sometimes called a 'projection') +used by the asset data. This value is a [WKT2](http://docs.opengeospatial.org/is/12-063r5/12-063r5.html) string. + +This field SHOULD be set to `null` in the following cases: + +- The asset data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. +- A CRS exists, but there is no valid WKT2 string for it. + #### geo:proj.projjson PROJJSON CRS representation @@ -82,6 +108,15 @@ PROJJSON CRS representation * **Type**: `any` * **Required**: No +A Coordinate Reference System (CRS) is the data reference system (sometimes called a 'projection') +used by the asset data. This value is a [PROJJSON](https://proj.org/specifications/projjson.html) object, +see the [JSON Schema](https://proj.org/schemas/v0.5/projjson.schema.json) for details. + +This field SHOULD be set to `null` in the following cases: + +- The asset data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. +- A CRS exists, but there is no valid PROJJSON for it. + #### geo:proj.bbox Bounding box in CRS coordinates @@ -89,6 +124,14 @@ Bounding box in CRS coordinates * **Type**: `number` `[]` * **Required**: No +Bounding box of the assets represented by this Item in the asset data CRS. Specified as 4 coordinates +based on the CRS defined in the `proj:code`, `proj:projjson` or `proj:wkt2` fields. First two numbers are coordinates +of the lower left corner, followed by coordinates of upper right corner, , e.g., \[west, south, east, north], +\[xmin, ymin, xmax, ymax], \[left, down, right, up], or \[west, south, lowest, east, north, highest]. +The length of the array must be 2\*n where n is the number of dimensions. The array contains all axes of the southwesterly +most extent followed by all axes of the northeasterly most extent specified in Longitude/Latitude +based on [WGS 84](http://www.opengis.net/def/crs/OGC/1.3/CRS84). + #### geo:proj.transform Affine transformation coefficients @@ -96,12 +139,41 @@ Affine transformation coefficients * **Type**: `number` `[]` * **Required**: No +Linear mapping from pixel coordinate space (Pixel, Line) to projection coordinate space (Xp, Yp). It is +a `3x3` matrix stored as a flat array of 9 elements in row major order. Since the last row is always `0,0,1` it can be omitted, +in which case only 6 elements are recorded. This mapping can be obtained from +GDAL([`GetGeoTransform`](https://gdal.org/api/gdaldataset_cpp.html#_CPPv4N11GDALDataset15GetGeoTransformEPd), requires re-ordering) +or the Rasterio ([`Transform`](https://rasterio.readthedocs.io/en/stable/api/rasterio.io.html#rasterio.io.BufferedDatasetWriter.transform)). +To get it on the command line you can use the [Rasterio CLI](https://rasterio.readthedocs.io/en/latest/cli.html) with the +[info](https://rasterio.readthedocs.io/en/latest/cli.html#info) command: `$ rio info`. + +```txt + [Xp] [a0, a1, a2] [Pixel] + [Yp] = [a3, a4, a5] * [Line ] + [1 ] [0 , 0, 1] [1 ] +``` + +If the transform is defined in Item Properties, it is used as the default transform for all assets that don't have an overriding transform. + +Note that `GetGeoTransform` and `rasterio` use different formats for reporting transform information. Order expected in `geo:proj.transform` is the +same as reported by `rasterio`. When using GDAL method you need to re-order in the following way: + +```python +g = GetGeoTransform(...) +proj_transform = [g[1], g[2], g[0], + g[4], g[5], g[3], + 0, 0, 1] +``` + #### geo:proj.spatial_dimensions Names of spatial dimensions [y_name, x_name] * **Type**: `string` `[2]` * **Required**: No + +See the [Spatial Dimension Identification](#spatial-dimension-identification) section below for details on how spatial dimensions are identified. + Note: The shape of spatial dimensions is obtained directly from the Zarr array metadata once the spatial dimensions are identified. diff --git a/attributes/geo:proj/schema.json b/attributes/geo:proj/schema.json index ddb2774..febc232 100644 --- a/attributes/geo:proj/schema.json +++ b/attributes/geo:proj/schema.json @@ -121,16 +121,9 @@ }, "bbox": { "type": "array", - "oneOf": [ - { - "minItems": 4, - "maxItems": 4 - }, - { - "minItems": 6, - "maxItems": 6 - } - ], + "minItems": 4, + "maxItems": 4 + }, "items": { "type": "number" }, From 1c0134f8da3d229cc4745c8666f6a1f7707f09e6 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Wed, 17 Sep 2025 09:14:54 +0200 Subject: [PATCH 30/38] Remove redundant emphasis on spatial dimensions interpretation in geo:proj README.md --- attributes/geo:proj/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index e18a5d9..e4b0553 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -206,7 +206,7 @@ If `spatial_dimensions` is not provided, implementations should scan `dimension_ - ["northing", "easting"] - ["row", "col"] or ["line", "sample"] -The first matching pair determines the spatial dimensions. **Important**: When dimensions like "X" and "Y" are found, they are always interpreted as [Y, X] (following lat/lon convention), regardless of their actual order in the Zarr array's `dimension_names`. +The first matching pair determines the spatial dimensions. ### Validation Rules From ac841d829d8576e2cc3c287a335f9c7f3633ffe5 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Wed, 17 Sep 2025 09:37:56 +0200 Subject: [PATCH 31/38] Add algorithm for resolving spatial dimensions in group-level geo:proj --- attributes/geo:proj/README.md | 51 ++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index e4b0553..9bf7761 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -208,22 +208,61 @@ If `spatial_dimensions` is not provided, implementations should scan `dimension_ The first matching pair determines the spatial dimensions. +#### Group-Level geo:proj with Array-Level dimension_names + +When `geo:proj` is defined at the group level but `spatial_dimensions` is not explicitly provided, implementations must handle the fact that `dimension_names` are defined at the individual array level in Zarr v3. The following algorithm defines how to resolve spatial dimensions: + +**For Explicit Declaration (when `spatial_dimensions` is provided):** + +1. Use the specified `spatial_dimensions` directly +2. Validate that each data array in the group contains these dimension names +3. If any data array lacks the specified spatial dimensions, ignore that array for the purpose of applying `geo:proj` +4. If no data arrays contain the specified spatial dimensions, implementations MUST raise an error + +**For Pattern-Based Detection (when `spatial_dimensions` is not provided):** + +1. Scan all data arrays within the group +2. For each data array, examine its `dimension_names` for the defined patterns +3. Use the first matching pattern found across all data arrays +4. If no spatial dimension patterns are found in any data array, implementations MUST raise an error + +**Example of Valid Group-Level Configuration:** + +```json +{ + "zarr_format": 3, + "node_type": "group", + "attributes": { + "geo:proj": { + "code": "EPSG:4326", + "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0] + } + } +} +``` + +With data arrays: + +- `temperature/`: `dimension_names: ["time", "lat", "lon"]` ✅ Contains ["lat", "lon"] +- `precipitation/`: `dimension_names: ["time", "lat", "lon"]` ✅ Same pattern +- `lat/`: `dimension_names: ["lat"]` ⚠️ Excluded (coordinate array) +- `lon/`: `dimension_names: ["lon"]` ⚠️ Excluded (coordinate array) + ### Validation Rules - **Shape Inference**: Once spatial dimensions are identified (either explicitly through `spatial_dimensions` or through pattern-based detection), their sizes are obtained from the Zarr array's shape metadata -- **Spatial Dimension Order**: The spatial dimension order is always [y/lat/northing, x/lon/easting] - **Error Handling**: If spatial dimensions cannot be identified through either method, implementations MUST raise an error - **Semantic Identity Requirement**: If more than one CRS representation (`code`, `wkt2`, `projjson`) is provided, they MUST be semantically identical (i.e., describe the same coordinate reference system). Implementations SHOULD validate this consistency and raise an error if the representations describe different CRS ### Shape Reconciliation -The shape of spatial dimensions is determined by: +The shape of spatial dimensions is determined on a per-array basis: -1. Identifying the spatial dimensions using either `spatial_dimensions` or pattern-based detection -2. Looking up these dimension names in the Zarr array's `dimension_names` -3. Using the corresponding sizes from the array's `shape` attribute +1. Identifying the spatial dimensions using either `spatial_dimensions` or pattern-based detection (as described above) +2. For each data array that the `geo:proj` applies to, looking up the spatial dimension names in that array's `dimension_names` +3. Using the corresponding sizes from that same array's `shape` attribute -This approach avoids redundancy and ensures consistency by using the array's own metadata rather than duplicating shape information. +This approach avoids redundancy and ensures consistency by using each array's own metadata rather than duplicating shape information. ## Examples From 803f269b4d83884071276446b18c64fcc8c95023 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Wed, 17 Sep 2025 20:12:39 +0200 Subject: [PATCH 32/38] Clarify flexibility in spatial dimension ordering in geo:proj README.md --- attributes/geo:proj/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/attributes/geo:proj/README.md b/attributes/geo:proj/README.md index 9bf7761..067a230 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo:proj/README.md @@ -248,6 +248,11 @@ With data arrays: - `lat/`: `dimension_names: ["lat"]` ⚠️ Excluded (coordinate array) - `lon/`: `dimension_names: ["lon"]` ⚠️ Excluded (coordinate array) +**Note**: The ordering of spatial dimensions is flexible. Both `["lat", "lon"]` and `["lon", "lat"]` orderings are valid: + +- `temperature/`: `dimension_names: ["time", "lon", "lat"]` ✅ Also valid +- `precipitation/`: `dimension_names: ["time", "lon", "lat"]` ✅ Also valid + ### Validation Rules - **Shape Inference**: Once spatial dimensions are identified (either explicitly through `spatial_dimensions` or through pattern-based detection), their sizes are obtained from the Zarr array's shape metadata From 27a1123c1f6d49b87ac6af935aebe5b34af55bd1 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 21 Sep 2025 21:08:12 +0200 Subject: [PATCH 33/38] Refactor Geo Projection Attribute Extension - Deleted the old README.md and schema.json files for the geo:proj extension. - Created a new README.md that clarifies the usage of the `proj` key under the `geo` dictionary, including inheritance rules and detailed property descriptions. - Updated the schema.json to reflect the new structure and properties for the geo projection attributes, ensuring compatibility with the latest specifications. --- attributes/{geo:proj => geo/proj}/README.md | 104 ++++++------ attributes/geo/proj/schema.json | 171 ++++++++++++++++++++ attributes/geo:proj/schema.json | 166 ------------------- 3 files changed, 228 insertions(+), 213 deletions(-) rename attributes/{geo:proj => geo/proj}/README.md (74%) create mode 100644 attributes/geo/proj/schema.json delete mode 100644 attributes/geo:proj/schema.json diff --git a/attributes/geo:proj/README.md b/attributes/geo/proj/README.md similarity index 74% rename from attributes/geo:proj/README.md rename to attributes/geo/proj/README.md index 067a230..78bd477 100644 --- a/attributes/geo:proj/README.md +++ b/attributes/geo/proj/README.md @@ -1,6 +1,6 @@ -# Projection Attribute Extension for Zarr +# Geo Projection Attribute Extension for Zarr -- **Extension Name**: Projection Attribute Extension +- **Extension Name**: Geo Projection Attribute Extension - **Version**: 0.1.0 - **Extension Type**: Attribute - **Status**: Proposed @@ -8,9 +8,9 @@ ## Description -This specification defines a JSON object that encodes datum and coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention for storing this object under the `"geo:proj"` key in the attributes of Zarr groups or arrays. +This specification defines a JSON object that encodes datum and coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention for storing this object under the `proj` key within the `geo` dictionary in the attributes of Zarr groups or arrays. -**Recommended usage**: Define `geo:proj` at the **group level** to apply CRS information to all arrays within that group. This matches the common geospatial pattern of storing multiple arrays with the same coordinates in a single group. Array-level definitions are supported for override cases but are less common. +**Recommended usage**: Define the `proj` key under the `geo` dictionary at the **group level** to apply CRS information to all arrays within that group. This matches the common geospatial pattern of storing multiple arrays with the same coordinates in a single group. Array-level definitions are supported for override cases but are less common. ## Motivation @@ -20,22 +20,22 @@ This specification defines a JSON object that encodes datum and coordinate refer ## Inheritance Model -The `geo:proj` attribute follows a simple group-to-array inheritance model that should be understood first: +The `proj` key under the `geo` dictionary follows a simple group-to-array inheritance model that should be understood first: ### Inheritance Rules -1. **Group-level definition** (recommended): When `geo:proj` is defined at the group level, it applies to all arrays that are direct children of that group. It does not apply to groups or arrays deeper in the hierarchy (e.g., grandchildren). -2. **Array-level override**: An array can completely override the group's `geo:proj` attribute with its own definition -3. **Complete replacement only**: Partial inheritance (overriding only some fields while inheriting others) is not allowed +1. **Group-level definition** (recommended): When `proj` is defined under the `geo` dictionary at the group level, it applies to all arrays that are direct children of that group. It does not apply to groups or arrays deeper in the hierarchy (e.g., grandchildren). +2. **Array-level override**: An array can completely override the group's `proj` key with its own definition. +3. **Complete replacement only**: Partial inheritance (overriding only some fields while inheriting others) is not allowed. Most use cases will use group-level definitions without array overrides. ## Specification -The `geo:proj` attribute can be added to Zarr arrays or groups to define projection information. +The `proj` key under the `geo` dictionary can be added to Zarr arrays or groups to define projection information. -**`geo:proj` Properties** +**`geo -> proj` Properties** | |Type|Description|Required| |---|---|---|---| @@ -51,16 +51,15 @@ The `geo:proj` attribute can be added to Zarr arrays or groups to define project Additional properties are allowed. -#### geo:proj.version +#### geo -> proj.version Projection metadata version * **Type**: `string` * **Required**: ✓ Yes -* **Allowed values**: - * `"0.1"` +* **Allowed values**: `0.1` -#### geo:proj.code +#### geo -> proj.code Authority:code identifier (e.g., EPSG:4326) @@ -80,13 +79,13 @@ clients are likely to support are listed in the following table. | Open Geospatial Consortium (OGC) | | | ESRI | | -The `geo:proj.code` field SHOULD be set to `null` in the following cases: +The `proj.code` field SHOULD be set to `null` in the following cases: - The data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. -- A CRS exists, but there is no valid EPSG code for it. In this case, the CRS should be provided in `geo:proj.wkt2` and/or `geo:proj.projjson`. +- A CRS exists, but there is no valid EPSG code for it. In this case, the CRS should be provided in `proj.wkt2` and/or `proj.projjson`. Clients can prefer to take either, although there may be discrepancies in how each might be interpreted. -#### geo:proj.wkt2 +#### geo -> proj.wkt2 WKT2 (ISO 19162) CRS representation @@ -101,7 +100,7 @@ This field SHOULD be set to `null` in the following cases: - The asset data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. - A CRS exists, but there is no valid WKT2 string for it. -#### geo:proj.projjson +#### geo -> proj.projjson PROJJSON CRS representation @@ -117,7 +116,7 @@ This field SHOULD be set to `null` in the following cases: - The asset data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. - A CRS exists, but there is no valid PROJJSON for it. -#### geo:proj.bbox +#### geo -> proj.bbox Bounding box in CRS coordinates @@ -125,14 +124,14 @@ Bounding box in CRS coordinates * **Required**: No Bounding box of the assets represented by this Item in the asset data CRS. Specified as 4 coordinates -based on the CRS defined in the `proj:code`, `proj:projjson` or `proj:wkt2` fields. First two numbers are coordinates +based on the CRS defined in the `proj.code`, `proj.projjson` or `proj.wkt2` fields. First two numbers are coordinates of the lower left corner, followed by coordinates of upper right corner, , e.g., \[west, south, east, north], \[xmin, ymin, xmax, ymax], \[left, down, right, up], or \[west, south, lowest, east, north, highest]. The length of the array must be 2\*n where n is the number of dimensions. The array contains all axes of the southwesterly most extent followed by all axes of the northeasterly most extent specified in Longitude/Latitude based on [WGS 84](http://www.opengis.net/def/crs/OGC/1.3/CRS84). -#### geo:proj.transform +#### geo -> proj.transform Affine transformation coefficients @@ -155,8 +154,7 @@ To get it on the command line you can use the [Rasterio CLI](https://rasterio.re If the transform is defined in Item Properties, it is used as the default transform for all assets that don't have an overriding transform. -Note that `GetGeoTransform` and `rasterio` use different formats for reporting transform information. Order expected in `geo:proj.transform` is the -same as reported by `rasterio`. When using GDAL method you need to re-order in the following way: +Note that `GetGeoTransform` and `rasterio` use different formats for reporting transform information. Order expected in `proj.transform` is the same as reported by `rasterio`. When using GDAL method you need to re-order in the following way: ```python g = GetGeoTransform(...) @@ -165,7 +163,7 @@ proj_transform = [g[1], g[2], g[0], 0, 0, 1] ``` -#### geo:proj.spatial_dimensions +#### geo -> proj.spatial_dimensions Names of spatial dimensions [y_name, x_name] @@ -191,8 +189,10 @@ The extension identifies these array dimensions through: ```json { - "geo:proj": { - "spatial_dimensions": ["latitude", "longitude"] + "geo": { + "proj": { + "spatial_dimensions": ["latitude", "longitude"] + } } } ``` @@ -208,9 +208,9 @@ If `spatial_dimensions` is not provided, implementations should scan `dimension_ The first matching pair determines the spatial dimensions. -#### Group-Level geo:proj with Array-Level dimension_names +#### Group-Level geo -> proj with Array-Level dimension_names -When `geo:proj` is defined at the group level but `spatial_dimensions` is not explicitly provided, implementations must handle the fact that `dimension_names` are defined at the individual array level in Zarr v3. The following algorithm defines how to resolve spatial dimensions: +When `proj` is defined under the `geo` dictionary at the group level but `spatial_dimensions` is not explicitly provided, implementations must handle the fact that `dimension_names` are defined at the individual array level in Zarr v3. The following algorithm defines how to resolve spatial dimensions: **For Explicit Declaration (when `spatial_dimensions` is provided):** @@ -233,9 +233,11 @@ When `geo:proj` is defined at the group level but `spatial_dimensions` is not ex "zarr_format": 3, "node_type": "group", "attributes": { - "geo:proj": { - "code": "EPSG:4326", - "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0] + "geo": { + "proj": { + "code": "EPSG:4326", + "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0] + } } } } @@ -278,10 +280,12 @@ This approach avoids redundancy and ensures consistency by using each array's ow "zarr_format": 3, "node_type": "group", "attributes": { - "geo:proj": { - "code": "EPSG:3857", - "transform": [156543.03392804097, 0.0, -20037508.342789244, 0.0, -156543.03392804097, 20037508.342789244], - "bbox": [-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244] + "geo": { + "proj": { + "code": "EPSG:3857", + "transform": [156543.03392804097, 0.0, -20037508.342789244, 0.0, -156543.03392804097, 20037508.342789244], + "bbox": [-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244] + } } } } @@ -295,11 +299,13 @@ This approach avoids redundancy and ensures consistency by using each array's ow "shape": [4, 2048, 2048], "dimension_names": ["band", "y", "x"], "attributes": { - "geo:proj": { - "code": "EPSG:32633", - "spatial_dimensions": ["y", "x"], - "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0], - "bbox": [500000.0, 4900000.0, 561440.0, 4961440.0] + "geo": { + "proj": { + "code": "EPSG:32633", + "spatial_dimensions": ["y", "x"], + "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0], + "bbox": [500000.0, 4900000.0, 561440.0, 4961440.0] + } } } } @@ -313,10 +319,12 @@ This approach avoids redundancy and ensures consistency by using each array's ow "shape": [1800, 3600], "dimension_names": ["lat", "lon"], "attributes": { - "geo:proj": { - "code": "EPSG:4326", - "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], - "bbox": [-180.0, -90.0, 180.0, 90.0] + "geo": { + "proj": { + "code": "EPSG:4326", + "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], + "bbox": [-180.0, -90.0, 180.0, 90.0] + } } } } @@ -330,9 +338,11 @@ This approach avoids redundancy and ensures consistency by using each array's ow "shape": [1000, 1000], "dimension_names": ["northing", "easting"], "attributes": { - "geo:proj": { - "wkt2": "PROJCRS[\"WGS 84 / UTM zone 33N\",BASEGEOGCRS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]]],CONVERSION[\"UTM zone 33N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1]]],CS[Cartesian,2],AXIS[\"easting\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],AXIS[\"northing\",north,ORDER[2],LENGTHUNIT[\"metre\",1]]]", - "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0] + "geo": { + "proj": { + "wkt2": "PROJCRS[\"WGS 84 / UTM zone 33N\",BASEGEOGCRS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]]],CONVERSION[\"UTM zone 33N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1]]],CS[Cartesian,2],AXIS[\"easting\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],AXIS[\"northing\",north,ORDER[2],LENGTHUNIT[\"metre\",1]]]", + "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0] + } } } } diff --git a/attributes/geo/proj/schema.json b/attributes/geo/proj/schema.json new file mode 100644 index 0000000..90d0de5 --- /dev/null +++ b/attributes/geo/proj/schema.json @@ -0,0 +1,171 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://zarr-specs.readthedocs.io/en/latest/extensions/attributes/projection/v0.1.0/schema.json", + "title": "Zarr Geo Projection Attribute Extension", + "description": "Geo Projection attribute extension for Zarr arrays and groups", + "type": "object", + "definitions": { + "projectionMetadataV0_1": { + "type": "object", + "properties": { + "code": { + "type": ["string", "null"], + "description": "Authority:code identifier (e.g., EPSG:4326)", + "pattern": "^[A-Z]+:[0-9]+$" + }, + "wkt2": { + "type": ["string", "null"], + "description": "WKT2 (ISO 19162) CRS representation" + }, + "projjson": { + "oneOf": [ + { + "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" + }, + { + "type": "null" + } + ], + "description": "PROJJSON CRS representation" + }, + "bbox": { + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + }, + "description": "Bounding box in CRS coordinates" + }, + "transform": { + "type": "array", + "oneOf": [ + { + "minItems": 6, + "maxItems": 6 + }, + { + "minItems": 9, + "maxItems": 9 + } + ], + "items": { + "type": "number" + }, + "description": "Affine transformation coefficients" + }, + "spatial_dimensions": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + }, + "description": "Names of spatial dimensions [y_name, x_name]" + } + }, + "oneOf": [ + { + "required": ["code"] + }, + { + "required": ["wkt2"] + }, + { + "required": ["projjson"] + } + ] + } + }, + "properties": { + "attributes": { + "type": "object", + "properties": { + "geo": { + "type": "object", + "description": "Geo extension namespace", + "properties": { + "proj": { + "anyOf": [ + { + "type": "object", + "properties": { + "version": { + "type": "string", + "description": "Projection attribute extension version", + "enum": ["0.1"] + }, + "code": { + "type": ["string", "null"], + "description": "Authority:code identifier (e.g., EPSG:4326)", + "pattern": "^[A-Z]+:[0-9]+$" + }, + "wkt2": { + "type": ["string", "null"], + "description": "WKT2 (ISO 19162) CRS representation" + }, + "projjson": { + "oneOf": [ + { + "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" + }, + { + "type": "null" + } + ], + "description": "PROJJSON CRS representation" + }, + "bbox": { + "type": "array", + "minItems": 4, + "maxItems": 4, + "items": { + "type": "number" + }, + "description": "Bounding box in CRS coordinates" + }, + "transform": { + "type": "array", + "oneOf": [ + { + "minItems": 6, + "maxItems": 6 + }, + { + "minItems": 9, + "maxItems": 9 + } + ], + "items": { + "type": "number" + }, + "description": "Affine transformation coefficients" + }, + "spatial_dimensions": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + }, + "description": "Names of spatial dimensions [y_name, x_name]" + } + }, + "required": ["version"] + } + ] + } + } + } + } + } + } +} diff --git a/attributes/geo:proj/schema.json b/attributes/geo:proj/schema.json deleted file mode 100644 index febc232..0000000 --- a/attributes/geo:proj/schema.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://zarr-specs.readthedocs.io/en/latest/extensions/attributes/projection/v0.1.0/schema.json", - "title": "Zarr Projection Attribute Extension", - "description": "Projection attribute extension for Zarr arrays and groups", - "type": "object", - "definitions": { - "projectionMetadataV0_1": { - "type": "object", - "properties": { - "code": { - "type": ["string", "null"], - "description": "Authority:code identifier (e.g., EPSG:4326)", - "pattern": "^[A-Z]+:[0-9]+$" - }, - "wkt2": { - "type": ["string", "null"], - "description": "WKT2 (ISO 19162) CRS representation" - }, - "projjson": { - "oneOf": [ - { - "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" - }, - { - "type": "null" - } - ], - "description": "PROJJSON CRS representation" - }, - "bbox": { - "type": "array", - "oneOf": [ - { - "minItems": 4, - "maxItems": 4 - }, - { - "minItems": 6, - "maxItems": 6 - } - ], - "items": { - "type": "number" - }, - "description": "Bounding box in CRS coordinates" - }, - "transform": { - "type": "array", - "oneOf": [ - { - "minItems": 6, - "maxItems": 6 - }, - { - "minItems": 9, - "maxItems": 9 - } - ], - "items": { - "type": "number" - }, - "description": "Affine transformation coefficients" - }, - "spatial_dimensions": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": { - "type": "string" - }, - "description": "Names of spatial dimensions [y_name, x_name]" - } - }, - "oneOf": [ - { - "required": ["code"] - }, - { - "required": ["wkt2"] - }, - { - "required": ["projjson"] - } - ] - } - }, - "properties": { - "attributes": { - "type": "object", - "properties": { - "geo:proj": { - "anyOf": [ - { - "type": "object", - "properties": { - "version": { - "type": "string", - "description": "Projection attribute extension version", - "enum": ["0.1"] - }, - "code": { - "type": ["string", "null"], - "description": "Authority:code identifier (e.g., EPSG:4326)", - "pattern": "^[A-Z]+:[0-9]+$" - }, - "wkt2": { - "type": ["string", "null"], - "description": "WKT2 (ISO 19162) CRS representation" - }, - "projjson": { - "oneOf": [ - { - "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" - }, - { - "type": "null" - } - ], - "description": "PROJJSON CRS representation" - }, - "bbox": { - "type": "array", - "minItems": 4, - "maxItems": 4 - }, - "items": { - "type": "number" - }, - "description": "Bounding box in CRS coordinates" - }, - "transform": { - "type": "array", - "oneOf": [ - { - "minItems": 6, - "maxItems": 6 - }, - { - "minItems": 9, - "maxItems": 9 - } - ], - "items": { - "type": "number" - }, - "description": "Affine transformation coefficients" - }, - "spatial_dimensions": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": { - "type": "string" - }, - "description": "Names of spatial dimensions [y_name, x_name]" - } - }, - "required": ["version"] - } - ] - } - } - } - } -} From eeb64ae982dfec6e0098509ec9f10ffa13e39ff4 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Mon, 22 Sep 2025 17:25:01 +0200 Subject: [PATCH 34/38] Update attributes/geo/proj/README.md Co-authored-by: Davis Bennett --- attributes/geo/proj/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/geo/proj/README.md b/attributes/geo/proj/README.md index 78bd477..45e13d6 100644 --- a/attributes/geo/proj/README.md +++ b/attributes/geo/proj/README.md @@ -10,7 +10,7 @@ This specification defines a JSON object that encodes datum and coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention for storing this object under the `proj` key within the `geo` dictionary in the attributes of Zarr groups or arrays. -**Recommended usage**: Define the `proj` key under the `geo` dictionary at the **group level** to apply CRS information to all arrays within that group. This matches the common geospatial pattern of storing multiple arrays with the same coordinates in a single group. Array-level definitions are supported for override cases but are less common. +**Recommended usage**: Store the CRS-encoding JSON object defined in this specification under the `"proj"` key in the `attributes` of a **group** that contains arrays to declare CRS metadata for those arrays. This supports the common geospatial practice of storing multiple arrays with the same coordinates in a single group. Array-level definitions are supported for override cases but are less common. ## Motivation From c84a05eb5019c1593af2cea4fb2a90de126ec5c7 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 28 Sep 2025 15:05:33 +0200 Subject: [PATCH 35/38] Update README.md for Geo Projection Attribute Extension and remove schema.json --- attributes/geo/proj/README.md | 356 +------------------------------- attributes/geo/proj/schema.json | 171 --------------- 2 files changed, 11 insertions(+), 516 deletions(-) delete mode 100644 attributes/geo/proj/schema.json diff --git a/attributes/geo/proj/README.md b/attributes/geo/proj/README.md index 45e13d6..daf4e6f 100644 --- a/attributes/geo/proj/README.md +++ b/attributes/geo/proj/README.md @@ -8,355 +8,21 @@ ## Description -This specification defines a JSON object that encodes datum and coordinate reference system (CRS) information for geospatial data. Additionally, this specification defines a convention for storing this object under the `proj` key within the `geo` dictionary in the attributes of Zarr groups or arrays. +This specification defines a JSON object that encodes datum and coordinate reference system (CRS) information for geospatial data stored under the `proj` key within the `geo` dictionary in the attributes of Zarr groups or arrays. -**Recommended usage**: Store the CRS-encoding JSON object defined in this specification under the `"proj"` key in the `attributes` of a **group** that contains arrays to declare CRS metadata for those arrays. This supports the common geospatial practice of storing multiple arrays with the same coordinates in a single group. Array-level definitions are supported for override cases but are less common. +## External Specification -## Motivation +The complete specification, including detailed documentation, JSON schema, examples, and implementation notes, is maintained in the [EOPF Data Model repository](https://github.com/EOPF/eopf-explorer/tree/main/data-model/attributes/geo/proj). -- Provides simple, standardized CRS encoding without complex nested structures -- Compatible with existing geospatial tools (GDAL, rasterio, pyproj) -- Based on the proven STAC Projection Extension model +**Repository**: https://github.com/EOPF/eopf-explorer/data-model +**Specification Path**: `attributes/geo/proj/` -## Inheritance Model +## Quick Reference -The `proj` key under the `geo` dictionary follows a simple group-to-array inheritance model that should be understood first: +- **Recommended usage**: Store CRS-encoding JSON object under the `proj` key in the `geo` dictionary of group `attributes` +- **Inheritance**: Group-level definitions apply to direct child arrays; array-level definitions override group-level +- **Key fields**: `version`, `code`, `wkt2`, `projjson`, `bbox`, `transform`, `spatial_dimensions` -### Inheritance Rules +## License -1. **Group-level definition** (recommended): When `proj` is defined under the `geo` dictionary at the group level, it applies to all arrays that are direct children of that group. It does not apply to groups or arrays deeper in the hierarchy (e.g., grandchildren). -2. **Array-level override**: An array can completely override the group's `proj` key with its own definition. -3. **Complete replacement only**: Partial inheritance (overriding only some fields while inheriting others) is not allowed. - -Most use cases will use group-level definitions without array overrides. - -## Specification - -The `proj` key under the `geo` dictionary can be added to Zarr arrays or groups to define projection information. - - -**`geo -> proj` Properties** - -| |Type|Description|Required| -|---|---|---|---| -|**version**|`string`|Projection metadata version| ✓ Yes| -|**code**|`["string", "null"]`|Authority:code identifier (e.g., EPSG:4326)|No| -|**wkt2**|`["string", "null"]`|WKT2 (ISO 19162) CRS representation|No| -|**projjson**|`any`|PROJJSON CRS representation|No| -|**bbox**|`number` `[]`|Bounding box in CRS coordinates|No| -|**transform**|`number` `[]`|Affine transformation coefficients|No| -|**spatial_dimensions**|`string` `[2]`|Names of spatial dimensions [y_name, x_name]|No| - -### Field Details - -Additional properties are allowed. - -#### geo -> proj.version - -Projection metadata version - -* **Type**: `string` -* **Required**: ✓ Yes -* **Allowed values**: `0.1` - -#### geo -> proj.code - -Authority:code identifier (e.g., EPSG:4326) - -* **Type**: `["string", "null"]` -* **Required**: No -* **Pattern**: `^[A-Z]+:[0-9]+$` - -Projection codes are identified by a string. The [proj](https://proj.org/) library defines projections -using "authority:code", e.g., "EPSG:4326" or "IAU_2015:30100". Different projection authorities may define -different string formats. Examples of known projection authorities, where when can find well known codes that -clients are likely to support are listed in the following table. - -| Authority Name | URL | -| --------------------------------------- | ---------------------------------------------------------- | -| European Petroleum Survey Groups (EPSG) | or | -| International Astronomical Union (IAU) | | -| Open Geospatial Consortium (OGC) | | -| ESRI | | - -The `proj.code` field SHOULD be set to `null` in the following cases: - -- The data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. -- A CRS exists, but there is no valid EPSG code for it. In this case, the CRS should be provided in `proj.wkt2` and/or `proj.projjson`. - Clients can prefer to take either, although there may be discrepancies in how each might be interpreted. - -#### geo -> proj.wkt2 - -WKT2 (ISO 19162) CRS representation - -* **Type**: `["string", "null"]` -* **Required**: No - -A Coordinate Reference System (CRS) is the data reference system (sometimes called a 'projection') -used by the asset data. This value is a [WKT2](http://docs.opengeospatial.org/is/12-063r5/12-063r5.html) string. - -This field SHOULD be set to `null` in the following cases: - -- The asset data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. -- A CRS exists, but there is no valid WKT2 string for it. - -#### geo -> proj.projjson - -PROJJSON CRS representation - -* **Type**: `any` -* **Required**: No - -A Coordinate Reference System (CRS) is the data reference system (sometimes called a 'projection') -used by the asset data. This value is a [PROJJSON](https://proj.org/specifications/projjson.html) object, -see the [JSON Schema](https://proj.org/schemas/v0.5/projjson.schema.json) for details. - -This field SHOULD be set to `null` in the following cases: - -- The asset data does not have a CRS, such as in the case of non-rectified imagery with Ground Control Points. -- A CRS exists, but there is no valid PROJJSON for it. - -#### geo -> proj.bbox - -Bounding box in CRS coordinates - -* **Type**: `number` `[]` -* **Required**: No - -Bounding box of the assets represented by this Item in the asset data CRS. Specified as 4 coordinates -based on the CRS defined in the `proj.code`, `proj.projjson` or `proj.wkt2` fields. First two numbers are coordinates -of the lower left corner, followed by coordinates of upper right corner, , e.g., \[west, south, east, north], -\[xmin, ymin, xmax, ymax], \[left, down, right, up], or \[west, south, lowest, east, north, highest]. -The length of the array must be 2\*n where n is the number of dimensions. The array contains all axes of the southwesterly -most extent followed by all axes of the northeasterly most extent specified in Longitude/Latitude -based on [WGS 84](http://www.opengis.net/def/crs/OGC/1.3/CRS84). - -#### geo -> proj.transform - -Affine transformation coefficients - -* **Type**: `number` `[]` -* **Required**: No - -Linear mapping from pixel coordinate space (Pixel, Line) to projection coordinate space (Xp, Yp). It is -a `3x3` matrix stored as a flat array of 9 elements in row major order. Since the last row is always `0,0,1` it can be omitted, -in which case only 6 elements are recorded. This mapping can be obtained from -GDAL([`GetGeoTransform`](https://gdal.org/api/gdaldataset_cpp.html#_CPPv4N11GDALDataset15GetGeoTransformEPd), requires re-ordering) -or the Rasterio ([`Transform`](https://rasterio.readthedocs.io/en/stable/api/rasterio.io.html#rasterio.io.BufferedDatasetWriter.transform)). -To get it on the command line you can use the [Rasterio CLI](https://rasterio.readthedocs.io/en/latest/cli.html) with the -[info](https://rasterio.readthedocs.io/en/latest/cli.html#info) command: `$ rio info`. - -```txt - [Xp] [a0, a1, a2] [Pixel] - [Yp] = [a3, a4, a5] * [Line ] - [1 ] [0 , 0, 1] [1 ] -``` - -If the transform is defined in Item Properties, it is used as the default transform for all assets that don't have an overriding transform. - -Note that `GetGeoTransform` and `rasterio` use different formats for reporting transform information. Order expected in `proj.transform` is the same as reported by `rasterio`. When using GDAL method you need to re-order in the following way: - -```python -g = GetGeoTransform(...) -proj_transform = [g[1], g[2], g[0], - g[4], g[5], g[3], - 0, 0, 1] -``` - -#### geo -> proj.spatial_dimensions - -Names of spatial dimensions [y_name, x_name] - -* **Type**: `string` `[2]` -* **Required**: No - -See the [Spatial Dimension Identification](#spatial-dimension-identification) section below for details on how spatial dimensions are identified. - - - -Note: The shape of spatial dimensions is obtained directly from the Zarr array metadata once the spatial dimensions are identified. - -### Spatial Dimension Identification - -In this extension, "spatial dimensions" refers to the dimension names of 2D/3D arrays within this group to which the projection definition applies. This extension is designed for regular grids where dimensions directly correspond to spatial axes. - -The extension identifies these array dimensions through: - -1. **Explicit Declaration** (recommended): Use `spatial_dimensions` to specify dimension names -2. **Pattern-Based Detection** (fallback): Automatically detect spatial dimensions using patterns defined by this extension - -#### Explicit Declaration - -```json -{ - "geo": { - "proj": { - "spatial_dimensions": ["latitude", "longitude"] - } - } -} -``` - -#### Pattern-Based Detection - -If `spatial_dimensions` is not provided, implementations should scan `dimension_names` for these patterns defined by this extension (in order): - -- ["y", "x"] or ["Y", "X"] -- ["lat", "lon"] or ["latitude", "longitude"] -- ["northing", "easting"] -- ["row", "col"] or ["line", "sample"] - -The first matching pair determines the spatial dimensions. - -#### Group-Level geo -> proj with Array-Level dimension_names - -When `proj` is defined under the `geo` dictionary at the group level but `spatial_dimensions` is not explicitly provided, implementations must handle the fact that `dimension_names` are defined at the individual array level in Zarr v3. The following algorithm defines how to resolve spatial dimensions: - -**For Explicit Declaration (when `spatial_dimensions` is provided):** - -1. Use the specified `spatial_dimensions` directly -2. Validate that each data array in the group contains these dimension names -3. If any data array lacks the specified spatial dimensions, ignore that array for the purpose of applying `geo:proj` -4. If no data arrays contain the specified spatial dimensions, implementations MUST raise an error - -**For Pattern-Based Detection (when `spatial_dimensions` is not provided):** - -1. Scan all data arrays within the group -2. For each data array, examine its `dimension_names` for the defined patterns -3. Use the first matching pattern found across all data arrays -4. If no spatial dimension patterns are found in any data array, implementations MUST raise an error - -**Example of Valid Group-Level Configuration:** - -```json -{ - "zarr_format": 3, - "node_type": "group", - "attributes": { - "geo": { - "proj": { - "code": "EPSG:4326", - "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0] - } - } - } -} -``` - -With data arrays: - -- `temperature/`: `dimension_names: ["time", "lat", "lon"]` ✅ Contains ["lat", "lon"] -- `precipitation/`: `dimension_names: ["time", "lat", "lon"]` ✅ Same pattern -- `lat/`: `dimension_names: ["lat"]` ⚠️ Excluded (coordinate array) -- `lon/`: `dimension_names: ["lon"]` ⚠️ Excluded (coordinate array) - -**Note**: The ordering of spatial dimensions is flexible. Both `["lat", "lon"]` and `["lon", "lat"]` orderings are valid: - -- `temperature/`: `dimension_names: ["time", "lon", "lat"]` ✅ Also valid -- `precipitation/`: `dimension_names: ["time", "lon", "lat"]` ✅ Also valid - -### Validation Rules - -- **Shape Inference**: Once spatial dimensions are identified (either explicitly through `spatial_dimensions` or through pattern-based detection), their sizes are obtained from the Zarr array's shape metadata -- **Error Handling**: If spatial dimensions cannot be identified through either method, implementations MUST raise an error -- **Semantic Identity Requirement**: If more than one CRS representation (`code`, `wkt2`, `projjson`) is provided, they MUST be semantically identical (i.e., describe the same coordinate reference system). Implementations SHOULD validate this consistency and raise an error if the representations describe different CRS - -### Shape Reconciliation - -The shape of spatial dimensions is determined on a per-array basis: - -1. Identifying the spatial dimensions using either `spatial_dimensions` or pattern-based detection (as described above) -2. For each data array that the `geo:proj` applies to, looking up the spatial dimension names in that array's `dimension_names` -3. Using the corresponding sizes from that same array's `shape` attribute - -This approach avoids redundancy and ensures consistency by using each array's own metadata rather than duplicating shape information. - -## Examples - -### Example 1: Simple Web Mercator Raster (Group Level) - -```json -{ - "zarr_format": 3, - "node_type": "group", - "attributes": { - "geo": { - "proj": { - "code": "EPSG:3857", - "transform": [156543.03392804097, 0.0, -20037508.342789244, 0.0, -156543.03392804097, 20037508.342789244], - "bbox": [-20037508.342789244, -20037508.342789244, 20037508.342789244, 20037508.342789244] - } - } - } -} -``` - -### Example 2: Multi-band Satellite Image - -```json -{ - "zarr_format": 3, - "shape": [4, 2048, 2048], - "dimension_names": ["band", "y", "x"], - "attributes": { - "geo": { - "proj": { - "code": "EPSG:32633", - "spatial_dimensions": ["y", "x"], - "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0], - "bbox": [500000.0, 4900000.0, 561440.0, 4961440.0] - } - } - } -} -``` - -### Example 3: Geographic Coordinates with Transform - -```json -{ - "zarr_format": 3, - "shape": [1800, 3600], - "dimension_names": ["lat", "lon"], - "attributes": { - "geo": { - "proj": { - "code": "EPSG:4326", - "transform": [0.1, 0.0, -180.0, 0.0, -0.1, 90.0], - "bbox": [-180.0, -90.0, 180.0, 90.0] - } - } - } -} -``` - -### Example 4: WKT2 Representation - -```json -{ - "zarr_format": 3, - "shape": [1000, 1000], - "dimension_names": ["northing", "easting"], - "attributes": { - "geo": { - "proj": { - "wkt2": "PROJCRS[\"WGS 84 / UTM zone 33N\",BASEGEOGCRS[\"WGS 84\",DATUM[\"World Geodetic System 1984\",ELLIPSOID[\"WGS 84\",6378137,298.257223563,LENGTHUNIT[\"metre\",1]]],PRIMEM[\"Greenwich\",0,ANGLEUNIT[\"degree\",0.0174532925199433]]],CONVERSION[\"UTM zone 33N\",METHOD[\"Transverse Mercator\",ID[\"EPSG\",9807]],PARAMETER[\"Latitude of natural origin\",0,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Longitude of natural origin\",15,ANGLEUNIT[\"degree\",0.0174532925199433]],PARAMETER[\"Scale factor at natural origin\",0.9996,SCALEUNIT[\"unity\",1]],PARAMETER[\"False easting\",500000,LENGTHUNIT[\"metre\",1]],PARAMETER[\"False northing\",0,LENGTHUNIT[\"metre\",1]]],CS[Cartesian,2],AXIS[\"easting\",east,ORDER[1],LENGTHUNIT[\"metre\",1]],AXIS[\"northing\",north,ORDER[2],LENGTHUNIT[\"metre\",1]]]", - "transform": [30.0, 0.0, 500000.0, 0.0, -30.0, 5000000.0] - } - } - } -} -``` - -## Compatibility Notes - -- The `version` field allows tracking of changes and ensures compatibility with future updates -- The `code` field follows the "authority:code" format used by PROJ library -- The `wkt2` field should conform to OGC WKT2 (ISO 19162) standard -- The `transform` field follows the same ordering as GDAL's GeoTransform and STAC's projection extension - -## References - -- [STAC Projection Extension v2.0.0](https://github.com/stac-extensions/projection) -- [PROJJSON Specification](https://proj.org/specifications/projjson.html) -- [OGC WKT2 Standard](https://www.ogc.org/standards/wkt-crs) +This attribute extension is licensed under the [MIT License](https://opensource.org/licenses/MIT). \ No newline at end of file diff --git a/attributes/geo/proj/schema.json b/attributes/geo/proj/schema.json deleted file mode 100644 index 90d0de5..0000000 --- a/attributes/geo/proj/schema.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://zarr-specs.readthedocs.io/en/latest/extensions/attributes/projection/v0.1.0/schema.json", - "title": "Zarr Geo Projection Attribute Extension", - "description": "Geo Projection attribute extension for Zarr arrays and groups", - "type": "object", - "definitions": { - "projectionMetadataV0_1": { - "type": "object", - "properties": { - "code": { - "type": ["string", "null"], - "description": "Authority:code identifier (e.g., EPSG:4326)", - "pattern": "^[A-Z]+:[0-9]+$" - }, - "wkt2": { - "type": ["string", "null"], - "description": "WKT2 (ISO 19162) CRS representation" - }, - "projjson": { - "oneOf": [ - { - "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" - }, - { - "type": "null" - } - ], - "description": "PROJJSON CRS representation" - }, - "bbox": { - "type": "array", - "oneOf": [ - { - "minItems": 4, - "maxItems": 4 - }, - { - "minItems": 6, - "maxItems": 6 - } - ], - "items": { - "type": "number" - }, - "description": "Bounding box in CRS coordinates" - }, - "transform": { - "type": "array", - "oneOf": [ - { - "minItems": 6, - "maxItems": 6 - }, - { - "minItems": 9, - "maxItems": 9 - } - ], - "items": { - "type": "number" - }, - "description": "Affine transformation coefficients" - }, - "spatial_dimensions": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": { - "type": "string" - }, - "description": "Names of spatial dimensions [y_name, x_name]" - } - }, - "oneOf": [ - { - "required": ["code"] - }, - { - "required": ["wkt2"] - }, - { - "required": ["projjson"] - } - ] - } - }, - "properties": { - "attributes": { - "type": "object", - "properties": { - "geo": { - "type": "object", - "description": "Geo extension namespace", - "properties": { - "proj": { - "anyOf": [ - { - "type": "object", - "properties": { - "version": { - "type": "string", - "description": "Projection attribute extension version", - "enum": ["0.1"] - }, - "code": { - "type": ["string", "null"], - "description": "Authority:code identifier (e.g., EPSG:4326)", - "pattern": "^[A-Z]+:[0-9]+$" - }, - "wkt2": { - "type": ["string", "null"], - "description": "WKT2 (ISO 19162) CRS representation" - }, - "projjson": { - "oneOf": [ - { - "$ref": "https://proj.org/schemas/v0.7/projjson.schema.json" - }, - { - "type": "null" - } - ], - "description": "PROJJSON CRS representation" - }, - "bbox": { - "type": "array", - "minItems": 4, - "maxItems": 4, - "items": { - "type": "number" - }, - "description": "Bounding box in CRS coordinates" - }, - "transform": { - "type": "array", - "oneOf": [ - { - "minItems": 6, - "maxItems": 6 - }, - { - "minItems": 9, - "maxItems": 9 - } - ], - "items": { - "type": "number" - }, - "description": "Affine transformation coefficients" - }, - "spatial_dimensions": { - "type": "array", - "minItems": 2, - "maxItems": 2, - "items": { - "type": "string" - }, - "description": "Names of spatial dimensions [y_name, x_name]" - } - }, - "required": ["version"] - } - ] - } - } - } - } - } - } -} From 5132326ae74490c301bfe9c72458e1e69615d444 Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Sun, 28 Sep 2025 15:08:02 +0200 Subject: [PATCH 36/38] Remove link to Attributes section in README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index b06db3c..92d599a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ This repository is the normative source of registered names for the Zarr v3 spec To register an extension, open a new PR with a new extension directory under the relevant extension point: - * [Attributes](./attributes/README.md) * [Codecs](./codecs/README.md) * [Data Types](./data-types/README.md) * [Chunk Key Encoding](./chunk-key-encodings/README.md) From b13da42ce5bf3034079b5077804205ba6534903d Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Mon, 29 Sep 2025 13:29:16 +0200 Subject: [PATCH 37/38] Update attributes/geo/proj/README.md Co-authored-by: Josh Moore --- attributes/geo/proj/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/geo/proj/README.md b/attributes/geo/proj/README.md index daf4e6f..96fccbd 100644 --- a/attributes/geo/proj/README.md +++ b/attributes/geo/proj/README.md @@ -12,7 +12,7 @@ This specification defines a JSON object that encodes datum and coordinate refer ## External Specification -The complete specification, including detailed documentation, JSON schema, examples, and implementation notes, is maintained in the [EOPF Data Model repository](https://github.com/EOPF/eopf-explorer/tree/main/data-model/attributes/geo/proj). +The complete specification, including detailed documentation, JSON schema, examples, and implementation notes, is maintained in the [EOPF Data Model repository](https://github.com/EOPF-Explorer/data-model/tree/main/attributes/geo/proj). **Repository**: https://github.com/EOPF/eopf-explorer/data-model **Specification Path**: `attributes/geo/proj/` From f9ffc2ff5da5b1ca7ee6fcca7b485905e7e8061a Mon Sep 17 00:00:00 2001 From: Emmanuel Mathot Date: Mon, 29 Sep 2025 13:29:24 +0200 Subject: [PATCH 38/38] Update attributes/geo/proj/README.md Co-authored-by: Josh Moore --- attributes/geo/proj/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attributes/geo/proj/README.md b/attributes/geo/proj/README.md index 96fccbd..bacaee9 100644 --- a/attributes/geo/proj/README.md +++ b/attributes/geo/proj/README.md @@ -14,7 +14,7 @@ This specification defines a JSON object that encodes datum and coordinate refer The complete specification, including detailed documentation, JSON schema, examples, and implementation notes, is maintained in the [EOPF Data Model repository](https://github.com/EOPF-Explorer/data-model/tree/main/attributes/geo/proj). -**Repository**: https://github.com/EOPF/eopf-explorer/data-model +**Repository**: https://github.com/EOPF-Explorer/data-model **Specification Path**: `attributes/geo/proj/` ## Quick Reference