From b9555c81f96ba6a03940a4e2687c83d6d5d90a2e Mon Sep 17 00:00:00 2001 From: = Date: Tue, 18 Mar 2025 11:02:44 +0100 Subject: [PATCH 01/68] added missing Classification parameter title --- pystac/extensions/classification.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pystac/extensions/classification.py b/pystac/extensions/classification.py index b7c23e3d3..8af00e982 100644 --- a/pystac/extensions/classification.py +++ b/pystac/extensions/classification.py @@ -60,6 +60,7 @@ def apply( value: int, description: str | None = None, name: str | None = None, + title: str | None = None, color_hint: str | None = None, nodata: bool | None = None, percentage: float | None = None, @@ -74,6 +75,7 @@ def apply( name: Short name of the class for machine readability. Must consist only of letters, numbers, -, and _ characters. Required as of v2.0 of this extension. + title: Human-readable name for use in, e.g., a map legend color_hint: An optional hexadecimal string-encoded representation of the RGB color that is suggested to represent this class (six hexadecimal characters, all capitalized) @@ -90,6 +92,7 @@ def apply( "As of v2.0.0 of the classification extension, 'name' is required" ) self.name = name + self.title = title self.description = description self.color_hint = color_hint self.nodata = nodata @@ -108,6 +111,7 @@ def create( value: int, description: str | None = None, name: str | None = None, + title: str | None = None, color_hint: str | None = None, nodata: bool | None = None, percentage: float | None = None, @@ -122,6 +126,7 @@ def create( name: Short name of the class for machine readability. Must consist only of letters, numbers, -, and _ characters. Required as of v2.0 of this extension. + title: Human-readable name for use in, e.g., a map legend color_hint: An optional hexadecimal string-encoded representation of the RGB color that is suggested to represent this class (six hexadecimal characters, all capitalized) @@ -134,6 +139,7 @@ def create( c.apply( value=value, name=name, + title=title, description=description, color_hint=color_hint, nodata=nodata, @@ -189,6 +195,17 @@ def name(self, v: str) -> None: ) self.properties["name"] = v + @property + def title(self) -> str | None: + return self.properties.get("title") + + @title.setter + def title(self, v: str) -> None: + if v is not None: + self.properties["title"] = v + else: + self.properties.pop("title", None) + @property def color_hint(self) -> str | None: """Get or set the optional color hint for this class. From f1c10db1da2075864d78adfac2ffa0257d6b3817 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 18 Mar 2025 11:17:00 +0100 Subject: [PATCH 02/68] added changes to changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb4e676a2..03c8f49c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,9 @@ - `Collection.from_items` for creating a `pystac.Collection` from an `ItemCollection` ([#1522](https://github.com/stac-utils/pystac/pull/1522)) +### Fixed +- fixed missing parameter "title" in pystac.extensions.classification.Classification ([#1539](https://github.com/stac-utils/pystac/pull/1539)) + ## [v1.12.2] ### Fixed From f3e714198629b2158881ec4a7a43738fdf5d2e35 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 18 Mar 2025 11:31:40 +0100 Subject: [PATCH 03/68] added missing parameters to test_classification_object: title, nodata, percentage, count --- tests/extensions/test_classification.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/extensions/test_classification.py b/tests/extensions/test_classification.py index 1f0b83731..d74838d67 100644 --- a/tests/extensions/test_classification.py +++ b/tests/extensions/test_classification.py @@ -60,12 +60,23 @@ def test_stac_extensions(landsat_item: Item) -> None: def test_classification_object() -> None: c = Classification.create( - name="dummy", description="empty class", value=0, color_hint="FF00AB" + name="dummy", + description="empty class", + value=0, + title="dummy title", + color_hint="FF00AB", + nodata=True, + percentage=20.3, + count=2, ) assert c.name == "dummy" assert c.description == "empty class" assert c.color_hint == "FF00AB" assert c.value == 0 + assert c.title == "dummy title" + assert c.nodata is True + assert c.percentage == 20.3 + assert c.count == 2 assert Classification(c.to_dict()) == c with pytest.raises(NotImplementedError): From 1cb33e4e5acb96b4d7b3b678c15265495493dcf4 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 26 Mar 2025 16:40:51 +0100 Subject: [PATCH 04/68] added ml extension --- pystac/extensions/ext.py | 7 + pystac/extensions/mlm.py | 1346 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 1353 insertions(+) create mode 100644 pystac/extensions/mlm.py diff --git a/pystac/extensions/ext.py b/pystac/extensions/ext.py index 5cad55b85..1de582131 100644 --- a/pystac/extensions/ext.py +++ b/pystac/extensions/ext.py @@ -19,6 +19,7 @@ from pystac.extensions.grid import GridExtension from pystac.extensions.item_assets import ItemAssetsExtension from pystac.extensions.mgrs import MgrsExtension +from pystac.extensions.mlm import MLMExtension from pystac.extensions.pointcloud import PointcloudExtension from pystac.extensions.projection import ProjectionExtension from pystac.extensions.raster import RasterExtension @@ -48,6 +49,7 @@ "grid", "item_assets", "mgrs", + "mlm", "pc", "proj", "raster", @@ -71,6 +73,7 @@ GridExtension.name: GridExtension, ItemAssetsExtension.name: ItemAssetsExtension, MgrsExtension.name: MgrsExtension, + MLMExtension.name: MLMExtension, PointcloudExtension.name: PointcloudExtension, ProjectionExtension.name: ProjectionExtension, RasterExtension.name: RasterExtension, @@ -225,6 +228,10 @@ def grid(self) -> GridExtension: def mgrs(self) -> MgrsExtension: return MgrsExtension.ext(self.stac_object) + @property + def mlm(self) -> MLMExtension[Item]: + return MLMExtension.ext(self.stac_object) + @property def pc(self) -> PointcloudExtension[Item]: return PointcloudExtension.ext(self.stac_object) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py new file mode 100644 index 000000000..d7b4f1df3 --- /dev/null +++ b/pystac/extensions/mlm.py @@ -0,0 +1,1346 @@ +from __future__ import annotations + +from collections.abc import Iterable +from typing import Any, Generic, Literal, TypeVar, cast + +import pystac +from pystac.errors import STACError +from pystac.extensions.base import ( + ExtensionManagementMixin, + PropertiesExtension, +) +from pystac.extensions.classification import Classification +from pystac.extensions.raster import DataType +from pystac.utils import StringEnum, get_required + +T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) + +# todo: support multiple version? +SCHEMA_URI_PATTERN: str = "https://stac-extensions.github.io/mlm/v{version}/schema.json" +DEFAULT_VERSION: str = "1.4.0" +SUPPORTED_VERSIONS: list[str] = ["1.4.0"] + +PREFIX: str = "mlm:" + +# Model Band Object properties +NAME_MODEL_BAND_OBJECT_PROP = "name" +FORMAT_MODEL_BAND_OBJECT_PROP = "format" +EXPRESSION_MODEL_BAND_OBJECT_PROP = "expression" + +# Input Structure properties +SHAPE_INPUT_STRUCTURE_PROP = "shape" +DIM_ORDER_INPUT_STRUCTURE_PROP = "dim_order" +DATA_TYPE_INPUT_STRUCTURE_PROP = "data_type" + +# Field names: Model Input Object +NAME_INPUT_OBJECT_PROP: str = "name" +BANDS_INPUT_OBJECT_PROP: str = "bands" +INPUT_INPUT_OBJECT_PROP: str = "input" +DESCRIPTION_INPUT_OBJECT_PROP: str = "description" +VALUE_SCALING_INPUT_OBJECT_PROP: str = "value_scaling" +RESIZE_TYPE_INPUT_OBJECT_PROP: str = "resize_type" +PRE_PROCESSING_FUNCTION_INPUT_OBJECT_PROP: str = "pre_processing_function" + +# Output Structure properties +SHAPE_RESULT_STRUCTURE_PROP = "shape" +DIM_ORDER_RESULT_STRUCTURE_PROP = "dim_order" +DATA_TYPE_RESULT_STRUCTURE_PROP = "data_type" + +# ProcessingExpression fields +FORMAT_PROCESSING_EXPRESSION_PROP = "format" +EXPRESSION_PROCESSING_EXPRESSION_PROP = "expression" + +# ValueScaling fields +TYPE_VALUE_SCALING_PROP = "type" +MINIMUM_VALUE_SCALING_PROP = "minimum" +MAXIMUM_VALUE_SCALING_PROP = "maximum" +MEAN_VALUE_SCALING_PROP = "mean" +STDDEV_VALUE_SCALING_PROP = "stddev" +VALUE_VALUE_SCALING_PROP = "value" +FORMAT_VALUE_SCALING_PROP = "format" +EXPRESSION_VALUE_SCALING_PROP = "expression" + +# Output properties +NAME_RESULT_PROP = "name" +TASKS_RESULT_PROP = "tasks" +RESULT_RESULT_PROP = "result" +DESCRIPTION_RESULT_PROP = "description" +CLASSES_RESULT_PROP = "classification:classes" +POST_PROCESSING_FUNCTION_RESULT_PROP = "post_processing_function" + +# Field names +NAME_PROP: str = PREFIX + "name" +ARCHITECTURE_PROP: str = PREFIX + "architecture" +TASKS_PROP: str = PREFIX + "tasks" +FRAMEWORK_PROP: str = PREFIX + "framework" +FRAMEWORK_VERSION_PROP: str = PREFIX + "framework_version" +MEMORY_SIZE_PROP: str = PREFIX + "memory_size" +TOTAL_PARAMETERS_PROP: str = PREFIX + "total_parameters" +PRETRAINED_PROP: str = PREFIX + "pretrained" +PRETRAINED_SOURCE_PROP: str = PREFIX + "pretrained_source" +BATCH_SIZE_SUGGESTION_PROP: str = PREFIX + "batch_size_suggestion" +ACCELERATOR_PROP: str = PREFIX + "accelerator" +ACCELERATOR_CONSTRAINED_PROP: str = PREFIX + "accelerator_constrained" +ACCELERATOR_SUMMARY_PROP: str = PREFIX + "accelerator_summary" +ACCELERATOR_COUNT_PROP: str = PREFIX + "accelerator_count" +INPUT_PROP: str = PREFIX + "input" +OUTPUT_PROP: str = PREFIX + "output" +HYPERPARAMETERS_PROP: str = PREFIX + "hyperparameters" + + +class TaskType(StringEnum): + REGRESSION = ("regression",) + CLASSIFICATION = ("classification",) + SCENE_CLASSIFICATION = ("scene-classification",) + DETECTION = ("detection",) + OBJECT_DETECTION = ("object-detection",) + SEGMENTATION = ("segmentation",) + SEMANTIC_SEGMENTATION = ("semantic-segmentation",) + INSTANCE_SEGMENTATION = ("instance-segmentation",) + PANOPTIC_SEGMENTATION = ("panoptic-segmentation",) + SIMILARITy_SEARCH = ("similarity-search",) + GENERATIVE = ("generative",) + IAMGE_CAPTIONING = ("image-captioning",) + SUPER_RESOLUTION = "super-resolution" + + +class AcceleratorType(StringEnum): + AMD64 = ("amd64",) + CUDA = ("cuda",) + XLA = ("xla",) + AMD_ROCM = ("amd-rocm",) + INTEL_IPEX_CPU = ("intel-ipex-cpu",) + INTEL_IPEX_GPU = ("intel-ipex-gpu",) + MACOS_ARM = "macos-arm" + + +class ResizeType(StringEnum): + CROP = "crop" + PAD = "pad" + INTERPOLATION_NEAREST = "interpolate-nearest" + INTERPOLATION_LINEAR = "interpolate-linear" + INTERPOLATION_CUBIC = "interpolation-cubic" + INTERPOLATION_AREA = "interpolation-area" + INTERPOLATION_LANCZOS4 = "interpolation-lanczos4" + INTERPOLATION_MAX = "interpolation-max" + WRAP_FILL_OUTLIERS = "wrap-fill-outliers" + WRAP_INVERSE_MAP = "wrap-inverse-map" + + +class ValueScalingType(StringEnum): + MIN_MAX = "min-max" + Z_SCORE = "z-score" + CLIP = "clip" + CLIP_MIN = "clip-min" + CLIP_MAX = "clip-max" + OFFSET = "offset" + SCALE = "scale" + PROCESSING = "processing" + + +class ModelBand: + properties: dict[str, Any] + + def __init__(self, properties: dict[str, Any]): + self.properties = properties + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ModelBand): + return NotImplemented + return ( + self.name == other.name + and self.format == other.format + and self.expression == other.expression + ) + + def apply( + self, name: str, format: str | None = None, expression: Any | None = None + ) -> None: + """ + Set the properties for a new ModelBand. + Args: + name: Name of the band referring to an extended band definition + format: The type of expression that is specified in the expression property + expression: An expression compliant with the format specified. + The cxpression can be applied to any data type and depends on the format + given + """ + self.name = name + self.format = format + self.expression = expression + + @classmethod + def create( + cls, name: str, format: str | None = None, expression: Any | None = None + ) -> ModelBand: + """ + Create a new ModelBand. + Args: + name: Name of the band referring to an extended band definition + format: The type of expression that is specified in the expression property + expression: An expression compliant with the format specified. + The cxpression can be applied to any data type and depends on the + format given + """ + c = cls({}) + c.apply(name=name, format=format, expression=expression) + return c + + @property + def name(self) -> str: + return get_required( + self.properties.get(NAME_MODEL_BAND_OBJECT_PROP), + self, + NAME_MODEL_BAND_OBJECT_PROP, + ) + + @name.setter + def name(self, v: str) -> None: + self.properties[NAME_MODEL_BAND_OBJECT_PROP] = v + + @property + def format(self) -> str | None: + return self.properties.get(FORMAT_MODEL_BAND_OBJECT_PROP) + + @format.setter + def format(self, v: str | None) -> None: + if v is not None: + self.properties[FORMAT_MODEL_BAND_OBJECT_PROP] = v + else: + self.properties.pop(FORMAT_MODEL_BAND_OBJECT_PROP, None) + + @property + def expression(self) -> Any: + return self.properties.get(EXPRESSION_MODEL_BAND_OBJECT_PROP) + + @expression.setter + def expression(self, v: Any) -> None: + if v is not None: + self.properties[EXPRESSION_MODEL_BAND_OBJECT_PROP] = v + else: + self.properties.pop(EXPRESSION_MODEL_BAND_OBJECT_PROP, None) + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class ProcessingExpression: + properties: dict[str, Any] + + def __init__(self, properties: dict[str, Any]): + self.properties = properties + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ProcessingExpression): + return NotImplemented + else: + return self.format == other.format and self.expression == other.expression + + def apply(self, format: str, expression: Any) -> None: + """ + Set the properties for a new ProcessingExpression + Args: + format: The type of the expression that is specified in the expression + property. + expression: An expression compliant with the format specified. The + expression can be any data type and depends on the format given, + e.g. string or object. + """ + self.format = format + self.expression = expression + + @classmethod + def create(cls, format: str, expression: Any) -> ProcessingExpression: + c = cls({}) + """ + Creates a new ProcessingExpression + Args: + format: The type of the expression that is specified in the expression + property. + expression: An expression compliant with the format specified. The + expression can be any data type and depends on the format given, + e.g. string or object. + """ + c.apply(format=format, expression=expression) + return c + + @property + def format(self) -> str: + return get_required( + self.properties.get(FORMAT_PROCESSING_EXPRESSION_PROP), + self, + FORMAT_PROCESSING_EXPRESSION_PROP, + ) + + @format.setter + def format(self, v: str) -> None: + self.properties[FORMAT_PROCESSING_EXPRESSION_PROP] = v + + @property + def expression(self) -> Any: + return get_required( + self.properties.get(EXPRESSION_PROCESSING_EXPRESSION_PROP), + self, + EXPRESSION_PROCESSING_EXPRESSION_PROP, + ) + + @expression.setter + def expression(self, v: Any) -> None: + self.properties[EXPRESSION_PROCESSING_EXPRESSION_PROP] = v + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class ValueScaling: + properties: dict[str, Any] + + def __init__(self, properties: dict[str, Any]): + self.properties = properties + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ValueScaling): + return NotImplemented + return ( + self.type == other.type + and self.minimum == other.minimum + and self.maximum == other.maximum + and self.mean == other.mean + and self.stddev == other.stddev + and self.value == other.value + and self.format == other.format + and self.expression == other.expression + ) + + def apply( + self, + type: ValueScalingType, + minimum: int | float | None = None, + maximum: int | float | None = None, + mean: int | float | None = None, + stddev: int | float | None = None, + value: int | float | None = None, + format: str | None = None, + expression: str | None = None, + ) -> None: + self.validate_property_dict(type, locals()) + + self.type = type + self.minimum = minimum + self.maximum = maximum + self.mean = mean + self.stddev = stddev + self.value = value + self.format = format + self.expression = expression + + @classmethod + def create( + cls, + type: ValueScalingType, + minimum: int | float | None = None, + maximum: int | float | None = None, + mean: int | float | None = None, + stddev: int | float | None = None, + value: int | float | None = None, + format: str | None = None, + expression: str | None = None, + ) -> ValueScaling: + c = cls({}) + c.apply( + type=type, + minimum=minimum, + maximum=maximum, + mean=mean, + stddev=stddev, + value=value, + format=format, + expression=expression, + ) + return c + + @classmethod + def get_required_props(cls, type: ValueScalingType) -> list[str]: + d: dict[str, list[str]] = { + "min-max": ["minimum", "maximum"], + "z-score": ["mean", "stddev"], + "clip": ["minimum", "maximum"], + "clip-min": ["minimum"], + "clip-max": ["maximum"], + "offset": ["value"], + "scale": ["value"], + "processing": ["format", "expression"], + } + return d[type] + + @classmethod + def validate_property_dict( + cls, type: ValueScalingType, props: dict[str, Any] + ) -> None: + required_props = cls.get_required_props(type) + given_props = [ + prop_name + for prop_name, prop_value in props.items() + if prop_value is not None + ] + given_props_cleaned = [ + prop for prop in given_props if prop != "self" and prop != "type" + ] + + valid = all( + [required_prop in given_props_cleaned for required_prop in required_props] + ) + + if not valid: + raise STACError( + f"ValueScaling object of {type=} " + f"requires properties: {required_props}. Given: {given_props_cleaned}" + ) + + @property + def type(self) -> str: + return get_required( + self.properties.get(TYPE_VALUE_SCALING_PROP), self, TYPE_VALUE_SCALING_PROP + ) + + @type.setter + def type(self, v: str) -> None: + self.properties[TYPE_VALUE_SCALING_PROP] = v + + @property + def minimum(self) -> int | float | None: + return self.properties.get(MINIMUM_VALUE_SCALING_PROP) + + @minimum.setter + def minimum(self, v: int | float | None) -> None: + if v is not None: + self.properties[MINIMUM_VALUE_SCALING_PROP] = v + else: + self.properties.pop(MINIMUM_VALUE_SCALING_PROP, None) + + @property + def maximum(self) -> int | float | None: + return self.properties.get(MAXIMUM_VALUE_SCALING_PROP) + + @maximum.setter + def maximum(self, v: int | float | None) -> None: + if v is not None: + self.properties[MAXIMUM_VALUE_SCALING_PROP] = v + else: + self.properties.get(MAXIMUM_VALUE_SCALING_PROP, None) + + @property + def mean(self) -> int | float | None: + return self.properties.get(MEAN_VALUE_SCALING_PROP) + + @mean.setter + def mean(self, v: int | float | None) -> None: + if v is not None: + self.properties[MEAN_VALUE_SCALING_PROP] = v + else: + self.properties.pop(MEAN_VALUE_SCALING_PROP, None) + + @property + def stddev(self) -> int | float | None: + return self.properties.get(STDDEV_VALUE_SCALING_PROP) + + @stddev.setter + def stddev(self, v: int | float | None) -> None: + if v is not None: + self.properties[STDDEV_VALUE_SCALING_PROP] = v + else: + self.properties.pop(STDDEV_VALUE_SCALING_PROP, None) + + @property + def value(self) -> int | float | None: + return self.properties.get(VALUE_VALUE_SCALING_PROP) + + @value.setter + def value(self, v: int | float | None) -> None: + if v is not None: + self.properties[VALUE_VALUE_SCALING_PROP] = v + else: + self.properties.pop(VALUE_VALUE_SCALING_PROP, None) + + @property + def format(self) -> str | None: + return self.properties.get(FORMAT_VALUE_SCALING_PROP) + + @format.setter + def format(self, v: str | None) -> None: + if v is not None: + self.properties[FORMAT_VALUE_SCALING_PROP] = v + else: + self.properties.pop(FORMAT_VALUE_SCALING_PROP, None) + + @property + def expression(self) -> str | None: + return self.properties.get(EXPRESSION_VALUE_SCALING_PROP) + + @expression.setter + def expression(self, v: str | None) -> None: + if v is not None: + self.properties[EXPRESSION_VALUE_SCALING_PROP] = v + else: + self.properties.pop(EXPRESSION_VALUE_SCALING_PROP, None) + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class InputStructure: + properties: dict[str, Any] + + def __init__(self, properties: dict[str, Any]): + self.properties = properties + + def __eq__(self, other: object) -> bool: + if not isinstance(other, InputStructure): + return NotImplemented + return ( + self.shape == other.shape + and self.dim_order == other.dim_order + and self.data_type == other.data_type + ) + + def apply( + self, + shape: list[int], + dim_order: list[str], # todo: make Dimension Enum? + data_type: DataType, + ) -> None: + """ + Set the properties for a new InputStructure. + Args: + shape: Shape of the input n-dimensional array (e.g.: B×C×H×W), including + the batch size dimension. + Each dimension must either be greater than 0 or -1 to indicate a + variable dimension size. + dim_order: Order of the shape dimensions by name. + data_type:The data type of values in the n-dimensional array. For model + inputs, this should be the data type of the processed input supplied to + the model inference function, not the data type of the source bands. + """ + self.shape = shape + self.dim_order = dim_order + self.data_type = data_type + + @classmethod + def create( + cls, shape: list[int], dim_order: list[str], data_type: DataType + ) -> InputStructure: + """ + Create a new InputStructure. + Args: + shape: Shape of the input n-dimensional array (e.g.: B×C×H×W), including the + batch size dimension. Each dimension must either be greater than 0 or + -1 to indicate a variable dimension size. + dim_order: Order of the shape dimensions by name. + dim_order: Order of the shape dimensions by name. + data_type: The data type of values in the n-dimensional array. For model + inputs, this should be the data type of the processed input supplied to + the model inference function, not the data type of the source bands. + """ + c = cls({}) + c.apply(shape=shape, dim_order=dim_order, data_type=data_type) + return c + + @property + def shape(self) -> list[int]: + return get_required( + self.properties.get(SHAPE_INPUT_STRUCTURE_PROP), + self, + SHAPE_INPUT_STRUCTURE_PROP, + ) + + @shape.setter + def shape(self, v: list[int]) -> None: + self.properties[SHAPE_INPUT_STRUCTURE_PROP] = v + + @property + def dim_order(self) -> list[str]: + return get_required( + self.properties.get(DIM_ORDER_INPUT_STRUCTURE_PROP), + self, + DIM_ORDER_INPUT_STRUCTURE_PROP, + ) + + @dim_order.setter + def dim_order(self, v: list[str]) -> None: + self.properties[DIM_ORDER_INPUT_STRUCTURE_PROP] = v + + @property + def data_type(self) -> DataType: + return get_required( + self.properties.get(DATA_TYPE_INPUT_STRUCTURE_PROP), + self, + DATA_TYPE_INPUT_STRUCTURE_PROP, + ) + + @data_type.setter + def data_type(self, v: DataType) -> None: + self.properties[DATA_TYPE_INPUT_STRUCTURE_PROP] = v + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class ModelInput: + properties: dict[str, Any] + + def __init__(self, properties: dict[str, Any]): + self.properties = properties + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ModelInput): + return NotImplemented + return ( + self.name == other.name + and self.bands == other.bands + and self.input == other.input + and self.description == other.description + and self.value_scaling == other.value_scaling + and self.resize_type == other.resize_type + and self.pre_processing_function == other.pre_processing_function + ) + + def apply( + self, + name: str, + bands: list[ModelBand] | list[str], + input: InputStructure, + description: str | None = None, + value_scaling: ValueScaling | None = None, + resize_type: ResizeType | None = None, + pre_processing_function: ProcessingExpression | None = None, + ) -> None: + """ + Sets the Properties for a new Input + Args: + name: Name of the input variable defined by the model. If no explicit name + is defined by the model, an informative name (e.g.: "RGB Time Series") + can be used instead. + bands: The raster band references used to train, fine-tune or perform + inference with the model, which may be all or a subset of bands + available in a STAC Item's Band Object. If no band applies for one + input, use an empty array. + input: The N-dimensional array definition that describes the shape, + dimension ordering, and data type. + description: Additional details about the input such as describing its + purpose or expected source that cannot be represented by other + properties. + value_scaling:Method to scale, normalize, or standardize the data inputs + values, across dimensions, per corresponding dimension index, or null + if none applies. These values often correspond to dataset or sensor + statistics employed for training the model, but can come from another + source as needed by the model definition. Consider using + pre_processing_function for custom implementations or more complex + combinations. + resize_type: High-level descriptor of the resize method to modify the input + dimensions shape. Select an appropriate option or null when none + applies. Consider using pre_processing_function for custom + implementations or more complex combinations. + pre_processing_function: Custom preprocessing function where rescaling and + resize, and any other significant data preparation operations takes + place. The pre_processing_function should be applied over all available + bands. + """ + self.name = name + self.bands = bands + self.input = input + self.description = description + self.value_scaling = value_scaling + self.resize_type = resize_type + self.pre_processing_function = pre_processing_function + + @classmethod + def create( + cls, + name: str, + bands: list[ModelBand] | list[str], + input: InputStructure, + description: str | None = None, + value_scaling: ValueScaling | None = None, + resize_type: ResizeType | None = None, + pre_processing_function: ProcessingExpression | None = None, + ) -> ModelInput: + """ + Creates a new Input + Args: + name: Name of the input variable defined by the model. If no explicit name + is defined by the model, an informative name (e.g.: "RGB Time Series") + can be used instead. + bands: The raster band references used to train, fine-tune or perform + inference with the model, which may be all or a subset of bands + available in a STAC Item's Band Object. If no band applies for one + input, use an empty array. + input: The N-dimensional array definition that describes the shape, + dimension ordering, and data type. + description: Additional details about the input such as describing its + purpose or expected source that cannot be represented by other + properties. + value_scaling:Method to scale, normalize, or standardize the data inputs + values, across dimensions, per corresponding dimension index, or null + if none applies. These values often correspond to dataset or sensor + statistics employed for training the model, but can come from another + source as needed by the model definition. Consider using + pre_processing_function for custom implementations or more complex + combinations. + resize_type: High-level descriptor of the resize method to modify the input + dimensions shape. Select an appropriate option or null when none + applies. Consider using pre_processing_function for custom + implementations or more complex combinations. + pre_processing_function: Custom preprocessing function where rescaling and + resize, and any other significant data preparation operations takes + place. The pre_processing_function should be applied over all available + bands. + """ + c = cls({}) + c.apply( + name=name, + bands=bands, + input=input, + description=description, + value_scaling=value_scaling, + resize_type=resize_type, + pre_processing_function=pre_processing_function, + ) + return c + + @property + def name(self) -> str: + return get_required( + self.properties.get(NAME_INPUT_OBJECT_PROP), self, NAME_INPUT_OBJECT_PROP + ) + + @name.setter + def name(self, v: str) -> None: + self.properties[NAME_INPUT_OBJECT_PROP] = v + + @property + def bands(self) -> list[ModelBand] | list[str]: + bands: list[str] | list[dict[str, Any]] = get_required( + self.properties.get(BANDS_INPUT_OBJECT_PROP), self, BANDS_INPUT_OBJECT_PROP + ) + + if isinstance(bands, list) and all(isinstance(item, str) for item in bands): + return [band for band in bands if isinstance(band, str)] + + elif isinstance(bands, list) and all(isinstance(item, dict) for item in bands): + return [ModelBand(band) for band in bands if isinstance(band, dict)] + + raise STACError("Invalid bands property. Must list[str] or list[ModelBand]") + + @bands.setter + def bands(self, v: list[ModelBand] | list[str]) -> None: + v_trans = [c.to_dict() if isinstance(c, ModelBand) else c for c in v] + self.properties[BANDS_INPUT_OBJECT_PROP] = v_trans + + @property + def input(self) -> InputStructure: + return InputStructure( + get_required( + self.properties.get(INPUT_INPUT_OBJECT_PROP), + self, + INPUT_INPUT_OBJECT_PROP, + ) + ) + + @input.setter + def input(self, v: InputStructure) -> None: + self.properties[INPUT_INPUT_OBJECT_PROP] = v.to_dict() + + @property + def description(self) -> str | None: + return self.properties.get(DESCRIPTION_INPUT_OBJECT_PROP) + + @description.setter + def description(self, v: str | None) -> None: + if v is not None: + self.properties[DESCRIPTION_INPUT_OBJECT_PROP] = v + else: + self.properties.pop(DESCRIPTION_INPUT_OBJECT_PROP, None) + + @property + def value_scaling(self) -> ValueScaling | None: + v = self.properties.get(VALUE_SCALING_INPUT_OBJECT_PROP) + return ValueScaling(v) if v is not None else v + + @value_scaling.setter + def value_scaling(self, v: ValueScaling | None) -> None: + # add None to properties dict and do not pop it, according to specification + self.properties[VALUE_SCALING_INPUT_OBJECT_PROP] = ( + None if v is None else v.to_dict() + ) + + @property + def resize_type(self) -> ResizeType | None: + return self.properties.get(RESIZE_TYPE_INPUT_OBJECT_PROP) + + @resize_type.setter + def resize_type(self, v: ResizeType | None) -> None: + # add to dict even if v is None and do not pop it, according to specification + self.properties[RESIZE_TYPE_INPUT_OBJECT_PROP] = v + + @property + def pre_processing_function(self) -> ProcessingExpression | None: + v = self.properties.get(PRE_PROCESSING_FUNCTION_INPUT_OBJECT_PROP) + return ProcessingExpression(v) if v is not None else None + + @pre_processing_function.setter + def pre_processing_function(self, v: ProcessingExpression | None) -> None: + # add to dict even if v is None and do not pop it, according to specification + self.properties[PRE_PROCESSING_FUNCTION_INPUT_OBJECT_PROP] = ( + None if v is None else v.to_dict() + ) + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class ResultStructure: + properties: dict[str, Any] + + def __init__(self, properties: dict[str, Any]): + self.properties = properties + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ResultStructure): + return NotImplemented + return ( + self.shape == other.shape + and self.dim_order == other.dim_order + and self.data_type == other.data_type + ) + + def apply( + self, shape: list[int], dim_order: list[str], data_type: DataType + ) -> None: + """ + Set the properties for a new ResultStructure. + Args: + shape: Shape of the n-dimensional result array (e.g.: B×H×W or B×C), + possibly including a batch size dimension. The dimensions must either + be greater than 0 or -1 to indicate a variable size. + dim_order: Order of the shape dimensions by name for the result array. + data_type: The data type of values in the n-dimensional array. For model + outputs, this should be the data type of the result of the model + inference without extra post processing. + """ + self.shape = shape + self.dim_order = dim_order + self.data_type = data_type + + @classmethod + def create( + cls, shape: list[int], dim_order: list[str], data_type: DataType + ) -> ResultStructure: + """ + Creates a new ResultStructure. + Args: + shape: Shape of the n-dimensional result array (e.g.: B×H×W or B×C), + possibly including a batch size dimension. The dimensions must either + be greater than 0 or -1 to indicate a variable size. + dim_order: Order of the shape dimensions by name for the result array. + data_type: The data type of values in the n-dimensional array. For model + outputs, this should be the data type of the result of the model + inference without extra post processing. + """ + c = cls({}) + c.apply(shape=shape, dim_order=dim_order, data_type=data_type) + return c + + @property + def shape(self) -> list[int]: + return get_required( + self.properties.get(SHAPE_RESULT_STRUCTURE_PROP), + self, + SHAPE_RESULT_STRUCTURE_PROP, + ) + + @shape.setter + def shape(self, v: list[int]) -> None: + self.properties[SHAPE_RESULT_STRUCTURE_PROP] = v + + @property + def dim_order(self) -> list[str]: + return get_required( + self.properties.get(DIM_ORDER_RESULT_STRUCTURE_PROP), + self, + DIM_ORDER_RESULT_STRUCTURE_PROP, + ) + + @dim_order.setter + def dim_order(self, v: list[str]) -> None: + self.properties[DIM_ORDER_RESULT_STRUCTURE_PROP] = v + + @property + def data_type(self) -> DataType: + return get_required( + self.properties.get(DATA_TYPE_RESULT_STRUCTURE_PROP), + self, + DIM_ORDER_RESULT_STRUCTURE_PROP, + ) + + @data_type.setter + def data_type(self, v: DataType) -> None: + self.properties[DATA_TYPE_RESULT_STRUCTURE_PROP] = v + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class ModelOutput: + properties: dict[str, Any] + + def __init__(self, properties: dict[str, Any]): + self.properties = properties + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ModelOutput): + return NotImplemented + return ( + self.name == other.name + and self.tasks == other.tasks + and self.result == other.result + and self.description == other.description + and self.classes == other.classes + and self.post_processing_function == other.post_processing_function + ) + + def apply( + self, + name: str, + tasks: list[TaskType], + result: ResultStructure, + description: str | None = None, + classes: list[Classification] | None = None, + post_processing_function: ProcessingExpression | None = None, + ) -> None: + """ + Sets the properties for a new Output + Args: + name:Name of the output variable defined by the model. If no explicit name + is defined by the model, an informative name (e.g.: "CLASSIFICATION") + can be used instead. + tasks: Specifies the Machine Learning tasks for which the output can be used + for. This can be a subset of mlm:tasks defined under the Item properties + as applicable. + result: The structure that describes the resulting output arrays/tensors + from one model head. + description: Additional details about the output such as describing its + purpose or expected result that cannot be represented by other + properties. + classes: A list of class objects adhering to the Classification Extension. + post_processing_function: Custom postprocessing function where + normalization, rescaling, or any other + significant operations takes place. + """ + self.name = name + self.tasks = tasks + self.result = result + self.description = description + self.classes = classes + self.post_processing_function = post_processing_function + + @classmethod + def create( + cls, + name: str, + tasks: list[TaskType], + result: ResultStructure, + description: str | None = None, + classes: list[Classification] | None = None, + post_processing_function: ProcessingExpression | None = None, + ) -> ModelOutput: + """ + Creates a new Output + Args: + name:Name of the output variable defined by the model. If no explicit name + is defined by the model, an informative name (e.g.: "CLASSIFICATION") + can be used instead. + tasks: Specifies the Machine Learning tasks for which the output can be used + for. This can be a subset of mlm:tasks defined under the Item properties + as applicable. + result: The structure that describes the resulting output arrays/tensors + from one model head. description: Additional details about the output such + as describing its purpose or expected result that cannot be represented + by other properties. + classes: A list of class objects adhering to the Classification Extension. + post_processing_function: Custom postprocessing function where + normalization, rescaling, or any other significant operations takes place. + """ + c = cls({}) + c.apply( + name=name, + tasks=tasks, + result=result, + description=description, + classes=classes, + post_processing_function=post_processing_function, + ) + return c + + @property + def name(self) -> str: + return get_required( + self.properties.get(NAME_RESULT_PROP), self, NAME_RESULT_PROP + ) + + @name.setter + def name(self, v: str) -> None: + self.properties[NAME_RESULT_PROP] = v + + @property + def tasks(self) -> list[TaskType]: + return get_required( + self.properties.get(TASKS_RESULT_PROP), self, TASKS_RESULT_PROP + ) + + @tasks.setter + def tasks(self, v: list[TaskType]) -> None: + self.properties[TASKS_RESULT_PROP] = v + + @property + def result(self) -> ResultStructure: + return ResultStructure( + get_required( + self.properties.get(RESULT_RESULT_PROP), self, RESULT_RESULT_PROP + ) + ) + + @result.setter + def result(self, v: ResultStructure) -> None: + self.properties[RESULT_RESULT_PROP] = v.to_dict() + + @property + def description(self) -> str | None: + return self.properties.get(DESCRIPTION_RESULT_PROP) + + @description.setter + def description(self, v: str | None) -> None: + if v is not None: + self.properties[DESCRIPTION_RESULT_PROP] = v + else: + self.properties.pop(DESCRIPTION_RESULT_PROP, None) + + @property + def classes(self) -> list[Classification] | None: + classes = self.properties.get(CLASSES_RESULT_PROP) + return [Classification(c) for c in classes] if classes is not None else None + + @classes.setter + def classes(self, v: list[Classification] | None) -> None: + if v is not None: + self.properties[CLASSES_RESULT_PROP] = [c.to_dict() for c in v] + else: + self.properties.pop(CLASSES_RESULT_PROP, None) + + @property + def post_processing_function(self) -> ProcessingExpression | None: + v = self.properties.get(POST_PROCESSING_FUNCTION_RESULT_PROP) + return ProcessingExpression(v) if v is not None else None + + @post_processing_function.setter + def post_processing_function(self, v: ProcessingExpression | None) -> None: + if v is not None: + self.properties[POST_PROCESSING_FUNCTION_RESULT_PROP] = v.to_dict() + else: + self.properties.pop(POST_PROCESSING_FUNCTION_RESULT_PROP, None) + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class Hyperparameters: + properties: dict[str, Any] + + def __init__(self, properties: dict[str, Any]): + self.properties = properties + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Hyperparameters): + return NotImplemented + return self.properties == other.properties + + def apply(self, **kwargs: Any) -> None: + self.properties.update(kwargs) + + @classmethod + def create(cls, **kwargs: Any) -> Hyperparameters: + c = cls({}) + c.apply(**kwargs) + return c + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class MLMExtension( + Generic[T], + PropertiesExtension, + ExtensionManagementMixin[pystac.Item | pystac.Collection], +): + """An abstract class that can be used to extend to properties of an + :class:`pystac.Item` or :class:`pystac.Collection`with properties from the + :stac-ext:`Machine Learning Model Extension `. This class is generic over the + type of STAC Object to be extended. + """ + + name: Literal["mlm"] = "mlm" + properties: dict[str, Any] + + def apply( + self, + name: str, + architecture: str, + tasks: list[TaskType], + input: list[ModelInput], + output: list[ModelOutput], + framework: str | None = None, + framework_version: str | None = None, + memory_size: int | None = None, + total_parameters: int | None = None, + pretrained: bool | None = None, + pretrained_source: str | None = None, + batch_size_suggestion: int | None = None, + accelerator: AcceleratorType | None = None, + accelerator_constrained: bool | None = None, + accelerator_summary: str | None = None, + accelerator_count: int | None = None, + hyperparameters: Hyperparameters | None = None, + ) -> None: + self.mlm_name = name + self.architecture = architecture + self.tasks = tasks + self.input = input + self.output = output + self.framework = framework + self.framework_version = framework_version + self.memory_size = memory_size + self.total_parameters = total_parameters + self.pretrained = pretrained + self.pretrained_source = pretrained_source + self.batch_size_suggestion = batch_size_suggestion + self.accelerator = accelerator + self.accelerator_constrained = accelerator_constrained + self.accelerator_summary = accelerator_summary + self.accelerator_count = accelerator_count + self.hyperparameters = hyperparameters + + @classmethod + def get_schema_uri(cls) -> str: + return SCHEMA_URI_PATTERN.format(version=DEFAULT_VERSION) + + @classmethod + def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: + if isinstance(obj, pystac.Item): + cls.ensure_has_extension(obj, add_if_missing) + return cast(MLMExtension[T], ItemMLMExtension(obj)) + elif isinstance(obj, pystac.Asset): + cls.ensure_owner_has_extension(obj, add_if_missing) + return cast(MLMExtension[T], AssetMLMExtension(obj)) + elif isinstance(obj, pystac.ItemAssetDefinition): + cls.ensure_owner_has_extension(obj, add_if_missing) + return cast(MLMExtension[T], ItemAssetClassificationExtension(obj)) + else: + raise pystac.ExtensionTypeError(cls._ext_error_message(obj)) + + @property + def mlm_name(self) -> str: + return get_required(self.properties.get(NAME_PROP), self, NAME_PROP) + + @mlm_name.setter + def mlm_name(self, v: str) -> None: + self._set_property(NAME_PROP, v) + + @property + def architecture(self) -> str: + return get_required( + self.properties.get(ARCHITECTURE_PROP), self, ARCHITECTURE_PROP + ) + + @architecture.setter + def architecture(self, v: str) -> None: + self._set_property(ARCHITECTURE_PROP, v) + + @property + def tasks(self) -> list[TaskType]: + return get_required(self.properties.get(TASKS_PROP), self, TASKS_PROP) + + @tasks.setter + def tasks(self, v: list[TaskType]) -> None: + self._set_property(TASKS_PROP, v) + + @property + def framework(self) -> str | None: + return self._get_property(FRAMEWORK_PROP, str) + + @framework.setter + def framework(self, v: str | None) -> None: + # todo: make enum? + self._set_property(FRAMEWORK_PROP, v) + + @property + def framework_version(self) -> str | None: + return self._get_property(FRAMEWORK_VERSION_PROP, str) + + @framework_version.setter + def framework_version(self, v: str | None) -> None: + self._set_property(FRAMEWORK_VERSION_PROP, v) + + @property + def memory_size(self) -> int | None: + return self._get_property(MEMORY_SIZE_PROP, int) + + @memory_size.setter + def memory_size(self, v: int | None) -> None: + self._set_property(MEMORY_SIZE_PROP, v) + + @property + def total_parameters(self) -> int | None: + return self._get_property(TOTAL_PARAMETERS_PROP, int) + + @total_parameters.setter + def total_parameters(self, v: int | None) -> None: + self._set_property(TOTAL_PARAMETERS_PROP, v) + + @property + def pretrained(self) -> bool | None: + return self._get_property(PRETRAINED_PROP, bool) + + @pretrained.setter + def pretrained(self, v: bool | None) -> None: + self._set_property(PRETRAINED_PROP, v) + + @property + def pretrained_source(self) -> str | None: + return self._get_property(PRETRAINED_SOURCE_PROP, str) + + @pretrained_source.setter + def pretrained_source(self, v: str | None) -> None: + self._set_property( + PRETRAINED_SOURCE_PROP, v, False + ) # dont pop as per documentation + + @property + def batch_size_suggestion(self) -> int | None: + return self._get_property(BATCH_SIZE_SUGGESTION_PROP, int) + + @batch_size_suggestion.setter + def batch_size_suggestion(self, v: int | None) -> None: + self._set_property(BATCH_SIZE_SUGGESTION_PROP, v) + + @property + def accelerator(self) -> AcceleratorType | None: + return self._get_property(ACCELERATOR_PROP, AcceleratorType) + + @accelerator.setter + def accelerator(self, v: AcceleratorType | None) -> None: + # dont pop as per documentation + self._set_property(ACCELERATOR_PROP, v, False) + + @property + def accelerator_constrained(self) -> bool | None: + return self._get_property(ACCELERATOR_CONSTRAINED_PROP, bool) + + @accelerator_constrained.setter + def accelerator_constrained(self, v: bool | None) -> None: + self._set_property(ACCELERATOR_CONSTRAINED_PROP, v) + + @property + def accelerator_summary(self) -> str | None: + return self._get_property(ACCELERATOR_SUMMARY_PROP, str) + + @accelerator_summary.setter + def accelerator_summary(self, v: str | None) -> None: + self._set_property(ACCELERATOR_SUMMARY_PROP, v) + + @property + def accelerator_count(self) -> int | None: + return self._get_property(ACCELERATOR_COUNT_PROP, int) + + @accelerator_count.setter + def accelerator_count(self, v: int | None) -> None: + self._set_property(ACCELERATOR_COUNT_PROP, v) + + @property + def input(self) -> list[ModelInput]: + return [ + ModelInput(inp) + for inp in get_required( + self._get_property(INPUT_PROP, list[dict[str, Any]]), self, INPUT_PROP + ) + ] + + @input.setter + def input(self, v: list[ModelInput]) -> None: + self._set_property(INPUT_PROP, [inp.to_dict() for inp in v]) + + @property + def output(self) -> list[ModelOutput]: + return [ + ModelOutput(outp) + for outp in get_required( + self._get_property(OUTPUT_PROP, list[dict[str, Any]]), self, OUTPUT_PROP + ) + ] + + @output.setter + def output(self, v: list[ModelOutput]) -> None: + self._set_property(OUTPUT_PROP, [outp.to_dict() for outp in v]) + + @property + def hyperparameters(self) -> Hyperparameters | None: + # todo: test this properly + prop = self._get_property(HYPERPARAMETERS_PROP, dict[str, Any]) + return Hyperparameters(prop) if prop is not None else None + + @hyperparameters.setter + def hyperparameters(self, v: Hyperparameters | None) -> None: + # todo test this + self._set_property(HYPERPARAMETERS_PROP, v.to_dict() if v is not None else None) + + def to_dict(self) -> dict[str, Any]: + return self.properties + + +class ItemMLMExtension(MLMExtension[pystac.Item]): + properties: dict[str, Any] + item: pystac.Item + + def __init__(self, item: pystac.Item): + self.item = item + self.properties = item.properties + + def __repr__(self) -> str: + return f"" + + +class AssetMLMExtension(MLMExtension[pystac.Asset]): + asset: pystac.Asset + asset_href: str + properties: dict[str, Any] + additional_read_properties: Iterable[dict[str, Any]] | None + + def __init__(self, asset: pystac.Asset): + self.asset = asset + self.asset_href = asset.href + self.properties = asset.extra_fields + if asset.owner and isinstance(asset.owner, pystac.Item): + self.additional_read_properties = [asset.owner.properties] + + def __repr__(self) -> str: + return f"" + + +class ItemAssetClassificationExtension(MLMExtension[pystac.ItemAssetDefinition]): + properties: dict[str, Any] + asset_defn: pystac.ItemAssetDefinition + + def __init__(self, item_asset: pystac.ItemAssetDefinition): + self.asset_defn = item_asset + self.properties = item_asset.properties + + def __repr__(self) -> str: + return f" Date: Wed, 26 Mar 2025 17:18:05 +0100 Subject: [PATCH 05/68] added stac:mlm extension tests --- tests/data-files/mlm/collection.json | 80 ++ tests/data-files/mlm/item_basic.json | 139 +++ .../cassettes/test_mlm/test_apply.yaml | 909 ++++++++++++++++++ .../test_mlm/test_mlm_validation.yaml | 651 +++++++++++++ .../cassettes/test_mlm/test_validate_mlm.yaml | 651 +++++++++++++ tests/extensions/test_mlm.py | 722 ++++++++++++++ 6 files changed, 3152 insertions(+) create mode 100644 tests/data-files/mlm/collection.json create mode 100644 tests/data-files/mlm/item_basic.json create mode 100644 tests/extensions/cassettes/test_mlm/test_apply.yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_mlm_validation.yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_validate_mlm.yaml create mode 100644 tests/extensions/test_mlm.py diff --git a/tests/data-files/mlm/collection.json b/tests/data-files/mlm/collection.json new file mode 100644 index 000000000..48de1e9ce --- /dev/null +++ b/tests/data-files/mlm/collection.json @@ -0,0 +1,80 @@ +{ + "stac_version": "1.1.0", + "stac_extensions": [ + "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json" + ], + "type": "Collection", + "id": "ml-model-examples", + "title": "Machine Learning Model examples", + "description": "Collection of items contained in the Machine Learning Model examples.", + "license": "Apache-2.0", + "extent": { + "spatial": { + "bbox": [ + [ + -7.882190080512502, + 37.13739173208318, + 27.911651652899923, + 58.21798141355221 + ] + ] + }, + "temporal": { + "interval": [ + [ + "1900-01-01T00:00:00Z", + "9999-12-31T23:59:59Z" + ] + ] + } + }, + "item_assets": { + "weights": { + "title": "model weights", + "roles": [ + "mlm:model", + "mlm:weights" + ] + } + }, + "summaries": { + "datetime": { + "minimum": "1900-01-01T00:00:00Z", + "maximum": "9999-12-31T23:59:59Z" + } + }, + "links": [ + { + "href": "collection.json", + "rel": "self" + }, + { + "href": "item_basic.json", + "rel": "item" + }, + { + "href": "item_bands_expression.json", + "rel": "item" + }, + { + "href": "item_eo_bands.json", + "rel": "item" + }, + { + "href": "item_eo_and_raster_bands.json", + "rel": "item" + }, + { + "href": "item_eo_bands_summarized.json", + "rel": "item" + }, + { + "href": "item_raster_bands.json", + "rel": "item" + }, + { + "href": "item_multi_io.json", + "rel": "item" + } + ] +} \ No newline at end of file diff --git a/tests/data-files/mlm/item_basic.json b/tests/data-files/mlm/item_basic.json new file mode 100644 index 000000000..f2c26f668 --- /dev/null +++ b/tests/data-files/mlm/item_basic.json @@ -0,0 +1,139 @@ +{ + "stac_version": "1.1.0", + "stac_extensions": [ + "https://stac-extensions.github.io/mlm/v1.4.0/schema.json" + ], + "type": "Feature", + "id": "example-model", + "collection": "ml-model-examples", + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -7.882190080512502, + 37.13739173208318 + ], + [ + -7.882190080512502, + 58.21798141355221 + ], + [ + 27.911651652899923, + 58.21798141355221 + ], + [ + 27.911651652899923, + 37.13739173208318 + ], + [ + -7.882190080512502, + 37.13739173208318 + ] + ] + ] + }, + "bbox": [ + -7.882190080512502, + 37.13739173208318, + 27.911651652899923, + 58.21798141355221 + ], + "properties": { + "description": "Basic STAC Item with only the MLM extension and no other extension cross-references.", + "datetime": null, + "start_datetime": "1900-01-01T00:00:00Z", + "end_datetime": "9999-12-31T23:59:59Z", + "mlm:name": "example-model", + "mlm:tasks": [ + "classification" + ], + "mlm:architecture": "ResNet", + "mlm:input": [ + { + "name": "Model with RGB input that does not refer to any band.", + "bands": [], + "input": { + "shape": [ + -1, + 3, + 64, + 64 + ], + "dim_order": [ + "batch", + "channel", + "height", + "width" + ], + "data_type": "float32" + } + } + ], + "mlm:output": [ + { + "name": "classification", + "tasks": [ + "classification" + ], + "result": { + "shape": [ + -1, + 1 + ], + "dim_order": [ + "batch", + "class" + ], + "data_type": "uint8" + }, + "classification_classes": [ + { + "value": 0, + "name": "BACKGROUND", + "description": "Background non-city.", + "color_hint": [ + 0, + 0, + 0 + ] + }, + { + "value": 1, + "name": "CITY", + "description": "A city is detected.", + "color_hint": [ + 0, + 0, + 255 + ] + } + ] + } + ] + }, + "assets": { + "model": { + "href": "https://huggingface.co/example/model-card", + "title": "Pytorch weights checkpoint", + "description": "Example model.", + "type": "text/html", + "roles": [ + "mlm:model" + ], + "mlm:artifact_type": "torch.save" + } + }, + "links": [ + { + "rel": "collection", + "href": "./collection.json", + "type": "application/json" + }, + { + "rel": "self", + "href": "https://example.com/item_basic.json", + "type": "application/geo+json" + } + ] +} \ No newline at end of file diff --git a/tests/extensions/cassettes/test_mlm/test_apply.yaml b/tests/extensions/cassettes/test_mlm/test_apply.yaml new file mode 100644 index 000000000..a140ac575 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_apply.yaml @@ -0,0 +1,909 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '0' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Fri, 21 Mar 2025 15:15:39 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - e6f0819013468ceeb42a39e2704aa62a148d5c14 + X-GitHub-Request-Id: + - 500D:3A6F18:89E644:8ACB55:67DD7A37 + X-Served-By: + - cache-fra-etou8220047-FRA + X-Timer: + - S1742570140.599424,VS0,VE129 + expires: + - Fri, 21 Mar 2025 14:49:51 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '0' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Fri, 21 Mar 2025 15:15:39 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - 036efacdda8669fa137ebe8ecd749a253d352b61 + X-GitHub-Request-Id: + - 76C2:3557:622AC4:62D633:67DCCB30 + X-Served-By: + - cache-fra-etou8220165-FRA + X-Timer: + - S1742570140.827733,VS0,VE93 + expires: + - Fri, 21 Mar 2025 02:23:04 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '0' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Fri, 21 Mar 2025 15:15:40 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - e2ba9fbc4a454ff21832a109922c330c668a4e77 + X-GitHub-Request-Id: + - D0E6:784E9:1823453:184B0AB:67DCEE1C + X-Served-By: + - cache-fra-etou8220063-FRA + X-Timer: + - S1742570140.991458,VS0,VE102 + expires: + - Fri, 21 Mar 2025 04:52:04 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v2.0.0/schema.json + response: + body: + string: "\n\n \n \n \n Page not found · GitHub Pages\n \n + \ \n \n\n
\n\n

404

\n + \

File not found

\n\n

\n The site + configured at this address does not\n contain the requested file.\n + \

\n\n

\n If this is your site, make sure that the + filename case matches the URL\n as well as any file permissions.
\n + \ For root URLs (like http://example.com/) you must provide + an\n index.html file.\n

\n\n

\n Read the full documentation\n + \ for more information about using GitHub Pages.\n + \

\n\n \n\n \n\n \n
\n \n\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '0' + Connection: + - close + Content-Length: + - '9379' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; img-src data:; connect-src + 'self' + Content-Type: + - text/html; charset=utf-8 + Date: + - Mon, 24 Mar 2025 15:36:45 GMT + ETag: + - '"64d39a40-24a3"' + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - 846304fdaed7762a83a65c80e93ddab35869b95a + X-GitHub-Request-Id: + - C119:2AF7BC:3D8B64:3E0D86:67E17C0D + X-Served-By: + - cache-fra-etou8220036-FRA + X-Timer: + - S1742830606.879533,VS0,VE118 + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 404 + message: Not Found +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_mlm_validation.yaml b/tests/extensions/cassettes/test_mlm/test_mlm_validation.yaml new file mode 100644 index 000000000..03e143392 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_mlm_validation.yaml @@ -0,0 +1,651 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '0' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 24 Mar 2025 13:05:52 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - b0cb43f638645db3ac6e51d76d502b46c3cee79c + X-GitHub-Request-Id: + - E452:10C3C4:7543D:76444:67E158B0 + X-Served-By: + - cache-fra-etou8220151-FRA + X-Timer: + - S1742821552.271476,VS0,VE120 + expires: + - Mon, 24 Mar 2025 13:15:52 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '138' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 24 Mar 2025 13:05:52 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - 9b1d7291f2992c19d0e908cbe54a77e3368b5418 + X-GitHub-Request-Id: + - 763B:5BEA4:46E1086:4783375:67E0BA63 + X-Served-By: + - cache-fra-etou8220157-FRA + X-Timer: + - S1742821553.532677,VS0,VE2 + expires: + - Mon, 24 Mar 2025 02:00:28 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_validate_mlm.yaml b/tests/extensions/cassettes/test_mlm/test_validate_mlm.yaml new file mode 100644 index 000000000..92facb58e --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_validate_mlm.yaml @@ -0,0 +1,651 @@ +interactions: +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '544' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 24 Mar 2025 13:14:56 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - 68aa3097205d303cc7aeb9a6558536efa8c74106 + X-GitHub-Request-Id: + - E452:10C3C4:7543D:76444:67E158B0 + X-Served-By: + - cache-fra-etou8220020-FRA + X-Timer: + - S1742822097.522824,VS0,VE2 + expires: + - Mon, 24 Mar 2025 13:15:52 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '0' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 24 Mar 2025 13:14:56 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - 58faf69ee3bfe8753044ce44d5b403768851054b + X-GitHub-Request-Id: + - 763B:5BEA4:46E1086:4783375:67E0BA63 + X-Served-By: + - cache-fra-etou8220160-FRA + X-Timer: + - S1742822097.614971,VS0,VE102 + expires: + - Mon, 24 Mar 2025 02:00:28 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py new file mode 100644 index 000000000..f5cd022e9 --- /dev/null +++ b/tests/extensions/test_mlm.py @@ -0,0 +1,722 @@ +import json +import logging +from copy import deepcopy +from typing import Any, cast + +import pytest + +import pystac.errors +from pystac import Asset, Collection, Item +from pystac.errors import STACError +from pystac.extensions.classification import Classification +from pystac.extensions.mlm import ( + ARCHITECTURE_PROP, + NAME_PROP, + TASKS_PROP, + AcceleratorType, + Hyperparameters, + InputStructure, + ItemMLMExtension, + MLMExtension, + ModelBand, + ModelInput, + ModelOutput, + ProcessingExpression, + ResizeType, + ResultStructure, + TaskType, + ValueScaling, + ValueScalingType, +) +from pystac.extensions.raster import DataType +from tests.utils import TestCases + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger() + +BASIC_MLM_ITEM_URI = TestCases.get_path("data-files/mlm/item_basic.json") +PLAIN_ITEM_URI = TestCases.get_path("data-files/item/sample-item.json") +MLM_COLLECTION_URI = TestCases.get_path("data-files/mlm/collection.json") + + +@pytest.fixture +def basic_item_dict() -> dict[str, Any]: + with open(BASIC_MLM_ITEM_URI) as f: + return cast(dict[str, Any], json.load(f)) + + +@pytest.fixture +def basic_mlm_item() -> Item: + return Item.from_file(BASIC_MLM_ITEM_URI) + + +@pytest.fixture +def plain_item() -> Item: + return Item.from_file(PLAIN_ITEM_URI) + + +@pytest.fixture +def mlm_collection() -> Collection: + return Collection.from_file(MLM_COLLECTION_URI) + + +def test_stac_extension(basic_mlm_item: Item) -> None: + assert MLMExtension.has_extension(basic_mlm_item) + + +def test_model_band() -> None: + d = {"name": "asdf", "format": "qwer", "expression": "asdf"} + c = ModelBand.create(**d) + assert c.name == d["name"] + assert c.format == d["format"] + assert c.expression == d["expression"] + + assert c.to_dict() == d + + +def test_model_props() -> None: + c = ModelBand({}) + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.name + + c.name = "asdf" + assert c.name == "asdf" + + c.format = "asdf" + assert c.name == "asdf" + + assert c.expression is None + c.expression = "asdf" + assert c.expression == "asdf" + + +def test_processing_expression() -> None: + d = {"format": "python", "expression": "asdf"} + c = ProcessingExpression.create(**d) + assert c.format == d["format"] + assert c.expression == d["expression"] + + assert c.to_dict() == d + + +def test_processint_expression_props() -> None: + c = ProcessingExpression({}) + + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.format + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.expression + + c.format = "python" + assert c.format == "python" + + c.expression = "B01 + B02" + assert c.expression == "B01 + B02" + + +def test_valuescaling_object() -> None: + c = ValueScaling.create( + ValueScalingType.MIN_MAX, + minimum=0, + maximum=4, + mean=3, + stddev=3.141, + value=4, + format="asdf", + expression="asdf", + ) + assert c.type == ValueScalingType.MIN_MAX + assert c.minimum == 0 + assert c.maximum == 4 + assert c.mean == 3 + assert c.stddev == 3.141 + assert c.value == 4 + assert c.format == "asdf" + assert c.expression == "asdf" + + with pytest.raises(STACError): + ValueScaling.create( + ValueScalingType.MIN_MAX, minimum=1 + ) # missing param maximum + + with pytest.raises(STACError): + ValueScaling.create(ValueScalingType.Z_SCORE, mean=3) # missing param stddev + + +def test_valuescaling_required_params() -> None: + assert ValueScaling.get_required_props(ValueScalingType.MIN_MAX) == [ + "minimum", + "maximum", + ] + assert ValueScaling.get_required_props(ValueScalingType.Z_SCORE) == [ + "mean", + "stddev", + ] + assert ValueScaling.get_required_props(ValueScalingType.CLIP) == [ + "minimum", + "maximum", + ] + assert ValueScaling.get_required_props(ValueScalingType.CLIP_MIN) == ["minimum"] + assert ValueScaling.get_required_props(ValueScalingType.CLIP_MAX) == ["maximum"] + assert ValueScaling.get_required_props(ValueScalingType.OFFSET) == ["value"] + assert ValueScaling.get_required_props(ValueScalingType.SCALE) == ["value"] + assert ValueScaling.get_required_props(ValueScalingType.PROCESSING) == [ + "format", + "expression", + ] + + +def test_input_structure() -> None: + c = InputStructure.create( + shape=[-1, 3, 64, 64], + dim_order=["batch", "channel", "width", "height"], + data_type=DataType.FLOAT64, + ) + assert c.shape == [-1, 3, 64, 64] + assert c.dim_order == ["batch", "channel", "width", "height"] + assert c.data_type == DataType.FLOAT64 + + +def test_model_input_structure_props() -> None: + c = InputStructure({}) + + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.shape + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.dim_order + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.data_type + + c.shape = [1] + assert c.shape == [1] + + c.dim_order = ["bands"] + assert c.dim_order == ["bands"] + + c.data_type = DataType.FLOAT64 + assert c.data_type == DataType.FLOAT64 + + +input_testdata = [ + ( + ["B02", "B03", "B04"], + ValueScaling.create(ValueScalingType.SCALE, value=3), + ResizeType.CROP, + ProcessingExpression.create("python", "asdf"), + ), + ( + ["B02", "B03", "B04"], + ValueScaling.create(ValueScalingType.SCALE, value=3), + None, + ProcessingExpression.create("python", "asdf"), + ), + ( + [ModelBand.create("B02"), ModelBand.create("B03"), ModelBand.create("B04")], + ValueScaling.create(ValueScalingType.SCALE, value=3), + ResizeType.CROP, + ProcessingExpression.create("python", "asdf"), + ), + ( + ["B02", "B03", "B04"], + None, + ResizeType.CROP, + ProcessingExpression.create("python", "asdf"), + ), + ( + ["B02", "B03", "B04"], + ValueScaling.create(ValueScalingType.SCALE, value=3), + ResizeType.CROP, + None, + ), +] + + +@pytest.mark.parametrize( + "bands, value_scaling, resize_type, pre_processing_function", input_testdata +) +def test_model_input( + bands: list[str] | list[ModelBand], + value_scaling: ValueScaling | None, + resize_type: ResizeType | None, + pre_processing_function: ProcessingExpression | None, +) -> None: + input_structure = InputStructure.create( + [1, 2, 3], ["batch", "width", "length"], DataType.FLOAT64 + ) + + c = ModelInput.create( + name="asdf", + bands=bands, + input=input_structure, + description="foo", + value_scaling=value_scaling, + resize_type=resize_type, + pre_processing_function=pre_processing_function, + ) + assert c.name == "asdf" + assert c.bands == bands + assert c.input == input_structure + assert c.description == "foo" + assert c.value_scaling == value_scaling + assert c.resize_type == resize_type + assert c.pre_processing_function == pre_processing_function + + # assert that some attributes are always included in dict, even if they are None + d_reverse = c.to_dict() + assert "value_scaling" in d_reverse + assert "resize_type" in d_reverse + assert "pre_processing_function" in d_reverse + + +def test_model_input_props() -> None: + c = ModelInput({}) + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.name + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.bands + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.input + + c.name = "asdf" + assert c.name == "asdf" + + c.bands = ["B02", "B03"] + assert c.bands == ["B02", "B03"] + + inp = InputStructure.create([12], ["bands"], DataType.FLOAT64) + c.input = inp + assert c.input == inp + + assert c.value_scaling is None + val_obj = ValueScaling.create(ValueScalingType.SCALE, value=3) + c.value_scaling = val_obj + assert c.value_scaling == val_obj + + assert c.resize_type is None + c.resize_type = ResizeType.CROP + assert c.resize_type == ResizeType.CROP + + assert c.pre_processing_function is None + exp = ProcessingExpression.create("python", "asdf") + c.pre_processing_function = exp + assert c.pre_processing_function == exp + + +def test_result_structure() -> None: + c = ResultStructure.create( + shape=[1, 64, 64], + dim_order=["time", "width", "height"], + data_type=DataType.FLOAT64, + ) + assert c.shape == [1, 64, 64] + assert c.dim_order == ["time", "width", "height"] + assert c.data_type == DataType.FLOAT64 + + +def test_result_structure_props() -> None: + c = ResultStructure({}) + + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.shape + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.dim_order + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.data_type + + c.shape = [1, 2, 3] + c.dim_order = ["batch", "band", "time"] + c.data_type = DataType.FLOAT64 + + assert c.shape == [1, 2, 3] + assert c.dim_order == ["batch", "band", "time"] + assert c.data_type == DataType.FLOAT64 + + +@pytest.mark.parametrize( + "post_proc_func", (ProcessingExpression.create("asdf", "asdf"), None) +) +def test_model_output(post_proc_func: ProcessingExpression | None) -> None: + c = ModelOutput.create( + name="asdf", + tasks=[TaskType.DETECTION, TaskType.OBJECT_DETECTION], + result=ResultStructure.create([1, 2, 3], ["a", "b", "c"], DataType.FLOAT64), + description="asdf", + classes=[ + Classification.create(1, name="a"), + Classification.create(2, name="b"), + ], + post_processing_function=post_proc_func, + ) + assert c.name == "asdf" + assert c.tasks == [TaskType.DETECTION, TaskType.OBJECT_DETECTION] + assert c.result == ResultStructure.create( + [1, 2, 3], ["a", "b", "c"], DataType.FLOAT64 + ) + assert c.description == "asdf" + assert c.classes == [ + Classification.create(1, name="a"), + Classification.create(2, name="b"), + ] + assert c.post_processing_function == post_proc_func + + +def test_model_output_props() -> None: + c = ModelOutput({}) + + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.name + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.tasks + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = c.result + + c.name = "asdf" + assert c.name == "asdf" + + c.tasks = [TaskType.CLASSIFICATION] + assert c.tasks == [TaskType.CLASSIFICATION] + + res = ResultStructure.create([1], ["band"], DataType.FLOAT64) + c.result = res + assert c.result == res + + assert c.description is None + c.description = "asdf" + assert c.description == "asdf" + + assert c.classes is None + c.classes = [Classification.create(value=3, name="foo")] + assert c.classes == [Classification.create(value=3, name="foo")] + + exp = ProcessingExpression.create("python", "asdf") + assert c.post_processing_function is None + c.post_processing_function = exp + assert c.post_processing_function == exp + + +def test_hyperparameters() -> None: + d = { + "nms_max_detections": 500, + "nms_threshold": 0.25, + "iou_threshold": 0.5, + "random_state": 12345, + } + c = Hyperparameters.create(**d) + for key in d: + assert key in c.to_dict() + assert c.to_dict()[key] == d[key] + + +def teest_get_schema_uri(basic_mlm_item: Item) -> None: + with pytest.raises(DeprecationWarning): + assert any( + [ + uri in basic_mlm_item.stac_extensions + for uri in MLMExtension.get_schema_uris() + ] + ) + + +def test_ext_raises_if_item_does_not_conform(plain_item: Item) -> None: + with pytest.raises(pystac.errors.ExtensionNotImplemented): + MLMExtension.ext(plain_item) + + +@pytest.mark.vcr() +def test_apply(plain_item: Item) -> None: + plain_item.assets["model"] = Asset( + href="https://google.com", + roles=["mlm:model"], + extra_fields={"mlm:artifact_type": "asdf"}, + ) + + MLMExtension.add_to(plain_item) + assert MLMExtension.get_schema_uri() in plain_item.stac_extensions + + model_input = [ + ModelInput.create( + name="modelinput", + bands=[], + input=InputStructure.create( + shape=[-1, 64, 64], + dim_order=["batch", "width", "height"], + data_type=DataType.FLOAT64, + ), + ) + ] + model_output = [ + ModelOutput.create( + name="modeloutput", + tasks=[TaskType.CLASSIFICATION], + result=ResultStructure.create( + shape=[1, 64, 64], + dim_order=["batch", "width", "height"], + data_type=DataType.FLOAT64, + ), + ) + ] + hyp = Hyperparameters.create( + nms_max_detections=500, + nms_threshold=0.25, + iou_threshold=0.5, + random_state=12345, + ) + + MLMExtension.ext(plain_item).apply( + name="asdf", + architecture="ResNet", + tasks=[TaskType.CLASSIFICATION], + framework="PyTorch", + framework_version="1.2.3", + memory_size=3, + total_parameters=123, + pretrained=True, + pretrained_source="asdfasdfasdf", + batch_size_suggestion=32, + accelerator=AcceleratorType.CUDA, + accelerator_constrained=False, + accelerator_summary="This is the summary", + accelerator_count=1, + input=model_input, + output=model_output, + hyperparameters=hyp, + ) + plain_item.validate() + + assert ( + MLMExtension.ext(plain_item).mlm_name is not None + and MLMExtension.ext(plain_item).mlm_name == "asdf" + ) + + assert ( + MLMExtension.ext(plain_item).architecture is not None + and MLMExtension.ext(plain_item).architecture == "ResNet" + ) + + assert MLMExtension.ext(plain_item).tasks is not None and MLMExtension.ext( + plain_item + ).tasks == [TaskType.CLASSIFICATION] + + assert ( + MLMExtension.ext(plain_item).framework is not None + and MLMExtension.ext(plain_item).framework == "PyTorch" + ) + + assert ( + MLMExtension.ext(plain_item).framework_version is not None + and MLMExtension.ext(plain_item).framework_version == "1.2.3" + ) + + assert ( + MLMExtension.ext(plain_item).memory_size is not None + and MLMExtension.ext(plain_item).memory_size == 3 + ) + + assert ( + MLMExtension.ext(plain_item).total_parameters is not None + and MLMExtension.ext(plain_item).total_parameters == 123 + ) + + assert ( + MLMExtension.ext(plain_item).pretrained is not None + and MLMExtension.ext(plain_item).pretrained is True + ) + + assert ( + MLMExtension.ext(plain_item).pretrained_source is not None + and MLMExtension.ext(plain_item).pretrained_source == "asdfasdfasdf" + ) + + assert ( + MLMExtension.ext(plain_item).batch_size_suggestion is not None + and MLMExtension.ext(plain_item).batch_size_suggestion == 32 + ) + + assert ( + MLMExtension.ext(plain_item).accelerator is not None + and MLMExtension.ext(plain_item).accelerator == AcceleratorType.CUDA + ) + + assert ( + MLMExtension.ext(plain_item).accelerator_constrained is not None + and MLMExtension.ext(plain_item).accelerator_constrained is False + ) + + assert ( + MLMExtension.ext(plain_item).accelerator_summary is not None + and MLMExtension.ext(plain_item).accelerator_summary == "This is the summary" + ) + + assert ( + MLMExtension.ext(plain_item).accelerator_count is not None + and MLMExtension.ext(plain_item).accelerator_count == 1 + ) + + assert MLMExtension.ext(plain_item).input is not None and len( + MLMExtension.ext(plain_item).input + ) == len(model_input) + + assert MLMExtension.ext(plain_item).input[0] == model_input[0] + + assert MLMExtension.ext(plain_item).output is not None and len( + MLMExtension.ext(plain_item).output + ) == len(model_output) + + assert MLMExtension.ext(plain_item).output[0] == model_output[0] + + assert ( + MLMExtension.ext(plain_item).hyperparameters is not None + and MLMExtension.ext(plain_item).hyperparameters == hyp + ) + + +def test_to_from_dict(basic_item_dict: dict[str, Any]) -> None: + d1 = deepcopy(basic_item_dict) + d2 = Item.from_dict(basic_item_dict, migrate=False).to_dict() + + assert d1 == d2 + + +def test_add_to_item(plain_item: Item) -> None: + # check that URI gets added + assert MLMExtension.get_schema_uri() not in plain_item.stac_extensions + MLMExtension.add_to(plain_item) + assert MLMExtension.get_schema_uri() in plain_item.stac_extensions + + # Assure that it gets added only once + MLMExtension.add_to(plain_item) + MLMExtension.add_to(plain_item) + + mlm_uris = [ + uri + for uri in plain_item.stac_extensions + if uri == MLMExtension.get_schema_uri() + ] + assert len(mlm_uris) == 1 + + +@pytest.mark.vcr() +def test_validate_mlm(basic_mlm_item: Item) -> None: + basic_mlm_item.validate() + + +def test_add_required_props(plain_item: Item) -> None: + item_ext = MLMExtension.ext(plain_item, add_if_missing=True) + + assert isinstance(item_ext, ItemMLMExtension) + + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = item_ext.mlm_name + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = item_ext.architecture + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = item_ext.tasks + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = item_ext.input + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = item_ext.output + + model_input = ModelInput.create( + name="DummyModel", + bands=["B04", "B08"], + input=InputStructure.create( + shape=[2, 10], dim_order=["time", "bands"], data_type=DataType.FLOAT64 + ), + ) + model_output = ModelOutput.create( + name="out", + tasks=[TaskType.CLASSIFICATION], + result=ResultStructure.create( + shape=[1], dim_order=["classification"], data_type=DataType.FLOAT64 + ), + ) + item_ext.mlm_name = "DummyModel" + item_ext.architecture = "ResNet" + item_ext.tasks = [TaskType.CLASSIFICATION] + item_ext.input = [model_input] + item_ext.output = [model_output] + + assert item_ext.mlm_name == "DummyModel" + assert item_ext.architecture == "ResNet" + assert item_ext.tasks == [TaskType.CLASSIFICATION] + assert item_ext.input == [model_input] + assert item_ext.output == [model_output] + + +def test_add_optional_props(plain_item: Item) -> None: + item_ext = MLMExtension.ext(plain_item, add_if_missing=True) + + assert isinstance(item_ext, ItemMLMExtension) + assert item_ext.framework is None + item_ext.framework = "pytorch" + assert item_ext.framework == "pytorch" + + assert item_ext.framework_version is None + item_ext.framework_version = "1.0.0" + assert item_ext.framework_version == "1.0.0" + + assert item_ext.memory_size is None + item_ext.memory_size = 3 + assert item_ext.memory_size == 3 + + assert item_ext.total_parameters is None + item_ext.total_parameters = 10000 + assert item_ext.total_parameters == 10000 + + assert item_ext.pretrained is None + item_ext.pretrained = True + assert item_ext.pretrained is True + + assert item_ext.pretrained_source is None + item_ext.pretrained_source = "asdf" + assert item_ext.pretrained_source == "asdf" + + assert item_ext.batch_size_suggestion is None + item_ext.batch_size_suggestion = 64 + assert item_ext.batch_size_suggestion == 64 + + assert item_ext.accelerator is None + item_ext.accelerator = AcceleratorType.CUDA + assert item_ext.accelerator == AcceleratorType.CUDA + + assert item_ext.accelerator_constrained is None + item_ext.accelerator_constrained = False + assert item_ext.accelerator_constrained is False + + assert item_ext.accelerator_summary is None + item_ext.accelerator_summary = "Summary" + assert item_ext.accelerator_summary == "Summary" + + assert item_ext.accelerator_count is None + item_ext.accelerator_count = 1 + assert item_ext.accelerator_count == 1 + + +def test_add_to_asset(plain_item: Item) -> None: + MLMExtension.ext(plain_item, add_if_missing=True) + asset = plain_item.assets["analytic"] + + assert NAME_PROP not in asset.extra_fields.keys() + assert ARCHITECTURE_PROP not in asset.extra_fields.keys() + assert TASKS_PROP not in asset.extra_fields.keys() + + asset_ext = MLMExtension.ext(asset) + asset_ext.mlm_name = "asdf" + asset_ext.architecture = "ResNet" + asset_ext.tasks = [TaskType.CLASSIFICATION] + + assert NAME_PROP in asset.extra_fields.keys() + assert ARCHITECTURE_PROP in asset.extra_fields.keys() + assert TASKS_PROP in asset.extra_fields.keys() + + assert asset.extra_fields[NAME_PROP] == "asdf" + assert asset.extra_fields[ARCHITECTURE_PROP] == "ResNet" + assert asset.extra_fields[TASKS_PROP] == [TaskType.CLASSIFICATION] + + +def test_item_asset_extension(mlm_collection: Collection) -> None: + assert mlm_collection.item_assets + item_asset = mlm_collection.item_assets["weights"] + MLMExtension.ext(item_asset, add_if_missing=True) + assert MLMExtension.get_schema_uri() in mlm_collection.stac_extensions + assert mlm_collection.item_assets["weights"].ext.has("mlm") From a89481b678fa5ff7bd9437948a13cabccf764f59 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 31 Mar 2025 16:24:52 +0200 Subject: [PATCH 06/68] added collection and asset extension --- pystac/extensions/mlm.py | 190 +++++++++++++++++++++++++++++++++-- tests/extensions/test_mlm.py | 175 +++++++++++++++++++++++++++++++- 2 files changed, 357 insertions(+), 8 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index d7b4f1df3..5faa881bd 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1,5 +1,6 @@ from __future__ import annotations +from abc import ABC from collections.abc import Iterable from typing import Any, Generic, Literal, TypeVar, cast @@ -13,7 +14,10 @@ from pystac.extensions.raster import DataType from pystac.utils import StringEnum, get_required -T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition) +T = TypeVar( + "T", pystac.Item, pystac.ItemAssetDefinition, pystac.Collection, pystac.Asset +) +AssetExtensionType = TypeVar("AssetExtensionType", bound="_AssetMLMExtension") # todo: support multiple version? SCHEMA_URI_PATTERN: str = "https://stac-extensions.github.io/mlm/v{version}/schema.json" @@ -87,6 +91,10 @@ OUTPUT_PROP: str = PREFIX + "output" HYPERPARAMETERS_PROP: str = PREFIX + "hyperparameters" +ARTIFACT_TYPE_ASSET_PROP = PREFIX + "artifact_type" +COMPILE_MDTHOD_ASSET_PROP = PREFIX + "compile_method" +ENTRYPOITN_ASSET_PROP = PREFIX + "entrypoint" + class TaskType(StringEnum): REGRESSION = ("regression",) @@ -1109,6 +1117,8 @@ def apply( accelerator_summary: str | None = None, accelerator_count: int | None = None, hyperparameters: Hyperparameters | None = None, + *args: Any, + **kwargs: Any, ) -> None: self.mlm_name = name self.architecture = architecture @@ -1137,12 +1147,18 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: if isinstance(obj, pystac.Item): cls.ensure_has_extension(obj, add_if_missing) return cast(MLMExtension[T], ItemMLMExtension(obj)) - elif isinstance(obj, pystac.Asset): - cls.ensure_owner_has_extension(obj, add_if_missing) - return cast(MLMExtension[T], AssetMLMExtension(obj)) + elif isinstance(obj, pystac.Collection): + cls.ensure_has_extension(obj, add_if_missing) + return cast(MLMExtension[T], CollectionMLMExtension(obj)) elif isinstance(obj, pystac.ItemAssetDefinition): cls.ensure_owner_has_extension(obj, add_if_missing) - return cast(MLMExtension[T], ItemAssetClassificationExtension(obj)) + return cast(MLMExtension[T], ItemAssetMLMExtension(obj)) + elif isinstance(obj, pystac.Asset): + raise pystac.STACError( + "This class cannot be used to extend STAC objects of type Assets. " + "To extend Asset objects, use either AssetNoPropsMLMExtension or " + "AssetMLMExtension" + ) else: raise pystac.ExtensionTypeError(cls._ext_error_message(obj)) @@ -1317,7 +1333,21 @@ def __repr__(self) -> str: return f"" -class AssetMLMExtension(MLMExtension[pystac.Asset]): +class CollectionMLMExtension(MLMExtension[pystac.Collection]): + collection: pystac.Collection + properties: dict[str, Any] + links: list[pystac.Link] + + def __init__(self, collection: pystac.Collection): + self.collection = collection + self.properties = collection.extra_fields + self.links = collection.links + + def __repr__(self) -> str: + return f"" + + +class _AssetMLMExtension(ABC): asset: pystac.Asset asset_href: str properties: dict[str, Any] @@ -1330,11 +1360,157 @@ def __init__(self, asset: pystac.Asset): if asset.owner and isinstance(asset.owner, pystac.Item): self.additional_read_properties = [asset.owner.properties] + @classmethod + def _ext(cls: type[AssetExtensionType], obj: pystac.Asset) -> AssetExtensionType: + if not isinstance(obj, pystac.Asset): + raise STACError( + "This class can only be used to extend Assets. " + "For Items and Collections use MLMExtension." + ) + return cls(obj) + + def to_dict(self) -> dict[str, Any]: + return self.properties # todo: test this + + @classmethod + def get_schema_uri(cls) -> str: + return SCHEMA_URI_PATTERN.format(version=DEFAULT_VERSION) + + @property + def artifact_type(self) -> str | None: + prop_value = self.properties.get(ARTIFACT_TYPE_ASSET_PROP) + if isinstance(self.asset.roles, list) and "mlm:model" in self.asset.roles: + return get_required(prop_value, self, ARTIFACT_TYPE_ASSET_PROP) + else: + return prop_value + + @artifact_type.setter + def artifact_type(self, v: str | None) -> None: + if isinstance(self.asset.roles, list) and "mlm:model" in self.asset.roles: + if v is None: + raise pystac.errors.RequiredPropertyMissing( + self, + ARTIFACT_TYPE_ASSET_PROP, + f"{ARTIFACT_TYPE_ASSET_PROP} is a required property and must " + f"not be None for asset with role mlm:model.", + ) + self.properties[ARTIFACT_TYPE_ASSET_PROP] = v + else: + if v is not None: + self.properties[ARTIFACT_TYPE_ASSET_PROP] = v + else: + self.properties.pop(ARTIFACT_TYPE_ASSET_PROP, None) + + @property + def compile_method(self) -> str | None: + return self.properties.get(COMPILE_MDTHOD_ASSET_PROP) + + @compile_method.setter + def compile_method(self, v: str | None) -> None: + if v is not None: + self.properties[COMPILE_MDTHOD_ASSET_PROP] = v + else: + self.properties.pop(COMPILE_MDTHOD_ASSET_PROP, None) + + @property + def entrypoint(self) -> str | None: + return self.properties.get(ENTRYPOITN_ASSET_PROP) + + @entrypoint.setter + def entrypoint(self, v: str | None) -> None: + if v is not None: + self.properties[ENTRYPOITN_ASSET_PROP] = v + else: + self.properties.pop(ENTRYPOITN_ASSET_PROP, None) + + def __repr__(self) -> str: + return f"" + + +class AssetNoPropsMLMExtension( + _AssetMLMExtension, + Generic[T], + PropertiesExtension, + ExtensionManagementMixin[pystac.Item | pystac.Collection], +): + def apply( + self, + artifact_type: str | None = None, + compile_method: str | None = None, + entrypoint: str | None = None, + ) -> None: + self.artifact_type = artifact_type + self.compile_method = compile_method + self.entrypoint = entrypoint + + @classmethod + def ext( + cls, obj: pystac.Asset, add_if_missing: bool = False + ) -> AssetNoPropsMLMExtension[pystac.Asset]: + cls.ensure_owner_has_extension(obj, add_if_missing) + return AssetNoPropsMLMExtension._ext(obj) + + +class AssetPropsMLMExtension(_AssetMLMExtension, MLMExtension[pystac.Asset]): def __repr__(self) -> str: return f"" + @classmethod + def ext( + cls, obj: pystac.Asset, add_if_missing: bool = False + ) -> AssetPropsMLMExtension: + cls.ensure_owner_has_extension(obj, add_if_missing) + return AssetPropsMLMExtension._ext(obj) + + def apply( + self, + name: str, + architecture: str, + tasks: list[TaskType], + input: list[ModelInput], + output: list[ModelOutput], + framework: str | None = None, + framework_version: str | None = None, + memory_size: int | None = None, + total_parameters: int | None = None, + pretrained: bool | None = None, + pretrained_source: str | None = None, + batch_size_suggestion: int | None = None, + accelerator: AcceleratorType | None = None, + accelerator_constrained: bool | None = None, + accelerator_summary: str | None = None, + accelerator_count: int | None = None, + hyperparameters: Hyperparameters | None = None, + artifact_type: str | None = None, + compile_method: str | None = None, + entrypoint: str | None = None, + ) -> None: + MLMExtension.apply( + self, + name=name, + architecture=architecture, + tasks=tasks, + input=input, + output=output, + framework=framework, + framework_version=framework_version, + memory_size=memory_size, + total_parameters=total_parameters, + pretrained=pretrained, + pretrained_source=pretrained_source, + batch_size_suggestion=batch_size_suggestion, + accelerator=accelerator, + accelerator_constrained=accelerator_constrained, + accelerator_summary=accelerator_summary, + accelerator_count=accelerator_count, + hyperparameters=hyperparameters, + ) + self.artifact_type = artifact_type + self.compile_method = compile_method + self.entrypoint = entrypoint + -class ItemAssetClassificationExtension(MLMExtension[pystac.ItemAssetDefinition]): +class ItemAssetMLMExtension(MLMExtension[pystac.ItemAssetDefinition]): properties: dict[str, Any] asset_defn: pystac.ItemAssetDefinition diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index f5cd022e9..830e3c491 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -14,6 +14,8 @@ NAME_PROP, TASKS_PROP, AcceleratorType, + AssetNoPropsMLMExtension, + AssetPropsMLMExtension, Hyperparameters, InputStructure, ItemMLMExtension, @@ -700,7 +702,7 @@ def test_add_to_asset(plain_item: Item) -> None: assert ARCHITECTURE_PROP not in asset.extra_fields.keys() assert TASKS_PROP not in asset.extra_fields.keys() - asset_ext = MLMExtension.ext(asset) + asset_ext = AssetPropsMLMExtension.ext(asset) asset_ext.mlm_name = "asdf" asset_ext.architecture = "ResNet" asset_ext.tasks = [TaskType.CLASSIFICATION] @@ -714,9 +716,180 @@ def test_add_to_asset(plain_item: Item) -> None: assert asset.extra_fields[TASKS_PROP] == [TaskType.CLASSIFICATION] +@pytest.mark.parametrize("is_model_asset", (True, False)) +def test_asset_props(plain_item: Item, is_model_asset: bool) -> None: + asset = plain_item.assets["analytic"] + asset_ext = AssetNoPropsMLMExtension.ext(asset, add_if_missing=True) + + assert asset_ext.artifact_type is None + assert asset_ext.compile_method is None + assert asset_ext.entrypoint is None + + # test special behavior if the asset has role "mlm:model" + if is_model_asset and isinstance(asset_ext.asset.roles, list): + asset_ext.asset.roles.append("mlm:model") + with pytest.raises(pystac.errors.RequiredPropertyMissing): + _ = asset_ext.artifact_type + + asset_ext.artifact_type = "foo" + asset_ext.compile_method = "bar" + asset_ext.entrypoint = "baz" + + assert asset_ext.artifact_type == "foo" + assert asset_ext.compile_method == "bar" + assert asset_ext.entrypoint == "baz" + + +def test_add_to_generic_asset() -> None: + asset = pystac.Asset( + href="http://example.com/test.tiff", + title="image", + description="asdf", + media_type="application/tiff", + roles=["mlm:model"], + extra_fields={ + "mlm:artifact_type": "foo", + "mlm:compile_method": "bar", + "mlm:entrypoint": "baz", + }, + ) + asset_ext = AssetNoPropsMLMExtension.ext(asset, add_if_missing=False) + assert asset_ext.artifact_type == "foo" + assert asset_ext.compile_method == "bar" + assert asset_ext.entrypoint == "baz" + + +def test_apply_generic_asset() -> None: + asset = pystac.Asset( + href="http://example.com/test.tiff", + title="image", + description="asdf", + media_type="application/tiff", + roles=["mlm:model"], + ) + asset_ext = AssetNoPropsMLMExtension.ext(asset, add_if_missing=False) + asset_ext.apply(artifact_type="foo", compile_method="bar", entrypoint="baz") + assert asset_ext.artifact_type == "foo" + assert asset_ext.compile_method == "bar" + assert asset_ext.entrypoint == "baz" + + +def test_add_to_detailled_asset() -> None: + model_input = ModelInput.create( + name="model", + bands=["B02"], + input=InputStructure.create( + shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 + ), + ) + model_output = ModelOutput.create( + name="output", + tasks=[TaskType.CLASSIFICATION], + result=ResultStructure.create( + shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 + ), + ) + + asset = pystac.Asset( + href="http://example.com/test.tiff", + title="image", + description="asdf", + media_type="application/tiff", + roles=["mlm:model"], + extra_fields={ + "mlm:name": "asdf", + "mlm:architecture": "ResNet", + "mlm:tasks": [TaskType.CLASSIFICATION], + "mlm:input": [model_input.to_dict()], + "mlm:output": [model_output.to_dict()], + "mlm:artifact_type": "foo", + "mlm:compile_method": "bar", + "mlm:entrypoint": "baz", + }, + ) + + asset_ext = AssetPropsMLMExtension.ext(asset, add_if_missing=False) + + assert asset_ext.mlm_name == "asdf" + assert asset_ext.architecture == "ResNet" + assert asset_ext.tasks == [TaskType.CLASSIFICATION] + assert asset_ext.input == [model_input] + assert asset_ext.output == [model_output] + assert asset_ext.artifact_type == "foo" + assert asset_ext.compile_method == "bar" + assert asset_ext.entrypoint == "baz" + + +def test_apply_detailled_asset() -> None: + asset = pystac.Asset( + href="http://example.com/test.tiff", + title="image", + description="asdf", + media_type="application/tiff", + roles=["mlm:model"], + ) + asset_ext = AssetPropsMLMExtension.ext(asset, add_if_missing=False) + + model_input = ModelInput.create( + name="model", + bands=["B02"], + input=InputStructure.create( + shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 + ), + ) + model_output = ModelOutput.create( + name="output", + tasks=[TaskType.CLASSIFICATION], + result=ResultStructure.create( + shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 + ), + ) + + asset_ext.apply( + "asdf", + "ResNet", + [TaskType.CLASSIFICATION], + [model_input], + [model_output], + artifact_type="foo", + compile_method="bar", + entrypoint="baz", + ) + + assert asset_ext.mlm_name == "asdf" + assert asset_ext.architecture == "ResNet" + assert asset_ext.tasks == [TaskType.CLASSIFICATION] + assert asset_ext.input == [model_input] + assert asset_ext.output == [model_output] + assert asset_ext.artifact_type == "foo" + assert asset_ext.compile_method == "bar" + assert asset_ext.entrypoint == "baz" + + def test_item_asset_extension(mlm_collection: Collection) -> None: assert mlm_collection.item_assets item_asset = mlm_collection.item_assets["weights"] MLMExtension.ext(item_asset, add_if_missing=True) assert MLMExtension.get_schema_uri() in mlm_collection.stac_extensions assert mlm_collection.item_assets["weights"].ext.has("mlm") + + +def test_collection_extension(mlm_collection: Collection) -> None: + coll_ext = MLMExtension.ext(mlm_collection, add_if_missing=True) + assert MLMExtension.get_schema_uri() in mlm_collection.stac_extensions + assert mlm_collection.ext.has("mlm") + + coll_ext.mlm_name = "asdf" + assert coll_ext.mlm_name == "asdf" + + +def test_raise_exception_on_mlm_extension_and_asset() -> None: + asset = pystac.Asset( + href="http://example.com/test.tiff", + title="image", + description="asdf", + media_type="application/tiff", + roles=["mlm:model"], + ) + with pytest.raises(pystac.errors.STACError): + MLMExtension.ext(asset, add_if_missing=False) From 9beabc1df6093c6c47a6b8221cc89de786be2495 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 31 Mar 2025 16:56:55 +0200 Subject: [PATCH 07/68] removed TODO --- pystac/extensions/mlm.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 5faa881bd..a5ee7c760 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1308,13 +1308,11 @@ def output(self, v: list[ModelOutput]) -> None: @property def hyperparameters(self) -> Hyperparameters | None: - # todo: test this properly prop = self._get_property(HYPERPARAMETERS_PROP, dict[str, Any]) return Hyperparameters(prop) if prop is not None else None @hyperparameters.setter def hyperparameters(self, v: Hyperparameters | None) -> None: - # todo test this self._set_property(HYPERPARAMETERS_PROP, v.to_dict() if v is not None else None) def to_dict(self) -> dict[str, Any]: From 7165b6175835a2c1629300b28b4b983159540f63 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 2 Apr 2025 10:55:52 +0200 Subject: [PATCH 08/68] added documentation --- docs/api/extensions/mlm.rst | 8 + pystac/extensions/mlm.py | 480 +++++++++++++++++++++++++++++++++++- 2 files changed, 482 insertions(+), 6 deletions(-) create mode 100644 docs/api/extensions/mlm.rst diff --git a/docs/api/extensions/mlm.rst b/docs/api/extensions/mlm.rst new file mode 100644 index 000000000..8a4e3531d --- /dev/null +++ b/docs/api/extensions/mlm.rst @@ -0,0 +1,8 @@ +pystac.extensions.mlm +============================ + +.. automodule:: pystac.extensions.mlm + :members: + :inherited-members: StringEnum, Generic, PropertiesExtension, ExtensionManagementMixin + :undoc-members: + diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index a5ee7c760..f694bd8a0 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1,3 +1,11 @@ +"""Implements the :stac-ext:`Machine Learning Model (MLM) Extension `. + +This documentation does not provide a full-detail description of the meaning of each +parameter and how to use them. For an in-depth description of this extension, and the +use of each property, please refer to the official +:stac-ext:`documentation ` +""" + from __future__ import annotations from abc import ABC @@ -97,6 +105,10 @@ class TaskType(StringEnum): + """ + An enumeration of Tasks supported by the extension + """ + REGRESSION = ("regression",) CLASSIFICATION = ("classification",) SCENE_CLASSIFICATION = ("scene-classification",) @@ -113,6 +125,10 @@ class TaskType(StringEnum): class AcceleratorType(StringEnum): + """ + An enumeration of accelerators supported by the extension + """ + AMD64 = ("amd64",) CUDA = ("cuda",) XLA = ("xla",) @@ -123,6 +139,10 @@ class AcceleratorType(StringEnum): class ResizeType(StringEnum): + """ + An enumeration of Resize operations supported by the extension + """ + CROP = "crop" PAD = "pad" INTERPOLATION_NEAREST = "interpolate-nearest" @@ -136,6 +156,10 @@ class ResizeType(StringEnum): class ValueScalingType(StringEnum): + """ + An enumeratino of Value Scaling operations supported by the extension + """ + MIN_MAX = "min-max" Z_SCORE = "z-score" CLIP = "clip" @@ -166,6 +190,7 @@ def apply( ) -> None: """ Set the properties for a new ModelBand. + Args: name: Name of the band referring to an extended band definition format: The type of expression that is specified in the expression property @@ -183,6 +208,7 @@ def create( ) -> ModelBand: """ Create a new ModelBand. + Args: name: Name of the band referring to an extended band definition format: The type of expression that is specified in the expression property @@ -196,6 +222,9 @@ def create( @property def name(self) -> str: + """ + Get or set the required name property of a ModelBand object + """ return get_required( self.properties.get(NAME_MODEL_BAND_OBJECT_PROP), self, @@ -208,6 +237,9 @@ def name(self, v: str) -> None: @property def format(self) -> str | None: + """ + Get or set the optional format property of a ModelBand object + """ return self.properties.get(FORMAT_MODEL_BAND_OBJECT_PROP) @format.setter @@ -219,6 +251,9 @@ def format(self, v: str | None) -> None: @property def expression(self) -> Any: + """ + Get or set the optional expression property of a ModelBand object + """ return self.properties.get(EXPRESSION_MODEL_BAND_OBJECT_PROP) @expression.setter @@ -229,6 +264,12 @@ def expression(self, v: Any) -> None: self.properties.pop(EXPRESSION_MODEL_BAND_OBJECT_PROP, None) def to_dict(self) -> dict[str, Any]: + """ + Returns the dictionary encoding of this ModelBand object + + Returns: + dict[str, Any + """ return self.properties @@ -247,6 +288,7 @@ def __eq__(self, other: object) -> bool: def apply(self, format: str, expression: Any) -> None: """ Set the properties for a new ProcessingExpression + Args: format: The type of the expression that is specified in the expression property. @@ -259,21 +301,27 @@ def apply(self, format: str, expression: Any) -> None: @classmethod def create(cls, format: str, expression: Any) -> ProcessingExpression: - c = cls({}) """ Creates a new ProcessingExpression + Args: format: The type of the expression that is specified in the expression property. - expression: An expression compliant with the format specified. The + expression: An expression compliant with the format specified. The expression can be any data type and depends on the format given, e.g. string or object. + Returns: + ProcessingExpression """ + c = cls({}) c.apply(format=format, expression=expression) return c @property def format(self) -> str: + """ + Get or set the required format property of this ProcessingExpression + """ return get_required( self.properties.get(FORMAT_PROCESSING_EXPRESSION_PROP), self, @@ -286,6 +334,9 @@ def format(self, v: str) -> None: @property def expression(self) -> Any: + """ + Get or set the required expression property of this ProcessingExpression + """ return get_required( self.properties.get(EXPRESSION_PROCESSING_EXPRESSION_PROP), self, @@ -297,6 +348,11 @@ def expression(self, v: Any) -> None: self.properties[EXPRESSION_PROCESSING_EXPRESSION_PROP] = v def to_dict(self) -> dict[str, Any]: + """ + Returns the dictionary encoding of this ProcessingExpression + Returns: + dict[str, Any] + """ return self.properties @@ -331,6 +387,22 @@ def apply( format: str | None = None, expression: str | None = None, ) -> None: + """ + Creates new ValueScaling object. Depending on the type + parameter, different parameters are required. Consult STAC:MLM documentation + or use :meth:`~get_required_props` for details on what parameters are required + for which ValueScaling ``type``. + + Args: + type: The type of ValueScaling object. + minimum: A minimum value + maximum: A maximum value + mean: A mean value + stddev: A standard deviation value + value: A scalar value + format: The format of the expression + expression: The expression itself + """ self.validate_property_dict(type, locals()) self.type = type @@ -354,6 +426,25 @@ def create( format: str | None = None, expression: str | None = None, ) -> ValueScaling: + """ + Creates new ValueScaling object. Depending on the type + parameter, different parameters are required. Consult STAC:MLM documentation + or use :meth:`~get_required_props` for details on what parameters are required + for which ValueScaling ``type``. + + Args: + type: The type of ValueScaling object. + minimum: A minimum value + maximum: A maximum value + mean: A mean value + stddev: A standard deviation value + value: A scalar value + format: The format of the expression + expression: The expression itself + + Returns: + ValueScaling + """ c = cls({}) c.apply( type=type, @@ -369,6 +460,16 @@ def create( @classmethod def get_required_props(cls, type: ValueScalingType) -> list[str]: + """ + Determines the parameters required for a certain ValueScaling operation. + + Args: + type: The type of ValueScaling operation for which required proreties are + to be retrieved + + Returns: + list[str]: names of proreties required for the given ``type`` + """ d: dict[str, list[str]] = { "min-max": ["minimum", "maximum"], "z-score": ["mean", "stddev"], @@ -385,6 +486,20 @@ def get_required_props(cls, type: ValueScalingType) -> list[str]: def validate_property_dict( cls, type: ValueScalingType, props: dict[str, Any] ) -> None: + """ + Validate whether given properties satisfy the requiremts set by the ValueScaling + ``type`` parameter + + Args: + type: The type of ValueScaling operation + props: The properties to validate. Keys in this dict are the property + names, values are the property values + + Raises: + :class:``STACError``: if the given properties do not satisfy + the requirements of the ValueScaling ``type`` + + """ required_props = cls.get_required_props(type) given_props = [ prop_name @@ -407,6 +522,9 @@ def validate_property_dict( @property def type(self) -> str: + """ + Get or set the required type property of this ValueScaling object + """ return get_required( self.properties.get(TYPE_VALUE_SCALING_PROP), self, TYPE_VALUE_SCALING_PROP ) @@ -417,6 +535,9 @@ def type(self, v: str) -> None: @property def minimum(self) -> int | float | None: + """ + Get or set the minimum property of this ValueScaling object + """ return self.properties.get(MINIMUM_VALUE_SCALING_PROP) @minimum.setter @@ -428,6 +549,9 @@ def minimum(self, v: int | float | None) -> None: @property def maximum(self) -> int | float | None: + """ + Get or set the maximum property of this ValueScaling object + """ return self.properties.get(MAXIMUM_VALUE_SCALING_PROP) @maximum.setter @@ -439,6 +563,9 @@ def maximum(self, v: int | float | None) -> None: @property def mean(self) -> int | float | None: + """ + Get or set the mean property of this ValueScaling object + """ return self.properties.get(MEAN_VALUE_SCALING_PROP) @mean.setter @@ -450,6 +577,9 @@ def mean(self, v: int | float | None) -> None: @property def stddev(self) -> int | float | None: + """ + Get or set the stddev (standard deviation) property of this ValueScaling object + """ return self.properties.get(STDDEV_VALUE_SCALING_PROP) @stddev.setter @@ -461,6 +591,9 @@ def stddev(self, v: int | float | None) -> None: @property def value(self) -> int | float | None: + """ + Get or set the value property of this ValueScaling object + """ return self.properties.get(VALUE_VALUE_SCALING_PROP) @value.setter @@ -472,6 +605,9 @@ def value(self, v: int | float | None) -> None: @property def format(self) -> str | None: + """ + Get or set the format property of this ValueScaling object + """ return self.properties.get(FORMAT_VALUE_SCALING_PROP) @format.setter @@ -483,6 +619,9 @@ def format(self, v: str | None) -> None: @property def expression(self) -> str | None: + """ + Get or set the expression property of this ValueScaling object + """ return self.properties.get(EXPRESSION_VALUE_SCALING_PROP) @expression.setter @@ -493,6 +632,12 @@ def expression(self, v: str | None) -> None: self.properties.pop(EXPRESSION_VALUE_SCALING_PROP, None) def to_dict(self) -> dict[str, Any]: + """ + Serialize a dict representation of this ValueScaling object + + Returns: + dict[str, Any] + """ return self.properties @@ -519,6 +664,7 @@ def apply( ) -> None: """ Set the properties for a new InputStructure. + Args: shape: Shape of the input n-dimensional array (e.g.: B×C×H×W), including the batch size dimension. @@ -539,6 +685,7 @@ def create( ) -> InputStructure: """ Create a new InputStructure. + Args: shape: Shape of the input n-dimensional array (e.g.: B×C×H×W), including the batch size dimension. Each dimension must either be greater than 0 or @@ -548,6 +695,8 @@ def create( data_type: The data type of values in the n-dimensional array. For model inputs, this should be the data type of the processed input supplied to the model inference function, not the data type of the source bands. + Returns: + InputStructure """ c = cls({}) c.apply(shape=shape, dim_order=dim_order, data_type=data_type) @@ -555,6 +704,9 @@ def create( @property def shape(self) -> list[int]: + """ + Get or set the required shape property of this InputStructure object + """ return get_required( self.properties.get(SHAPE_INPUT_STRUCTURE_PROP), self, @@ -567,6 +719,9 @@ def shape(self, v: list[int]) -> None: @property def dim_order(self) -> list[str]: + """ + Get or set the required dim_order property of this InputStructure object + """ return get_required( self.properties.get(DIM_ORDER_INPUT_STRUCTURE_PROP), self, @@ -579,6 +734,9 @@ def dim_order(self, v: list[str]) -> None: @property def data_type(self) -> DataType: + """ + Get or set the required data_type property of this InputStructure object + """ return get_required( self.properties.get(DATA_TYPE_INPUT_STRUCTURE_PROP), self, @@ -590,6 +748,12 @@ def data_type(self, v: DataType) -> None: self.properties[DATA_TYPE_INPUT_STRUCTURE_PROP] = v def to_dict(self) -> dict[str, Any]: + """ + Serializes a dict representation of this InputStucture object + + Returns: + dict[str, Any] + """ return self.properties @@ -624,6 +788,7 @@ def apply( ) -> None: """ Sets the Properties for a new Input + Args: name: Name of the input variable defined by the model. If no explicit name is defined by the model, an informative name (e.g.: "RGB Time Series") @@ -674,6 +839,7 @@ def create( ) -> ModelInput: """ Creates a new Input + Args: name: Name of the input variable defined by the model. If no explicit name is defined by the model, an informative name (e.g.: "RGB Time Series") @@ -717,6 +883,9 @@ def create( @property def name(self) -> str: + """ + Gets or sets the required name property of this ModelInput object + """ return get_required( self.properties.get(NAME_INPUT_OBJECT_PROP), self, NAME_INPUT_OBJECT_PROP ) @@ -727,6 +896,9 @@ def name(self, v: str) -> None: @property def bands(self) -> list[ModelBand] | list[str]: + """ + Gets or sets the required bands property of this ModelInput object + """ bands: list[str] | list[dict[str, Any]] = get_required( self.properties.get(BANDS_INPUT_OBJECT_PROP), self, BANDS_INPUT_OBJECT_PROP ) @@ -746,6 +918,9 @@ def bands(self, v: list[ModelBand] | list[str]) -> None: @property def input(self) -> InputStructure: + """ + Gets or sets the required input property of this ModelInput object + """ return InputStructure( get_required( self.properties.get(INPUT_INPUT_OBJECT_PROP), @@ -760,6 +935,9 @@ def input(self, v: InputStructure) -> None: @property def description(self) -> str | None: + """ + Gets or sets the description property of this ModelInput object + """ return self.properties.get(DESCRIPTION_INPUT_OBJECT_PROP) @description.setter @@ -771,6 +949,9 @@ def description(self, v: str | None) -> None: @property def value_scaling(self) -> ValueScaling | None: + """ + Gets or sets the value_scaling property of this ModelInput object + """ v = self.properties.get(VALUE_SCALING_INPUT_OBJECT_PROP) return ValueScaling(v) if v is not None else v @@ -783,6 +964,9 @@ def value_scaling(self, v: ValueScaling | None) -> None: @property def resize_type(self) -> ResizeType | None: + """ + Gets or sets the resize_type property of this ModelInput object + """ return self.properties.get(RESIZE_TYPE_INPUT_OBJECT_PROP) @resize_type.setter @@ -792,6 +976,9 @@ def resize_type(self, v: ResizeType | None) -> None: @property def pre_processing_function(self) -> ProcessingExpression | None: + """ + Gets or sets the pre_processing_function property of this ModelInput object + """ v = self.properties.get(PRE_PROCESSING_FUNCTION_INPUT_OBJECT_PROP) return ProcessingExpression(v) if v is not None else None @@ -803,6 +990,12 @@ def pre_processing_function(self, v: ProcessingExpression | None) -> None: ) def to_dict(self) -> dict[str, Any]: + """ + Serializes this ModelInput object into its dict representation + + Returns: + dict[str, Any] + """ return self.properties @@ -826,6 +1019,7 @@ def apply( ) -> None: """ Set the properties for a new ResultStructure. + Args: shape: Shape of the n-dimensional result array (e.g.: B×H×W or B×C), possibly including a batch size dimension. The dimensions must either @@ -845,6 +1039,7 @@ def create( ) -> ResultStructure: """ Creates a new ResultStructure. + Args: shape: Shape of the n-dimensional result array (e.g.: B×H×W or B×C), possibly including a batch size dimension. The dimensions must either @@ -853,6 +1048,9 @@ def create( data_type: The data type of values in the n-dimensional array. For model outputs, this should be the data type of the result of the model inference without extra post processing. + + Returns: + ResultStructure """ c = cls({}) c.apply(shape=shape, dim_order=dim_order, data_type=data_type) @@ -860,6 +1058,9 @@ def create( @property def shape(self) -> list[int]: + """ + Gets or sets the required shape property of the ResultStructure object + """ return get_required( self.properties.get(SHAPE_RESULT_STRUCTURE_PROP), self, @@ -872,6 +1073,9 @@ def shape(self, v: list[int]) -> None: @property def dim_order(self) -> list[str]: + """ + Gets or sets the required dim_order property of the ResultStructure object + """ return get_required( self.properties.get(DIM_ORDER_RESULT_STRUCTURE_PROP), self, @@ -884,6 +1088,9 @@ def dim_order(self, v: list[str]) -> None: @property def data_type(self) -> DataType: + """ + Gets or sets the required data_type property of the ResultStructure object + """ return get_required( self.properties.get(DATA_TYPE_RESULT_STRUCTURE_PROP), self, @@ -895,6 +1102,11 @@ def data_type(self, v: DataType) -> None: self.properties[DATA_TYPE_RESULT_STRUCTURE_PROP] = v def to_dict(self) -> dict[str, Any]: + """ + Serilaizes this ResultStructure object to a dict + Returns: + dict[str, Any] + """ return self.properties @@ -927,6 +1139,7 @@ def apply( ) -> None: """ Sets the properties for a new Output + Args: name:Name of the output variable defined by the model. If no explicit name is defined by the model, an informative name (e.g.: "CLASSIFICATION") @@ -963,6 +1176,7 @@ def create( ) -> ModelOutput: """ Creates a new Output + Args: name:Name of the output variable defined by the model. If no explicit name is defined by the model, an informative name (e.g.: "CLASSIFICATION") @@ -977,6 +1191,9 @@ def create( classes: A list of class objects adhering to the Classification Extension. post_processing_function: Custom postprocessing function where normalization, rescaling, or any other significant operations takes place. + + Returns: + ModelOutput """ c = cls({}) c.apply( @@ -991,6 +1208,9 @@ def create( @property def name(self) -> str: + """ + Gets or sets the required name property of this ModelOutput object + """ return get_required( self.properties.get(NAME_RESULT_PROP), self, NAME_RESULT_PROP ) @@ -1001,6 +1221,9 @@ def name(self, v: str) -> None: @property def tasks(self) -> list[TaskType]: + """ + Gets or sets the required tasks property of this ModelOutput object + """ return get_required( self.properties.get(TASKS_RESULT_PROP), self, TASKS_RESULT_PROP ) @@ -1011,6 +1234,9 @@ def tasks(self, v: list[TaskType]) -> None: @property def result(self) -> ResultStructure: + """ + Gets or sets the required results property of this ModelOutput object + """ return ResultStructure( get_required( self.properties.get(RESULT_RESULT_PROP), self, RESULT_RESULT_PROP @@ -1023,6 +1249,9 @@ def result(self, v: ResultStructure) -> None: @property def description(self) -> str | None: + """ + Gets or sets the description property of this ModelOutput object + """ return self.properties.get(DESCRIPTION_RESULT_PROP) @description.setter @@ -1034,6 +1263,9 @@ def description(self, v: str | None) -> None: @property def classes(self) -> list[Classification] | None: + """ + Gets or sets the classes property of this ModelOutput object + """ classes = self.properties.get(CLASSES_RESULT_PROP) return [Classification(c) for c in classes] if classes is not None else None @@ -1046,6 +1278,9 @@ def classes(self, v: list[Classification] | None) -> None: @property def post_processing_function(self) -> ProcessingExpression | None: + """ + Gets or sets the post_processing_function property of this ModelOutput object + """ v = self.properties.get(POST_PROCESSING_FUNCTION_RESULT_PROP) return ProcessingExpression(v) if v is not None else None @@ -1057,6 +1292,11 @@ def post_processing_function(self, v: ProcessingExpression | None) -> None: self.properties.pop(POST_PROCESSING_FUNCTION_RESULT_PROP, None) def to_dict(self) -> dict[str, Any]: + """ + Serializes this ModelOutput object into a dict + Returns: + dict[str, Any] + """ return self.properties @@ -1072,6 +1312,12 @@ def __eq__(self, other: object) -> bool: return self.properties == other.properties def apply(self, **kwargs: Any) -> None: + """ + Sets the properties for a new Hyperparameters object. The stac:mlm specification + defines neither their names nor their values, so any key-value pair is allowed. + Args: + **kwargs: any model hyperparameter name and its value, as key-value paris + """ self.properties.update(kwargs) @classmethod @@ -1081,6 +1327,11 @@ def create(cls, **kwargs: Any) -> Hyperparameters: return c def to_dict(self) -> dict[str, Any]: + """ + Serializes this Hyperparameters object into a dictionary + Returns: + dict[str, Any] + """ return self.properties @@ -1090,9 +1341,12 @@ class MLMExtension( ExtensionManagementMixin[pystac.Item | pystac.Collection], ): """An abstract class that can be used to extend to properties of an - :class:`pystac.Item` or :class:`pystac.Collection`with properties from the - :stac-ext:`Machine Learning Model Extension `. This class is generic over the - type of STAC Object to be extended. + :class:`pystac.Item` or :class:`pystac.Collection` with properties from the + :stac-ext:`Machine Learning Model Extension `. + + This class can be used to extend :class:`pystac.Item`, :class:`pystac.Collection` + and :class:`pystac.ItemAssetDefinition`. For extending :class:`pystac.Asset`, use + either :class:`~AssetNoPropsMLMExtension`: or :class:`AssetPropsMLMExtension`. """ name: Literal["mlm"] = "mlm" @@ -1120,6 +1374,39 @@ def apply( *args: Any, **kwargs: Any, ) -> None: + """ + Sets the properties of a new MLMExtension + + Args: + name: name for the model + architecture: A generic and well established architecture name of the model + tasks: Specifies the Machine Learning tasks for which the model can be + used for + input: Describes the transformation between the EO data and the model input + output: Describes each model output and how to interpret it. + framework: Framework used to train the model + framework_version: The ``framework`` library version + memory_size: The in-memory size of the model on the accelerator during + inference (bytes) + total_parameters: Total number of model parameters, including trainable and + non-trainable parameters. + pretrained: Indicates if the model was pretrained. If the model was + pretrained, consider providing ``pretrained_source`` if it is known + pretrained_source: The source of the pretraining. + batch_size_suggestion: A suggested batch size for the accelerator and + summarized hardware. + accelerator: The intended computational hardware that runs inference + accelerator_constrained: Indicates if the intended ``accelerator`` is the + only accelerator that can run inference + accelerator_summary: A high level description of the ``accelerator`` + accelerator_count: A minimum amount of ``accelerator`` instances required to + run the model + hyperparameters: Additional hyperparameters relevant for the model + *args: Unused (no effect, only here for signature compliance with apply + method in derived classes + **kwargs: Unused (no effect, only here for signature compliance with apply + method in derived classes + """ self.mlm_name = name self.architecture = architecture self.tasks = tasks @@ -1140,10 +1427,35 @@ def apply( @classmethod def get_schema_uri(cls) -> str: + """ + Retrieves this extension's schema URI + + Returns: + str: the schema URI + """ return SCHEMA_URI_PATTERN.format(version=DEFAULT_VERSION) @classmethod def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: + """ + Extend a STAC object (``obj``) with the MLMExtension + + Args: + obj: The STAC object to be extended. + add_if_missing: Defines whether this extension's URI should be added to + this object's (or its parent's) list of extensions if it is not already + listed there. + + Returns: + MLMExtension[pystac.extensions.mlm.T]: The extended object + + Raises: + pystac.STACError: When a :class:`pystac.Asset` object is apssed as the + `obj` parameter + pystac.ExtensionTypeError: When any unsupported object is passed as the + `obj` parameter. If you see this extension in this context, please + raise an issue on github. + """ if isinstance(obj, pystac.Item): cls.ensure_has_extension(obj, add_if_missing) return cast(MLMExtension[T], ItemMLMExtension(obj)) @@ -1164,6 +1476,10 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: @property def mlm_name(self) -> str: + """ + Get or set the required (mlm) name property. It is named mlm_name in this + context to not break convention and overwrite the extension name class property. + """ return get_required(self.properties.get(NAME_PROP), self, NAME_PROP) @mlm_name.setter @@ -1172,6 +1488,9 @@ def mlm_name(self, v: str) -> None: @property def architecture(self) -> str: + """ + Get or set the required architecture property + """ return get_required( self.properties.get(ARCHITECTURE_PROP), self, ARCHITECTURE_PROP ) @@ -1182,6 +1501,9 @@ def architecture(self, v: str) -> None: @property def tasks(self) -> list[TaskType]: + """ + Get or set the required tasks property + """ return get_required(self.properties.get(TASKS_PROP), self, TASKS_PROP) @tasks.setter @@ -1190,6 +1512,9 @@ def tasks(self, v: list[TaskType]) -> None: @property def framework(self) -> str | None: + """ + Get or set the framework property + """ return self._get_property(FRAMEWORK_PROP, str) @framework.setter @@ -1199,6 +1524,9 @@ def framework(self, v: str | None) -> None: @property def framework_version(self) -> str | None: + """ + Get or set the framework_version property + """ return self._get_property(FRAMEWORK_VERSION_PROP, str) @framework_version.setter @@ -1207,6 +1535,9 @@ def framework_version(self, v: str | None) -> None: @property def memory_size(self) -> int | None: + """ + Get or set the memory_size property + """ return self._get_property(MEMORY_SIZE_PROP, int) @memory_size.setter @@ -1215,6 +1546,9 @@ def memory_size(self, v: int | None) -> None: @property def total_parameters(self) -> int | None: + """ + Get or set the total_parameters property + """ return self._get_property(TOTAL_PARAMETERS_PROP, int) @total_parameters.setter @@ -1223,6 +1557,9 @@ def total_parameters(self, v: int | None) -> None: @property def pretrained(self) -> bool | None: + """ + Get or set the pretrained property + """ return self._get_property(PRETRAINED_PROP, bool) @pretrained.setter @@ -1231,6 +1568,9 @@ def pretrained(self, v: bool | None) -> None: @property def pretrained_source(self) -> str | None: + """ + Get or set the pretrained_source property + """ return self._get_property(PRETRAINED_SOURCE_PROP, str) @pretrained_source.setter @@ -1241,6 +1581,9 @@ def pretrained_source(self, v: str | None) -> None: @property def batch_size_suggestion(self) -> int | None: + """ + Get or set the batch_size_suggestion property + """ return self._get_property(BATCH_SIZE_SUGGESTION_PROP, int) @batch_size_suggestion.setter @@ -1249,6 +1592,9 @@ def batch_size_suggestion(self, v: int | None) -> None: @property def accelerator(self) -> AcceleratorType | None: + """ + Get or set the accelerator property + """ return self._get_property(ACCELERATOR_PROP, AcceleratorType) @accelerator.setter @@ -1258,6 +1604,9 @@ def accelerator(self, v: AcceleratorType | None) -> None: @property def accelerator_constrained(self) -> bool | None: + """ + Get or set the accelerator_constrianed property + """ return self._get_property(ACCELERATOR_CONSTRAINED_PROP, bool) @accelerator_constrained.setter @@ -1266,6 +1615,9 @@ def accelerator_constrained(self, v: bool | None) -> None: @property def accelerator_summary(self) -> str | None: + """ + Get or set the accelerator_summary property + """ return self._get_property(ACCELERATOR_SUMMARY_PROP, str) @accelerator_summary.setter @@ -1274,6 +1626,9 @@ def accelerator_summary(self, v: str | None) -> None: @property def accelerator_count(self) -> int | None: + """ + Get or set the accelerator_count property + """ return self._get_property(ACCELERATOR_COUNT_PROP, int) @accelerator_count.setter @@ -1282,6 +1637,9 @@ def accelerator_count(self, v: int | None) -> None: @property def input(self) -> list[ModelInput]: + """ + Get or set the required input property + """ return [ ModelInput(inp) for inp in get_required( @@ -1295,6 +1653,9 @@ def input(self, v: list[ModelInput]) -> None: @property def output(self) -> list[ModelOutput]: + """ + Get or set the required output property + """ return [ ModelOutput(outp) for outp in get_required( @@ -1308,6 +1669,9 @@ def output(self, v: list[ModelOutput]) -> None: @property def hyperparameters(self) -> Hyperparameters | None: + """ + Get or set the hyperparameters property + """ prop = self._get_property(HYPERPARAMETERS_PROP, dict[str, Any]) return Hyperparameters(prop) if prop is not None else None @@ -1316,6 +1680,12 @@ def hyperparameters(self, v: Hyperparameters | None) -> None: self._set_property(HYPERPARAMETERS_PROP, v.to_dict() if v is not None else None) def to_dict(self) -> dict[str, Any]: + """ + Serializes this MLMExtension object into a dict + + Returns: + dict[str, Any] + """ return self.properties @@ -1346,6 +1716,10 @@ def __repr__(self) -> str: class _AssetMLMExtension(ABC): + """ + Abstract base class for (derived) MLM asset extensions. + """ + asset: pystac.Asset asset_href: str properties: dict[str, Any] @@ -1368,14 +1742,30 @@ def _ext(cls: type[AssetExtensionType], obj: pystac.Asset) -> AssetExtensionType return cls(obj) def to_dict(self) -> dict[str, Any]: - return self.properties # todo: test this + """ + Serializes the extended asset's properties into a dict + + Returns: + dict[str, Any] + """ + return self.properties @classmethod def get_schema_uri(cls) -> str: + """ + Retrieve this extension's schema URI + + Returns: + str: Schema URI + """ return SCHEMA_URI_PATTERN.format(version=DEFAULT_VERSION) @property def artifact_type(self) -> str | None: + """ + Get or set the artifact_type property. This is required if ``mlm:model`` as one + of the asset's roles + """ prop_value = self.properties.get(ARTIFACT_TYPE_ASSET_PROP) if isinstance(self.asset.roles, list) and "mlm:model" in self.asset.roles: return get_required(prop_value, self, ARTIFACT_TYPE_ASSET_PROP) @@ -1401,6 +1791,9 @@ def artifact_type(self, v: str | None) -> None: @property def compile_method(self) -> str | None: + """ + Get or set this asset's compile_method property + """ return self.properties.get(COMPILE_MDTHOD_ASSET_PROP) @compile_method.setter @@ -1412,6 +1805,9 @@ def compile_method(self, v: str | None) -> None: @property def entrypoint(self) -> str | None: + """ + Get or set this asset's entrypoint property asdfasdf + """ return self.properties.get(ENTRYPOITN_ASSET_PROP) @entrypoint.setter @@ -1437,6 +1833,18 @@ def apply( compile_method: str | None = None, entrypoint: str | None = None, ) -> None: + """ + Sets the properties of a new AssetNoPropsMLMExtension + + Args: + artifact_type: Specifies the kind of model artifact, any string is allowed. + Typically related to a particular ML framework. This property is + required when ``mlm:model`` is listed as a role of this asset + compile_method: Describes the method used to compile the ML model either + when the model is saved or at model runtime prior to inference + entrypoint: Specific entrypoint reference in the code to use for running + model inference. + """ self.artifact_type = artifact_type self.compile_method = compile_method self.entrypoint = entrypoint @@ -1445,6 +1853,18 @@ def apply( def ext( cls, obj: pystac.Asset, add_if_missing: bool = False ) -> AssetNoPropsMLMExtension[pystac.Asset]: + """ + Extend a :class:`pystac.Asset` (``obj``) with the AssetNoPropsMLMExtension + + Args: + obj: The Asset to be extended. + add_if_missing: Defines whether this extension's URI should be added to + this asset's parent's list of extensions if it is not already + listed there. Use ``False`` if the asset does not specify a parent + + Returns: + AssetNoPropsMLMExtension[pystac.Asset]: The extended object + """ cls.ensure_owner_has_extension(obj, add_if_missing) return AssetNoPropsMLMExtension._ext(obj) @@ -1457,6 +1877,18 @@ def __repr__(self) -> str: def ext( cls, obj: pystac.Asset, add_if_missing: bool = False ) -> AssetPropsMLMExtension: + """ + Extend a :class:`pystac.Asset` (``obj``) with the AssetNoPropsMLMExtension + + Args: + obj: The Asset to be extended. + add_if_missing: Defines whether this extension's URI should be added to + this asset's parent's list of extensions if it is not already + listed there. Use ``False`` if the asset does not specify a parent + + Returns: + AssetPropsMLMExtension[pystac.Asset]: The extended object + """ cls.ensure_owner_has_extension(obj, add_if_missing) return AssetPropsMLMExtension._ext(obj) @@ -1483,6 +1915,42 @@ def apply( compile_method: str | None = None, entrypoint: str | None = None, ) -> None: + """ + Sets the properties of a new MLMExtension + + Args: + name: name for the model + architecture: A generic and well established architecture name of the model + tasks: Specifies the Machine Learning tasks for which the model can be + used for + input: Describes the transformation between the EO data and the model input + output: Describes each model output and how to interpret it. + framework: Framework used to train the model + framework_version: The ``framework`` library version + memory_size: The in-memory size of the model on the accelerator during + inference (bytes) + total_parameters: Total number of model parameters, including trainable and + non-trainable parameters. + pretrained: Indicates if the model was pretrained. If the model was + pretrained, consider providing ``pretrained_source`` if it is known + pretrained_source: The source of the pretraining. + batch_size_suggestion: A suggested batch size for the accelerator and + summarized hardware. + accelerator: The intended computational hardware that runs inference + accelerator_constrained: Indicates if the intended ``accelerator`` is the + only accelerator that can run inference + accelerator_summary: A high level description of the ``accelerator`` + accelerator_count: A minimum amount of ``accelerator`` instances required to + run the model + hyperparameters: Additional hyperparameters relevant for the model + artifact_type: Specifies the kind of model artifact, any string is allowed. + Typically related to a particular ML framework. This property is + required when ``mlm:model`` is listed as a role of this asset + compile_method: Describes the method used to compile the ML model either + when the model is saved or at model runtime prior to inference + entrypoint: Specific entrypoint reference in the code to use for running + model inference. + """ MLMExtension.apply( self, name=name, From 478580a96a06450ae6ac6fa5110550d3d08c183e Mon Sep 17 00:00:00 2001 From: = Date: Wed, 2 Apr 2025 11:53:57 +0200 Subject: [PATCH 09/68] added docstring to classes --- pystac/extensions/mlm.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index f694bd8a0..42f4b591e 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1827,6 +1827,18 @@ class AssetNoPropsMLMExtension( PropertiesExtension, ExtensionManagementMixin[pystac.Item | pystac.Collection], ): + """A class that can be used to extend the properties of an + :class:`pystac.Asset` object with properties from the + :stac-ext:`Machine Learning Model Extension `. + + Use this class, if model metadata is provided by by the asset's parent object + (i.e. :class:`pystac.Item` or :class:`pystac.Item`. If Model metadata is provided + by the asset object itself, use :class:`AssetPropsMLMExtension`. + + For extending :class:`pystac.Item`, :class:`pystac.Collection` or + :class:`pystac.ItemAssetDefinition` objects, use :class:`MLMExtension` instead. + """ + def apply( self, artifact_type: str | None = None, @@ -1870,6 +1882,19 @@ def ext( class AssetPropsMLMExtension(_AssetMLMExtension, MLMExtension[pystac.Asset]): + """A class that can be used to extend the properties of an + :class:`pystac.Asset` object with properties from the + :stac-ext:`Machine Learning Model Extension `. + + Use this class, if model metadata is provided by the asset. If model metadata is + provided by the asset's parent object + (i.e. :class:`pystac.Item` or :class:`pystac.Item`, use + :class:`AssetPropsMLMExtension`. + + For extending :class:`pystac.Item`, :class:`pystac.Collection` or + :class:`pystac.ItemAssetDefinition` objects, use :class:`MLMExtension` instead. + """ + def __repr__(self) -> str: return f"" From c74d544c651213d1e01dd9790728db9c696dc8aa Mon Sep 17 00:00:00 2001 From: = Date: Wed, 2 Apr 2025 12:20:48 +0200 Subject: [PATCH 10/68] renamed asset extension classes --- pystac/extensions/mlm.py | 34 +++++++++++++++++----------------- tests/extensions/test_mlm.py | 16 ++++++++-------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 42f4b591e..51b6f4abf 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1346,7 +1346,7 @@ class MLMExtension( This class can be used to extend :class:`pystac.Item`, :class:`pystac.Collection` and :class:`pystac.ItemAssetDefinition`. For extending :class:`pystac.Asset`, use - either :class:`~AssetNoPropsMLMExtension`: or :class:`AssetPropsMLMExtension`. + either :class:`~AssetGeneralMLMExtension`: or :class:`AssetDetailedMLMExtension`. """ name: Literal["mlm"] = "mlm" @@ -1468,8 +1468,8 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: elif isinstance(obj, pystac.Asset): raise pystac.STACError( "This class cannot be used to extend STAC objects of type Assets. " - "To extend Asset objects, use either AssetNoPropsMLMExtension or " - "AssetMLMExtension" + "To extend Asset objects, use either AssetGeneralMLMExtension or " + "AssetDetailedMLMExtension" ) else: raise pystac.ExtensionTypeError(cls._ext_error_message(obj)) @@ -1821,7 +1821,7 @@ def __repr__(self) -> str: return f"" -class AssetNoPropsMLMExtension( +class AssetGeneralMLMExtension( _AssetMLMExtension, Generic[T], PropertiesExtension, @@ -1833,7 +1833,7 @@ class AssetNoPropsMLMExtension( Use this class, if model metadata is provided by by the asset's parent object (i.e. :class:`pystac.Item` or :class:`pystac.Item`. If Model metadata is provided - by the asset object itself, use :class:`AssetPropsMLMExtension`. + by the asset object itself, use :class:`AssetDetailedMLMExtension`. For extending :class:`pystac.Item`, :class:`pystac.Collection` or :class:`pystac.ItemAssetDefinition` objects, use :class:`MLMExtension` instead. @@ -1846,7 +1846,7 @@ def apply( entrypoint: str | None = None, ) -> None: """ - Sets the properties of a new AssetNoPropsMLMExtension + Sets the properties of a new AssetGeneralMLMExtension Args: artifact_type: Specifies the kind of model artifact, any string is allowed. @@ -1864,9 +1864,9 @@ def apply( @classmethod def ext( cls, obj: pystac.Asset, add_if_missing: bool = False - ) -> AssetNoPropsMLMExtension[pystac.Asset]: + ) -> AssetGeneralMLMExtension[pystac.Asset]: """ - Extend a :class:`pystac.Asset` (``obj``) with the AssetNoPropsMLMExtension + Extend a :class:`pystac.Asset` (``obj``) with the AssetGeneralMLMExtension Args: obj: The Asset to be extended. @@ -1875,13 +1875,13 @@ def ext( listed there. Use ``False`` if the asset does not specify a parent Returns: - AssetNoPropsMLMExtension[pystac.Asset]: The extended object + AssetGeneralMLMExtension[pystac.Asset]: The extended object """ cls.ensure_owner_has_extension(obj, add_if_missing) - return AssetNoPropsMLMExtension._ext(obj) + return AssetGeneralMLMExtension._ext(obj) -class AssetPropsMLMExtension(_AssetMLMExtension, MLMExtension[pystac.Asset]): +class AssetDetailedMLMExtension(_AssetMLMExtension, MLMExtension[pystac.Asset]): """A class that can be used to extend the properties of an :class:`pystac.Asset` object with properties from the :stac-ext:`Machine Learning Model Extension `. @@ -1889,7 +1889,7 @@ class AssetPropsMLMExtension(_AssetMLMExtension, MLMExtension[pystac.Asset]): Use this class, if model metadata is provided by the asset. If model metadata is provided by the asset's parent object (i.e. :class:`pystac.Item` or :class:`pystac.Item`, use - :class:`AssetPropsMLMExtension`. + :class:`AssetGeneralMLMExtension`. For extending :class:`pystac.Item`, :class:`pystac.Collection` or :class:`pystac.ItemAssetDefinition` objects, use :class:`MLMExtension` instead. @@ -1901,9 +1901,9 @@ def __repr__(self) -> str: @classmethod def ext( cls, obj: pystac.Asset, add_if_missing: bool = False - ) -> AssetPropsMLMExtension: + ) -> AssetDetailedMLMExtension: """ - Extend a :class:`pystac.Asset` (``obj``) with the AssetNoPropsMLMExtension + Extend a :class:`pystac.Asset` (``obj``) with the AssetDetailedMLMExtension Args: obj: The Asset to be extended. @@ -1912,10 +1912,10 @@ def ext( listed there. Use ``False`` if the asset does not specify a parent Returns: - AssetPropsMLMExtension[pystac.Asset]: The extended object + AssetDetailedMLMExtension[pystac.Asset]: The extended object """ cls.ensure_owner_has_extension(obj, add_if_missing) - return AssetPropsMLMExtension._ext(obj) + return AssetDetailedMLMExtension._ext(obj) def apply( self, @@ -1941,7 +1941,7 @@ def apply( entrypoint: str | None = None, ) -> None: """ - Sets the properties of a new MLMExtension + Sets the properties of a new AssetDetailedMLMExtensions Args: name: name for the model diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 830e3c491..3cb9db3e4 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -14,8 +14,8 @@ NAME_PROP, TASKS_PROP, AcceleratorType, - AssetNoPropsMLMExtension, - AssetPropsMLMExtension, + AssetDetailedMLMExtension, + AssetGeneralMLMExtension, Hyperparameters, InputStructure, ItemMLMExtension, @@ -702,7 +702,7 @@ def test_add_to_asset(plain_item: Item) -> None: assert ARCHITECTURE_PROP not in asset.extra_fields.keys() assert TASKS_PROP not in asset.extra_fields.keys() - asset_ext = AssetPropsMLMExtension.ext(asset) + asset_ext = AssetDetailedMLMExtension.ext(asset) asset_ext.mlm_name = "asdf" asset_ext.architecture = "ResNet" asset_ext.tasks = [TaskType.CLASSIFICATION] @@ -719,7 +719,7 @@ def test_add_to_asset(plain_item: Item) -> None: @pytest.mark.parametrize("is_model_asset", (True, False)) def test_asset_props(plain_item: Item, is_model_asset: bool) -> None: asset = plain_item.assets["analytic"] - asset_ext = AssetNoPropsMLMExtension.ext(asset, add_if_missing=True) + asset_ext = AssetGeneralMLMExtension.ext(asset, add_if_missing=True) assert asset_ext.artifact_type is None assert asset_ext.compile_method is None @@ -753,7 +753,7 @@ def test_add_to_generic_asset() -> None: "mlm:entrypoint": "baz", }, ) - asset_ext = AssetNoPropsMLMExtension.ext(asset, add_if_missing=False) + asset_ext = AssetGeneralMLMExtension.ext(asset, add_if_missing=False) assert asset_ext.artifact_type == "foo" assert asset_ext.compile_method == "bar" assert asset_ext.entrypoint == "baz" @@ -767,7 +767,7 @@ def test_apply_generic_asset() -> None: media_type="application/tiff", roles=["mlm:model"], ) - asset_ext = AssetNoPropsMLMExtension.ext(asset, add_if_missing=False) + asset_ext = AssetGeneralMLMExtension.ext(asset, add_if_missing=False) asset_ext.apply(artifact_type="foo", compile_method="bar", entrypoint="baz") assert asset_ext.artifact_type == "foo" assert asset_ext.compile_method == "bar" @@ -808,7 +808,7 @@ def test_add_to_detailled_asset() -> None: }, ) - asset_ext = AssetPropsMLMExtension.ext(asset, add_if_missing=False) + asset_ext = AssetDetailedMLMExtension.ext(asset, add_if_missing=False) assert asset_ext.mlm_name == "asdf" assert asset_ext.architecture == "ResNet" @@ -828,7 +828,7 @@ def test_apply_detailled_asset() -> None: media_type="application/tiff", roles=["mlm:model"], ) - asset_ext = AssetPropsMLMExtension.ext(asset, add_if_missing=False) + asset_ext = AssetDetailedMLMExtension.ext(asset, add_if_missing=False) model_input = ModelInput.create( name="model", From 1af5e3ef2ba5ed649205e14aa6568c4c4115cf51 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 2 Apr 2025 12:25:06 +0200 Subject: [PATCH 11/68] added mlm extension classes --- docs/api/extensions.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api/extensions.rst b/docs/api/extensions.rst index 44cc8af32..ce37d3215 100644 --- a/docs/api/extensions.rst +++ b/docs/api/extensions.rst @@ -20,6 +20,9 @@ pystac.extensions grid.GridExtension item_assets.ItemAssetsExtension mgrs.MgrsExtension + mlm.MLMExtension + mlm.AssetGeneralMLMExtension + mlm.AssetDetailedMLMExtension pointcloud.PointcloudExtension projection.ProjectionExtension raster.RasterExtension From a3bec040ec2efe03242b8764ce84eaa90dfcdd2f Mon Sep 17 00:00:00 2001 From: = Date: Wed, 2 Apr 2025 15:22:55 +0200 Subject: [PATCH 12/68] added mlm extension --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03c8f49c0..bca80b4ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - `Collection.from_items` for creating a `pystac.Collection` from an `ItemCollection` ([#1522](https://github.com/stac-utils/pystac/pull/1522)) +- `extensions.mlm` for supporting the [MLM](https://github.com/stac-extensions/mlm) extension ([1542](https://github.com/stac-utils/pystac/pull/1542)) ### Fixed - fixed missing parameter "title" in pystac.extensions.classification.Classification ([#1539](https://github.com/stac-utils/pystac/pull/1539)) From 46516ea9199ff96e10866d21ffc3d03346988ae4 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 2 Apr 2025 15:55:29 +0200 Subject: [PATCH 13/68] removed todos --- pystac/extensions/mlm.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 51b6f4abf..f8744b29c 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -27,10 +27,9 @@ ) AssetExtensionType = TypeVar("AssetExtensionType", bound="_AssetMLMExtension") -# todo: support multiple version? SCHEMA_URI_PATTERN: str = "https://stac-extensions.github.io/mlm/v{version}/schema.json" DEFAULT_VERSION: str = "1.4.0" -SUPPORTED_VERSIONS: list[str] = ["1.4.0"] +SUPPORTED_VERSIONS: list[str] = ["1.0.0", "1.1.0", "1.2.0", "1.3.0", "1.4.0"] PREFIX: str = "mlm:" @@ -659,7 +658,7 @@ def __eq__(self, other: object) -> bool: def apply( self, shape: list[int], - dim_order: list[str], # todo: make Dimension Enum? + dim_order: list[str], data_type: DataType, ) -> None: """ @@ -1519,7 +1518,6 @@ def framework(self) -> str | None: @framework.setter def framework(self, v: str | None) -> None: - # todo: make enum? self._set_property(FRAMEWORK_PROP, v) @property From 56f0ed071a847d771202d29964b02ae82773d53c Mon Sep 17 00:00:00 2001 From: = Date: Wed, 2 Apr 2025 17:21:25 +0200 Subject: [PATCH 14/68] added extension migration --- pystac/__init__.py | 2 ++ pystac/extensions/mlm.py | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/pystac/__init__.py b/pystac/__init__.py index c2a5b53b1..1fbbb8343 100644 --- a/pystac/__init__.py +++ b/pystac/__init__.py @@ -100,6 +100,7 @@ warnings.filterwarnings("ignore", category=DeprecationWarning) import pystac.extensions.label import pystac.extensions.mgrs +import pystac.extensions.mlm import pystac.extensions.pointcloud import pystac.extensions.projection import pystac.extensions.raster @@ -123,6 +124,7 @@ pystac.extensions.item_assets.ITEM_ASSETS_EXTENSION_HOOKS, pystac.extensions.label.LABEL_EXTENSION_HOOKS, pystac.extensions.mgrs.MGRS_EXTENSION_HOOKS, + pystac.extensions.mlm.MLM_EXTENSION_HOOKS, pystac.extensions.pointcloud.POINTCLOUD_EXTENSION_HOOKS, pystac.extensions.projection.PROJECTION_EXTENSION_HOOKS, pystac.extensions.raster.RASTER_EXTENSION_HOOKS, diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index f8744b29c..b6cd3b61d 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -19,7 +19,9 @@ PropertiesExtension, ) from pystac.extensions.classification import Classification +from pystac.extensions.hooks import ExtensionHooks from pystac.extensions.raster import DataType +from pystac.serialization.identify import STACJSONDescription, STACVersionID from pystac.utils import StringEnum, get_required T = TypeVar( @@ -1718,6 +1720,7 @@ class _AssetMLMExtension(ABC): Abstract base class for (derived) MLM asset extensions. """ + name: Literal["mlm"] = "mlm" asset: pystac.Asset asset_href: str properties: dict[str, Any] @@ -2009,3 +2012,41 @@ def __init__(self, item_asset: pystac.ItemAssetDefinition): def __repr__(self) -> str: return f" None: + # migrate from 1.0.0 to 1.1.0 + if SCHEMA_URI_PATTERN.format(version="1.0.0") in info.extensions: + # no migrations needed + pass + + # migrate from 1.1.0 to 1.2.0 + if SCHEMA_URI_PATTERN.format(version="1.1.0") in info.extensions: + # no migrations needed + pass + + # migrate from 1.2.0 to 1.3.0 + if SCHEMA_URI_PATTERN.format(version="1.2.0") in info.extensions: + # no migration needed + pass + + # migrate from 1.3.0 to 1.4.0 + if SCHEMA_URI_PATTERN.format(version="1.3.0") in info.extensions: + # no migration needed + pass + + super().migrate(obj, version, info) + + +MLM_EXTENSION_HOOKS: ExtensionHooks = MLMExtensionHooks() From 10bbb41b10f9ba7d7018d391a300a93f3ba55672 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 3 Apr 2025 10:00:12 +0200 Subject: [PATCH 15/68] removed useless code --- pystac/extensions/mlm.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index b6cd3b61d..a53b3e723 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2026,26 +2026,8 @@ class MLMExtensionHooks(ExtensionHooks): def migrate( self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription ) -> None: - # migrate from 1.0.0 to 1.1.0 - if SCHEMA_URI_PATTERN.format(version="1.0.0") in info.extensions: - # no migrations needed - pass - - # migrate from 1.1.0 to 1.2.0 - if SCHEMA_URI_PATTERN.format(version="1.1.0") in info.extensions: - # no migrations needed - pass - - # migrate from 1.2.0 to 1.3.0 - if SCHEMA_URI_PATTERN.format(version="1.2.0") in info.extensions: - # no migration needed - pass - - # migrate from 1.3.0 to 1.4.0 - if SCHEMA_URI_PATTERN.format(version="1.3.0") in info.extensions: - # no migration needed - pass - + # mo adjustments to objects needed when migrating yet + # schema back to v1.0 is fully backwards compatible super().migrate(obj, version, info) From ceec5f107f99a6a56561e5be9aa2732a25a95eab Mon Sep 17 00:00:00 2001 From: = Date: Thu, 3 Apr 2025 10:58:46 +0200 Subject: [PATCH 16/68] fixed docstrings --- pystac/extensions/mlm.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index a53b3e723..d5f6520a2 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -24,6 +24,8 @@ from pystac.serialization.identify import STACJSONDescription, STACVersionID from pystac.utils import StringEnum, get_required +#: Generalized version of :class:`pystac.Item`, :class:`pystac.ItemAssetDefinition`, +#: :class:`pystac.Collection`, or :class:`pystac.Asset` T = TypeVar( "T", pystac.Item, pystac.ItemAssetDefinition, pystac.Collection, pystac.Asset ) @@ -1186,12 +1188,13 @@ def create( for. This can be a subset of mlm:tasks defined under the Item properties as applicable. result: The structure that describes the resulting output arrays/tensors - from one model head. description: Additional details about the output such - as describing its purpose or expected result that cannot be represented - by other properties. + from one model head. description: Additional details about the output + such as describing its purpose or expected result that cannot be + represented by other properties. classes: A list of class objects adhering to the Classification Extension. post_processing_function: Custom postprocessing function where - normalization, rescaling, or any other significant operations takes place. + normalization, rescaling, or any other significant operations takes + place. Returns: ModelOutput @@ -1448,7 +1451,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: listed there. Returns: - MLMExtension[pystac.extensions.mlm.T]: The extended object + MLMExtension[T]: The extended object Raises: pystac.STACError: When a :class:`pystac.Asset` object is apssed as the From 26b221cea9469128c5d4b2a6411eec9cf44c3764 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 3 Apr 2025 12:00:48 +0200 Subject: [PATCH 17/68] fixed __eq__ NotImplemented --- pystac/extensions/mlm.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index d5f6520a2..d448357ac 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -181,7 +181,7 @@ def __init__(self, properties: dict[str, Any]): def __eq__(self, other: object) -> bool: if not isinstance(other, ModelBand): - return NotImplemented + raise NotImplementedError return ( self.name == other.name and self.format == other.format @@ -284,7 +284,7 @@ def __init__(self, properties: dict[str, Any]): def __eq__(self, other: object) -> bool: if not isinstance(other, ProcessingExpression): - return NotImplemented + raise NotImplementedError else: return self.format == other.format and self.expression == other.expression @@ -367,7 +367,7 @@ def __init__(self, properties: dict[str, Any]): def __eq__(self, other: object) -> bool: if not isinstance(other, ValueScaling): - return NotImplemented + raise NotImplementedError return ( self.type == other.type and self.minimum == other.minimum @@ -652,7 +652,7 @@ def __init__(self, properties: dict[str, Any]): def __eq__(self, other: object) -> bool: if not isinstance(other, InputStructure): - return NotImplemented + raise NotImplementedError return ( self.shape == other.shape and self.dim_order == other.dim_order @@ -768,7 +768,7 @@ def __init__(self, properties: dict[str, Any]): def __eq__(self, other: object) -> bool: if not isinstance(other, ModelInput): - return NotImplemented + raise NotImplementedError return ( self.name == other.name and self.bands == other.bands @@ -1010,7 +1010,7 @@ def __init__(self, properties: dict[str, Any]): def __eq__(self, other: object) -> bool: if not isinstance(other, ResultStructure): - return NotImplemented + raise NotImplementedError return ( self.shape == other.shape and self.dim_order == other.dim_order @@ -1121,7 +1121,7 @@ def __init__(self, properties: dict[str, Any]): def __eq__(self, other: object) -> bool: if not isinstance(other, ModelOutput): - return NotImplemented + raise NotImplementedError return ( self.name == other.name and self.tasks == other.tasks @@ -1312,7 +1312,7 @@ def __init__(self, properties: dict[str, Any]): def __eq__(self, other: object) -> bool: if not isinstance(other, Hyperparameters): - return NotImplemented + raise NotImplementedError return self.properties == other.properties def apply(self, **kwargs: Any) -> None: From 3cefe9face5044e9725814eb86a51aff73c28cd9 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 3 Apr 2025 12:21:57 +0200 Subject: [PATCH 18/68] fixed repr strings --- pystac/extensions/mlm.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index d448357ac..446052c24 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1715,7 +1715,7 @@ def __init__(self, collection: pystac.Collection): self.links = collection.links def __repr__(self) -> str: - return f"" + return f"" class _AssetMLMExtension(ABC): @@ -1821,9 +1821,6 @@ def entrypoint(self, v: str | None) -> None: else: self.properties.pop(ENTRYPOITN_ASSET_PROP, None) - def __repr__(self) -> str: - return f"" - class AssetGeneralMLMExtension( _AssetMLMExtension, @@ -1884,6 +1881,9 @@ def ext( cls.ensure_owner_has_extension(obj, add_if_missing) return AssetGeneralMLMExtension._ext(obj) + def __repr__(self) -> str: + return f"" + class AssetDetailedMLMExtension(_AssetMLMExtension, MLMExtension[pystac.Asset]): """A class that can be used to extend the properties of an @@ -1900,7 +1900,7 @@ class AssetDetailedMLMExtension(_AssetMLMExtension, MLMExtension[pystac.Asset]): """ def __repr__(self) -> str: - return f"" + return f"" @classmethod def ext( From bd81c786b332d1de980651f3b88d2a65d62db49f Mon Sep 17 00:00:00 2001 From: = Date: Thu, 3 Apr 2025 14:40:40 +0200 Subject: [PATCH 19/68] improved test coverage --- tests/extensions/test_mlm.py | 188 +++++++++++++++++++++++++++++++---- 1 file changed, 169 insertions(+), 19 deletions(-) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 3cb9db3e4..e6a7ef878 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -75,6 +75,9 @@ def test_model_band() -> None: assert c.to_dict() == d + with pytest.raises(NotImplementedError): + _ = c == "blah" + def test_model_props() -> None: c = ModelBand({}) @@ -100,6 +103,9 @@ def test_processing_expression() -> None: assert c.to_dict() == d + with pytest.raises(NotImplementedError): + _ = c == "blah" + def test_processint_expression_props() -> None: c = ProcessingExpression({}) @@ -116,25 +122,43 @@ def test_processint_expression_props() -> None: assert c.expression == "B01 + B02" -def test_valuescaling_object() -> None: +@pytest.mark.parametrize( + "scale_type, min_val, max_val, mean, stddev, value, format_val, expression", + [ + (ValueScalingType.MIN_MAX, 0, 4, 3, 3, 4, "asdf", "asdf"), + (ValueScalingType.MIN_MAX, 0.2, 4.3, 3.13, 3.2, 4.5, "asdf", "asdf"), + (ValueScalingType.MIN_MAX, 0, 4, None, None, None, None, None), + (ValueScalingType.SCALE, None, None, None, None, 2, None, None), + ], +) +def test_valuescaling_object( + scale_type: ValueScalingType, + min_val: int | float | None, + max_val: int | float | None, + mean: int | float | None, + stddev: int | float | None, + value: int | float | None, + format_val: str | None, + expression: str | None, +) -> None: c = ValueScaling.create( - ValueScalingType.MIN_MAX, - minimum=0, - maximum=4, - mean=3, - stddev=3.141, - value=4, - format="asdf", - expression="asdf", - ) - assert c.type == ValueScalingType.MIN_MAX - assert c.minimum == 0 - assert c.maximum == 4 - assert c.mean == 3 - assert c.stddev == 3.141 - assert c.value == 4 - assert c.format == "asdf" - assert c.expression == "asdf" + scale_type, + minimum=min_val, + maximum=max_val, + mean=mean, + stddev=stddev, + value=value, + format=format_val, + expression=expression, + ) + assert c.type == scale_type + assert c.minimum == min_val + assert c.maximum == max_val + assert c.mean == mean + assert c.stddev == stddev + assert c.value == value + assert c.format == format_val + assert c.expression == expression with pytest.raises(STACError): ValueScaling.create( @@ -144,6 +168,9 @@ def test_valuescaling_object() -> None: with pytest.raises(STACError): ValueScaling.create(ValueScalingType.Z_SCORE, mean=3) # missing param stddev + with pytest.raises(NotImplementedError): + _ = c == "blah" + def test_valuescaling_required_params() -> None: assert ValueScaling.get_required_props(ValueScalingType.MIN_MAX) == [ @@ -178,6 +205,9 @@ def test_input_structure() -> None: assert c.dim_order == ["batch", "channel", "width", "height"] assert c.data_type == DataType.FLOAT64 + with pytest.raises(NotImplementedError): + _ = c == "blah" + def test_model_input_structure_props() -> None: c = InputStructure({}) @@ -269,6 +299,9 @@ def test_model_input( assert "resize_type" in d_reverse assert "pre_processing_function" in d_reverse + with pytest.raises(NotImplementedError): + _ = c == "blah" + def test_model_input_props() -> None: c = ModelInput({}) @@ -314,6 +347,9 @@ def test_result_structure() -> None: assert c.dim_order == ["time", "width", "height"] assert c.data_type == DataType.FLOAT64 + with pytest.raises(NotImplementedError): + _ = c == "blah" + def test_result_structure_props() -> None: c = ResultStructure({}) @@ -361,6 +397,9 @@ def test_model_output(post_proc_func: ProcessingExpression | None) -> None: ] assert c.post_processing_function == post_proc_func + with pytest.raises(NotImplementedError): + _ = c == "blah" + def test_model_output_props() -> None: c = ModelOutput({}) @@ -408,6 +447,9 @@ def test_hyperparameters() -> None: assert key in c.to_dict() assert c.to_dict()[key] == d[key] + with pytest.raises(NotImplementedError): + _ = c == "blah" + def teest_get_schema_uri(basic_mlm_item: Item) -> None: with pytest.raises(DeprecationWarning): @@ -571,6 +613,34 @@ def test_apply(plain_item: Item) -> None: and MLMExtension.ext(plain_item).hyperparameters == hyp ) + d = { + **plain_item.properties, + "mlm:name": "asdf", + "mlm:architecture": "ResNet", + "mlm:tasks": [TaskType.CLASSIFICATION], + "mlm:framework": "PyTorch", + "mlm:framework_version": "1.2.3", + "mlm:memory_size": 3, + "mlm:total_parameters": 123, + "mlm:pretrained": True, + "mlm:pretrained_source": "asdfasdfasdf", + "mlm:batch_size_suggestion": 32, + "mlm:accelerator": AcceleratorType.CUDA, + "mlm:accelerator_constrained": False, + "mlm:accelerator_summary": "This is the summary", + "mlm:accelerator_count": 1, + "mlm:input": [inp.to_dict() for inp in model_input], + "mlm:output": [out.to_dict() for out in model_output], + "mlm:hyperparameters": hyp.to_dict(), + } + + assert MLMExtension.ext(plain_item).to_dict() == d + + +def test_apply_wrong_object() -> None: + with pytest.raises(pystac.ExtensionTypeError): + _ = MLMExtension.ext(1, False) + def test_to_from_dict(basic_item_dict: dict[str, Any]) -> None: d1 = deepcopy(basic_item_dict) @@ -774,6 +844,25 @@ def test_apply_generic_asset() -> None: assert asset_ext.entrypoint == "baz" +def test_to_dict_asset_generic() -> None: + asset = pystac.Asset( + href="http://example.com/test.tiff", + title="image", + description="asdf", + media_type="application/tiff", + roles=["mlm:model"], + ) + asset_ext = AssetGeneralMLMExtension.ext(asset, add_if_missing=False) + asset_ext.apply(artifact_type="foo", compile_method="bar", entrypoint="baz") + + d = { + "mlm:artifact_type": "foo", + "mlm:compile_method": "bar", + "mlm:entrypoint": "baz", + } + assert asset_ext.to_dict() == d + + def test_add_to_detailled_asset() -> None: model_input = ModelInput.create( name="model", @@ -819,6 +908,8 @@ def test_add_to_detailled_asset() -> None: assert asset_ext.compile_method == "bar" assert asset_ext.entrypoint == "baz" + assert repr(asset_ext) == f"" + def test_apply_detailled_asset() -> None: asset = pystac.Asset( @@ -866,13 +957,69 @@ def test_apply_detailled_asset() -> None: assert asset_ext.entrypoint == "baz" +def test_to_dict_detailed_asset() -> None: + asset = pystac.Asset( + href="http://example.com/test.tiff", + title="image", + description="asdf", + media_type="application/tiff", + roles=["mlm:model"], + ) + asset_ext = AssetDetailedMLMExtension.ext(asset, add_if_missing=False) + + model_input = ModelInput.create( + name="model", + bands=["B02"], + input=InputStructure.create( + shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 + ), + ) + model_output = ModelOutput.create( + name="output", + tasks=[TaskType.CLASSIFICATION], + result=ResultStructure.create( + shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 + ), + ) + + asset_ext.apply( + "asdf", + "ResNet", + [TaskType.CLASSIFICATION], + [model_input], + [model_output], + artifact_type="foo", + compile_method="bar", + entrypoint="baz", + ) + + d = { + "mlm:name": "asdf", + "mlm:architecture": "ResNet", + "mlm:tasks": [TaskType.CLASSIFICATION], + "mlm:input": [model_input.to_dict()], + "mlm:output": [model_output.to_dict()], + "mlm:artifact_type": "foo", + "mlm:compile_method": "bar", + "mlm:entrypoint": "baz", + "mlm:accelerator": None, + "mlm:pretrained_source": None, + } + assert asset_ext.to_dict() == d + + def test_item_asset_extension(mlm_collection: Collection) -> None: assert mlm_collection.item_assets item_asset = mlm_collection.item_assets["weights"] - MLMExtension.ext(item_asset, add_if_missing=True) + item_asset_ext = MLMExtension.ext(item_asset, add_if_missing=True) assert MLMExtension.get_schema_uri() in mlm_collection.stac_extensions assert mlm_collection.item_assets["weights"].ext.has("mlm") + assert ( + repr(item_asset_ext) + == f" None: coll_ext = MLMExtension.ext(mlm_collection, add_if_missing=True) @@ -881,6 +1028,9 @@ def test_collection_extension(mlm_collection: Collection) -> None: coll_ext.mlm_name = "asdf" assert coll_ext.mlm_name == "asdf" + assert ( + repr(coll_ext) == f"" + ) def test_raise_exception_on_mlm_extension_and_asset() -> None: From 28dc4b3116dad38495444cb52fec3f1c8d63c584 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 3 Apr 2025 15:06:37 +0200 Subject: [PATCH 20/68] improved test coverage --- tests/extensions/test_mlm.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index e6a7ef878..12750e574 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -910,6 +910,19 @@ def test_add_to_detailled_asset() -> None: assert repr(asset_ext) == f"" + with pytest.raises(pystac.errors.RequiredPropertyMissing): + asset_ext.artifact_type = None + + asset_ext.compile_method = None + assert asset_ext.compile_method is None + + asset_ext.entrypoint = None + assert asset_ext.entrypoint is None + + asset.roles.remove("mlm:model") if isinstance(asset.roles, list) else None + asset_ext.artifact_type = None + assert asset_ext.artifact_type is None + def test_apply_detailled_asset() -> None: asset = pystac.Asset( From 2fc681eed02a95203ac7660aee0a6ca673fd9150 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 3 Apr 2025 15:27:33 +0200 Subject: [PATCH 21/68] changed exceptions --- pystac/extensions/mlm.py | 8 ++++---- tests/extensions/test_mlm.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 446052c24..cb80b89ad 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -912,7 +912,7 @@ def bands(self) -> list[ModelBand] | list[str]: elif isinstance(bands, list) and all(isinstance(item, dict) for item in bands): return [ModelBand(band) for band in bands if isinstance(band, dict)] - raise STACError("Invalid bands property. Must list[str] or list[ModelBand]") + raise TypeError("Invalid bands property. Must list[str] or list[ModelBand]") @bands.setter def bands(self, v: list[ModelBand] | list[str]) -> None: @@ -1454,7 +1454,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: MLMExtension[T]: The extended object Raises: - pystac.STACError: When a :class:`pystac.Asset` object is apssed as the + pystac.TypeError: When a :class:`pystac.Asset` object is apssed as the `obj` parameter pystac.ExtensionTypeError: When any unsupported object is passed as the `obj` parameter. If you see this extension in this context, please @@ -1470,7 +1470,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: cls.ensure_owner_has_extension(obj, add_if_missing) return cast(MLMExtension[T], ItemAssetMLMExtension(obj)) elif isinstance(obj, pystac.Asset): - raise pystac.STACError( + raise TypeError( "This class cannot be used to extend STAC objects of type Assets. " "To extend Asset objects, use either AssetGeneralMLMExtension or " "AssetDetailedMLMExtension" @@ -1739,7 +1739,7 @@ def __init__(self, asset: pystac.Asset): @classmethod def _ext(cls: type[AssetExtensionType], obj: pystac.Asset) -> AssetExtensionType: if not isinstance(obj, pystac.Asset): - raise STACError( + raise TypeError( "This class can only be used to extend Assets. " "For Items and Collections use MLMExtension." ) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 12750e574..14afbb77d 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1054,5 +1054,5 @@ def test_raise_exception_on_mlm_extension_and_asset() -> None: media_type="application/tiff", roles=["mlm:model"], ) - with pytest.raises(pystac.errors.STACError): + with pytest.raises(TypeError): MLMExtension.ext(asset, add_if_missing=False) From fcfba65122ade2c500083d983123eec073e4abeb Mon Sep 17 00:00:00 2001 From: = Date: Thu, 3 Apr 2025 15:33:43 +0200 Subject: [PATCH 22/68] fixed docstring --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index cb80b89ad..e8b7e6c3d 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1454,7 +1454,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: MLMExtension[T]: The extended object Raises: - pystac.TypeError: When a :class:`pystac.Asset` object is apssed as the + TypeError: When a :class:`pystac.Asset` object is apssed as the `obj` parameter pystac.ExtensionTypeError: When any unsupported object is passed as the `obj` parameter. If you see this extension in this context, please From c75f50c64a7d1e35844066c428d473491d8f53b5 Mon Sep 17 00:00:00 2001 From: Jonas Hurst <76062450+jonas-hurst@users.noreply.github.com> Date: Mon, 14 Apr 2025 09:52:57 +0200 Subject: [PATCH 23/68] Update CHANGELOG.md Co-authored-by: Pete Gadomski --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc015afa6..3e5ae24ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,6 @@ - `Collection.from_items` for creating a `pystac.Collection` from an `ItemCollection` ([#1522](https://github.com/stac-utils/pystac/pull/1522)) - `extensions.mlm` for supporting the [MLM](https://github.com/stac-extensions/mlm) extension ([1542](https://github.com/stac-utils/pystac/pull/1542)) -### Fixed -- fixed missing parameter "title" in pystac.extensions.classification.Classification ([#1539](https://github.com/stac-utils/pystac/pull/1539)) - ### Fixed - `proj:epsg` migration when `None` ([#1544](https://github.com/stac-utils/pystac/pull/1544)) From f33c75a79efe66e8633d9db2416deea04c601b86 Mon Sep 17 00:00:00 2001 From: Jonas Hurst <76062450+jonas-hurst@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:10:45 +0200 Subject: [PATCH 24/68] fixed typo Co-authored-by: Pete Gadomski --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index e8b7e6c3d..df02f0286 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -216,7 +216,7 @@ def create( name: Name of the band referring to an extended band definition format: The type of expression that is specified in the expression property expression: An expression compliant with the format specified. - The cxpression can be applied to any data type and depends on the + The expression can be applied to any data type and depends on the format given """ c = cls({}) From cfd713d9ed196e12458ca00f440cc4cdc22ea07d Mon Sep 17 00:00:00 2001 From: Jonas Hurst <76062450+jonas-hurst@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:13:18 +0200 Subject: [PATCH 25/68] fixed typo Co-authored-by: Pete Gadomski --- pystac/extensions/mlm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index df02f0286..3fb1485dc 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -467,11 +467,11 @@ def get_required_props(cls, type: ValueScalingType) -> list[str]: Determines the parameters required for a certain ValueScaling operation. Args: - type: The type of ValueScaling operation for which required proreties are + type: The type of ValueScaling operation for which required properties are to be retrieved Returns: - list[str]: names of proreties required for the given ``type`` + list[str]: names of properties required for the given ``type`` """ d: dict[str, list[str]] = { "min-max": ["minimum", "maximum"], From fb359942dc0321a4826143a3751b5c3c1f5fda0a Mon Sep 17 00:00:00 2001 From: Jonas Hurst <76062450+jonas-hurst@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:13:48 +0200 Subject: [PATCH 26/68] fixed typo Co-authored-by: Pete Gadomski --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 3fb1485dc..68691f5ca 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -490,7 +490,7 @@ def validate_property_dict( cls, type: ValueScalingType, props: dict[str, Any] ) -> None: """ - Validate whether given properties satisfy the requiremts set by the ValueScaling + Validate whether given properties satisfy the requirements set by the ValueScaling ``type`` parameter Args: From 40b0c8fa2c4caffa7509a9abddadf56e31b42098 Mon Sep 17 00:00:00 2001 From: Jonas Hurst <76062450+jonas-hurst@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:17:16 +0200 Subject: [PATCH 27/68] fixed typo Co-authored-by: Pete Gadomski --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 68691f5ca..607aa109d 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1454,7 +1454,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: MLMExtension[T]: The extended object Raises: - TypeError: When a :class:`pystac.Asset` object is apssed as the + TypeError: When a :class:`pystac.Asset` object is passed as the `obj` parameter pystac.ExtensionTypeError: When any unsupported object is passed as the `obj` parameter. If you see this extension in this context, please From 0fb78c83a4231f4dbab55a9568cc9975bbcac196 Mon Sep 17 00:00:00 2001 From: Jonas Hurst <76062450+jonas-hurst@users.noreply.github.com> Date: Mon, 14 Apr 2025 10:17:34 +0200 Subject: [PATCH 28/68] fixed typo Co-authored-by: Pete Gadomski --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 607aa109d..366b40c1f 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1608,7 +1608,7 @@ def accelerator(self, v: AcceleratorType | None) -> None: @property def accelerator_constrained(self) -> bool | None: """ - Get or set the accelerator_constrianed property + Get or set the accelerator_constrained property """ return self._get_property(ACCELERATOR_CONSTRAINED_PROP, bool) From 16bfdcd527813a4ead3be1b117ab31d2b2fe5fbd Mon Sep 17 00:00:00 2001 From: = Date: Mon, 14 Apr 2025 10:34:04 +0200 Subject: [PATCH 29/68] line break to satisfy line length requirement --- pystac/extensions/mlm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 366b40c1f..78cbfe2a1 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -490,8 +490,8 @@ def validate_property_dict( cls, type: ValueScalingType, props: dict[str, Any] ) -> None: """ - Validate whether given properties satisfy the requirements set by the ValueScaling - ``type`` parameter + Validate whether given properties satisfy the requirements set by the + ValueScaling ``type`` parameter Args: type: The type of ValueScaling operation From 69e9bc38dfda36574a5883934d2c8060b998ceb9 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 14 Apr 2025 10:34:45 +0200 Subject: [PATCH 30/68] fixed typo in ENTRYPOINT_ASSET_PROP --- pystac/extensions/mlm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 78cbfe2a1..9193d4642 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -104,7 +104,7 @@ ARTIFACT_TYPE_ASSET_PROP = PREFIX + "artifact_type" COMPILE_MDTHOD_ASSET_PROP = PREFIX + "compile_method" -ENTRYPOITN_ASSET_PROP = PREFIX + "entrypoint" +ENTRYPOINT_ASSET_PROP = PREFIX + "entrypoint" class TaskType(StringEnum): @@ -1812,14 +1812,14 @@ def entrypoint(self) -> str | None: """ Get or set this asset's entrypoint property asdfasdf """ - return self.properties.get(ENTRYPOITN_ASSET_PROP) + return self.properties.get(ENTRYPOINT_ASSET_PROP) @entrypoint.setter def entrypoint(self, v: str | None) -> None: if v is not None: - self.properties[ENTRYPOITN_ASSET_PROP] = v + self.properties[ENTRYPOINT_ASSET_PROP] = v else: - self.properties.pop(ENTRYPOITN_ASSET_PROP, None) + self.properties.pop(ENTRYPOINT_ASSET_PROP, None) class AssetGeneralMLMExtension( From 036aa0643a6d741ca68e390e4a13809ada0ad4ab Mon Sep 17 00:00:00 2001 From: = Date: Mon, 14 Apr 2025 10:37:16 +0200 Subject: [PATCH 31/68] fixed typo in COMPILE_METHOD_ASSET_PROP --- pystac/extensions/mlm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 9193d4642..2291a467d 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -103,7 +103,7 @@ HYPERPARAMETERS_PROP: str = PREFIX + "hyperparameters" ARTIFACT_TYPE_ASSET_PROP = PREFIX + "artifact_type" -COMPILE_MDTHOD_ASSET_PROP = PREFIX + "compile_method" +COMPILE_METHOD_ASSET_PROP = PREFIX + "compile_method" ENTRYPOINT_ASSET_PROP = PREFIX + "entrypoint" @@ -1798,14 +1798,14 @@ def compile_method(self) -> str | None: """ Get or set this asset's compile_method property """ - return self.properties.get(COMPILE_MDTHOD_ASSET_PROP) + return self.properties.get(COMPILE_METHOD_ASSET_PROP) @compile_method.setter def compile_method(self, v: str | None) -> None: if v is not None: - self.properties[COMPILE_MDTHOD_ASSET_PROP] = v + self.properties[COMPILE_METHOD_ASSET_PROP] = v else: - self.properties.pop(COMPILE_MDTHOD_ASSET_PROP, None) + self.properties.pop(COMPILE_METHOD_ASSET_PROP, None) @property def entrypoint(self) -> str | None: From 68075aa883e4976ff0978ac8aa4d2d5bf350952a Mon Sep 17 00:00:00 2001 From: = Date: Mon, 14 Apr 2025 10:39:43 +0200 Subject: [PATCH 32/68] removed dummy docstring --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 2291a467d..45d04b548 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1810,7 +1810,7 @@ def compile_method(self, v: str | None) -> None: @property def entrypoint(self) -> str | None: """ - Get or set this asset's entrypoint property asdfasdf + Get or set this asset's entrypoint property """ return self.properties.get(ENTRYPOINT_ASSET_PROP) From 4d1f860beaafb6c268b6c10d87074208c720f48d Mon Sep 17 00:00:00 2001 From: = Date: Mon, 14 Apr 2025 10:55:20 +0200 Subject: [PATCH 33/68] fixed Enum tuples --- pystac/extensions/mlm.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 45d04b548..b3f53935c 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -112,18 +112,18 @@ class TaskType(StringEnum): An enumeration of Tasks supported by the extension """ - REGRESSION = ("regression",) - CLASSIFICATION = ("classification",) - SCENE_CLASSIFICATION = ("scene-classification",) - DETECTION = ("detection",) - OBJECT_DETECTION = ("object-detection",) - SEGMENTATION = ("segmentation",) - SEMANTIC_SEGMENTATION = ("semantic-segmentation",) - INSTANCE_SEGMENTATION = ("instance-segmentation",) - PANOPTIC_SEGMENTATION = ("panoptic-segmentation",) - SIMILARITy_SEARCH = ("similarity-search",) - GENERATIVE = ("generative",) - IAMGE_CAPTIONING = ("image-captioning",) + REGRESSION = "regression" + CLASSIFICATION = "classification" + SCENE_CLASSIFICATION = "scene-classification" + DETECTION = "detection" + OBJECT_DETECTION = "object-detection" + SEGMENTATION = "segmentation" + SEMANTIC_SEGMENTATION = "semantic-segmentation" + INSTANCE_SEGMENTATION = "instance-segmentation" + PANOPTIC_SEGMENTATION = "panoptic-segmentation" + SIMILARITY_SEARCH = "similarity-search" + GENERATIVE = "generative" + IAMGE_CAPTIONING = "image-captioning" SUPER_RESOLUTION = "super-resolution" @@ -132,12 +132,12 @@ class AcceleratorType(StringEnum): An enumeration of accelerators supported by the extension """ - AMD64 = ("amd64",) - CUDA = ("cuda",) - XLA = ("xla",) - AMD_ROCM = ("amd-rocm",) - INTEL_IPEX_CPU = ("intel-ipex-cpu",) - INTEL_IPEX_GPU = ("intel-ipex-gpu",) + AMD64 = "amd64" + CUDA = "cuda" + XLA = "xla" + AMD_ROCM = "amd-rocm" + INTEL_IPEX_CPU = "intel-ipex-cpu" + INTEL_IPEX_GPU = "intel-ipex-gpu" MACOS_ARM = "macos-arm" From 58feb542bb6341577be8b43280044b917505321f Mon Sep 17 00:00:00 2001 From: = Date: Mon, 14 Apr 2025 14:43:43 +0200 Subject: [PATCH 34/68] added mlm to CollectionExt --- pystac/extensions/ext.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pystac/extensions/ext.py b/pystac/extensions/ext.py index 1de582131..f8ed7c23d 100644 --- a/pystac/extensions/ext.py +++ b/pystac/extensions/ext.py @@ -156,6 +156,10 @@ def cube(self) -> DatacubeExtension[Collection]: def item_assets(self) -> dict[str, ItemAssetDefinition]: return ItemAssetsExtension.ext(self.stac_object).item_assets + @property + def mlm(self) -> MLMExtension[Collection]: + return MLMExtension.ext(self.stac_object) + @property def render(self) -> dict[str, Render]: return RenderExtension.ext(self.stac_object).renders From a0221f3fb786a5dec47eab2d8de3f48352cc08a2 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 15 Apr 2025 10:35:10 +0200 Subject: [PATCH 35/68] added mlm to Asset and ItemAssetDefinition mlm accessor --- pystac/extensions/ext.py | 17 ++++++++++++++++- tests/extensions/test_mlm.py | 17 ++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/pystac/extensions/ext.py b/pystac/extensions/ext.py index f8ed7c23d..84f60c39d 100644 --- a/pystac/extensions/ext.py +++ b/pystac/extensions/ext.py @@ -19,7 +19,11 @@ from pystac.extensions.grid import GridExtension from pystac.extensions.item_assets import ItemAssetsExtension from pystac.extensions.mgrs import MgrsExtension -from pystac.extensions.mlm import MLMExtension +from pystac.extensions.mlm import ( + AssetDetailedMLMExtension, + AssetGeneralMLMExtension, + MLMExtension, +) from pystac.extensions.pointcloud import PointcloudExtension from pystac.extensions.projection import ProjectionExtension from pystac.extensions.raster import RasterExtension @@ -400,6 +404,13 @@ class AssetExt(_AssetExt[Asset]): def file(self) -> FileExtension[Asset]: return FileExtension.ext(self.stac_object) + @property + def mlm(self) -> AssetGeneralMLMExtension[Asset] | AssetDetailedMLMExtension: + if "mlm:name" in self.stac_object.extra_fields: + return AssetDetailedMLMExtension.ext(self.stac_object) + else: + return AssetGeneralMLMExtension.ext(self.stac_object) + @property def timestamps(self) -> TimestampsExtension[Asset]: return TimestampsExtension.ext(self.stac_object) @@ -417,6 +428,10 @@ class ItemAssetExt(_AssetExt[ItemAssetDefinition]): stac_object: ItemAssetDefinition + @property + def mlm(self) -> MLMExtension[ItemAssetDefinition]: + return MLMExtension.ext(self.stac_object) + @dataclass class LinkExt(_AssetsExt[Link]): diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 14afbb77d..cb7090fe7 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -6,7 +6,7 @@ import pytest import pystac.errors -from pystac import Asset, Collection, Item +from pystac import Asset, Collection, Item, ItemAssetDefinition from pystac.errors import STACError from pystac.extensions.classification import Classification from pystac.extensions.mlm import ( @@ -924,6 +924,21 @@ def test_add_to_detailled_asset() -> None: assert asset_ext.artifact_type is None +def test_correct_asset_extension_is_used() -> None: + asset = Asset("https://example.com") + assert isinstance(asset.ext.mlm, AssetGeneralMLMExtension) + + asset.extra_fields["mlm:name"] = "asdf" + assert isinstance(asset.ext.mlm, AssetDetailedMLMExtension) + + +def test_asset_accessor() -> None: + item_asset = ItemAssetDefinition.create( + title="asdf", description="asdf", media_type="asdf", roles=["asdf"] + ) + assert isinstance(item_asset.ext.mlm, MLMExtension) + + def test_apply_detailled_asset() -> None: asset = pystac.Asset( href="http://example.com/test.tiff", From 477f5f996795a50a5cbdd9665b18954a39f98d8d Mon Sep 17 00:00:00 2001 From: Pete Gadomski Date: Tue, 15 Apr 2025 07:56:53 -0600 Subject: [PATCH 36/68] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e5ae24ac..cd921ad05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Added - `Collection.from_items` for creating a `pystac.Collection` from an `ItemCollection` ([#1522](https://github.com/stac-utils/pystac/pull/1522)) -- `extensions.mlm` for supporting the [MLM](https://github.com/stac-extensions/mlm) extension ([1542](https://github.com/stac-utils/pystac/pull/1542)) +- `extensions.mlm` for supporting the [MLM](https://github.com/stac-extensions/mlm) extension ([#1542](https://github.com/stac-utils/pystac/pull/1542)) ### Fixed From 30c3cb4ac75f99518a01953786e16503d4a6c01e Mon Sep 17 00:00:00 2001 From: = Date: Tue, 22 Apr 2025 10:53:04 +0200 Subject: [PATCH 37/68] fixed typo --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index b3f53935c..9e852ea3f 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -123,7 +123,7 @@ class TaskType(StringEnum): PANOPTIC_SEGMENTATION = "panoptic-segmentation" SIMILARITY_SEARCH = "similarity-search" GENERATIVE = "generative" - IAMGE_CAPTIONING = "image-captioning" + IMAGE_CAPTIONING = "image-captioning" SUPER_RESOLUTION = "super-resolution" From d5717bfda5dcf921bcac67f53f894f13891ed07d Mon Sep 17 00:00:00 2001 From: = Date: Tue, 22 Apr 2025 10:57:12 +0200 Subject: [PATCH 38/68] fixed enum values --- pystac/extensions/mlm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 9e852ea3f..9c3bfafcf 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -148,8 +148,8 @@ class ResizeType(StringEnum): CROP = "crop" PAD = "pad" - INTERPOLATION_NEAREST = "interpolate-nearest" - INTERPOLATION_LINEAR = "interpolate-linear" + INTERPOLATION_NEAREST = "interpolation-nearest" + INTERPOLATION_LINEAR = "interpolation-linear" INTERPOLATION_CUBIC = "interpolation-cubic" INTERPOLATION_AREA = "interpolation-area" INTERPOLATION_LANCZOS4 = "interpolation-lanczos4" From 325241a0ad69ccdd6bedb6eff933d3e6056a32db Mon Sep 17 00:00:00 2001 From: = Date: Tue, 22 Apr 2025 11:00:18 +0200 Subject: [PATCH 39/68] fixed property name for error message --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 9c3bfafcf..e373f9c47 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1097,7 +1097,7 @@ def data_type(self) -> DataType: return get_required( self.properties.get(DATA_TYPE_RESULT_STRUCTURE_PROP), self, - DIM_ORDER_RESULT_STRUCTURE_PROP, + DATA_TYPE_RESULT_STRUCTURE_PROP, ) @data_type.setter From 7a93619a460bb675eb4d7a1a07bbd4d18307eaae Mon Sep 17 00:00:00 2001 From: = Date: Tue, 22 Apr 2025 11:07:17 +0200 Subject: [PATCH 40/68] fix: pop max value --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index e373f9c47..3a5797e6a 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -562,7 +562,7 @@ def maximum(self, v: int | float | None) -> None: if v is not None: self.properties[MAXIMUM_VALUE_SCALING_PROP] = v else: - self.properties.get(MAXIMUM_VALUE_SCALING_PROP, None) + self.properties.pop(MAXIMUM_VALUE_SCALING_PROP, None) @property def mean(self) -> int | float | None: From ca229ba2c44032eda2c74e82da73817e7b9b4348 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 22 Apr 2025 11:09:53 +0200 Subject: [PATCH 41/68] fix docstring typo --- pystac/extensions/mlm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 3a5797e6a..6714e810b 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -160,7 +160,7 @@ class ResizeType(StringEnum): class ValueScalingType(StringEnum): """ - An enumeratino of Value Scaling operations supported by the extension + An enumeration of Value Scaling operations supported by the extension """ MIN_MAX = "min-max" From 6141557aa40ff9a87eec4f7f5f098881f258020c Mon Sep 17 00:00:00 2001 From: = Date: Tue, 22 Apr 2025 11:34:16 +0200 Subject: [PATCH 42/68] fix value_scaling property type in ModelInput --- pystac/extensions/mlm.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 6714e810b..48aac91d2 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -785,7 +785,7 @@ def apply( bands: list[ModelBand] | list[str], input: InputStructure, description: str | None = None, - value_scaling: ValueScaling | None = None, + value_scaling: list[ValueScaling] | None = None, resize_type: ResizeType | None = None, pre_processing_function: ProcessingExpression | None = None, ) -> None: @@ -836,7 +836,7 @@ def create( bands: list[ModelBand] | list[str], input: InputStructure, description: str | None = None, - value_scaling: ValueScaling | None = None, + value_scaling: list[ValueScaling] | None = None, resize_type: ResizeType | None = None, pre_processing_function: ProcessingExpression | None = None, ) -> ModelInput: @@ -951,18 +951,18 @@ def description(self, v: str | None) -> None: self.properties.pop(DESCRIPTION_INPUT_OBJECT_PROP, None) @property - def value_scaling(self) -> ValueScaling | None: + def value_scaling(self) -> list[ValueScaling] | None: """ Gets or sets the value_scaling property of this ModelInput object """ - v = self.properties.get(VALUE_SCALING_INPUT_OBJECT_PROP) - return ValueScaling(v) if v is not None else v + v_list = self.properties.get(VALUE_SCALING_INPUT_OBJECT_PROP) + return [ValueScaling(v) for v in v_list] if v_list is not None else None @value_scaling.setter - def value_scaling(self, v: ValueScaling | None) -> None: + def value_scaling(self, v: list[ValueScaling] | None) -> None: # add None to properties dict and do not pop it, according to specification self.properties[VALUE_SCALING_INPUT_OBJECT_PROP] = ( - None if v is None else v.to_dict() + None if v is None else [v_entry.to_dict() for v_entry in v] ) @property From 41b4c7775cbc925c2d5aaaa7f5292530d4840044 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 22 Apr 2025 11:44:08 +0200 Subject: [PATCH 43/68] change tests for value_scaling --- tests/extensions/test_mlm.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index cb7090fe7..8415b9d0f 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -232,19 +232,19 @@ def test_model_input_structure_props() -> None: input_testdata = [ ( ["B02", "B03", "B04"], - ValueScaling.create(ValueScalingType.SCALE, value=3), + [ValueScaling.create(ValueScalingType.SCALE, value=3)], ResizeType.CROP, ProcessingExpression.create("python", "asdf"), ), ( ["B02", "B03", "B04"], - ValueScaling.create(ValueScalingType.SCALE, value=3), + [ValueScaling.create(ValueScalingType.SCALE, value=3)], None, ProcessingExpression.create("python", "asdf"), ), ( [ModelBand.create("B02"), ModelBand.create("B03"), ModelBand.create("B04")], - ValueScaling.create(ValueScalingType.SCALE, value=3), + [ValueScaling.create(ValueScalingType.SCALE, value=3)], ResizeType.CROP, ProcessingExpression.create("python", "asdf"), ), @@ -256,7 +256,7 @@ def test_model_input_structure_props() -> None: ), ( ["B02", "B03", "B04"], - ValueScaling.create(ValueScalingType.SCALE, value=3), + [ValueScaling.create(ValueScalingType.SCALE, value=3)], ResizeType.CROP, None, ), @@ -268,7 +268,7 @@ def test_model_input_structure_props() -> None: ) def test_model_input( bands: list[str] | list[ModelBand], - value_scaling: ValueScaling | None, + value_scaling: list[ValueScaling] | None, resize_type: ResizeType | None, pre_processing_function: ProcessingExpression | None, ) -> None: @@ -323,7 +323,7 @@ def test_model_input_props() -> None: assert c.input == inp assert c.value_scaling is None - val_obj = ValueScaling.create(ValueScalingType.SCALE, value=3) + val_obj = [ValueScaling.create(ValueScalingType.SCALE, value=3)] c.value_scaling = val_obj assert c.value_scaling == val_obj From 6013b20d6864b73da3fff742f87f21d65a544e2c Mon Sep 17 00:00:00 2001 From: = Date: Wed, 23 Apr 2025 17:44:05 +0200 Subject: [PATCH 44/68] fix: added missing migrations --- pystac/extensions/mlm.py | 78 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 48aac91d2..b0b51b766 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2026,11 +2026,89 @@ class MLMExtensionHooks(ExtensionHooks): } stac_object_types = {pystac.STACObjectType.ITEM, pystac.STACObjectType.COLLECTION} + @staticmethod + def _migrate_1_0_to_1_1(obj: dict[str, Any]) -> None: + if "mlm:framework" in obj["properties"]: + if obj["properties"]["mlm:framework"] == "Scikit-learn": + obj["properties"]["mlm:framework"] = "scikit-learn" + if obj["properties"]["mlm:framework"] == "Huggingface": + obj["properties"]["mlm:framework"] = "Hugging Face" + + @staticmethod + def _migrate_1_1_to_1_2(obj: dict[str, Any]) -> None: + pass + + @staticmethod + def _migrate_1_2_to_1_3(obj: dict[str, Any]) -> None: + pass + + @staticmethod + def _migrate_1_3_to_1_4(obj: dict[str, Any]) -> None: + for input_obj in obj["properties"]["mlm:input"]: + if "norm_type" in input_obj and input_obj["norm_type"] is not None: + norm_type = input_obj["norm_type"] + value_scaling_list = [] + if norm_type == "min-max": + for band_statistic in input_obj["statistics"]: + value_scaling_obj = { + "type": "min-max", + "minimum": band_statistic["minimum"], + "maximum": band_statistic["maximum"], + } + value_scaling_list.append(value_scaling_obj) + elif norm_type == "z-score": + for band_statistic in input_obj["statistics"]: + value_scaling_obj = { + "type": "z-score", + "mean": band_statistic["mean"], + "stddev": band_statistic["stddev"], + } + value_scaling_list.append(value_scaling_obj) + elif norm_type == "clip": + for clip_value in input_obj["norm_clip"]: + value_scaling_obj = { + "type": "processing", + "format": "gdal-calc", + "expression": f"numpy.clip(A / {clip_value}, 0, 1)", + } + value_scaling_list.append(value_scaling_obj) + else: + raise NotImplementedError( + f"Normalization type {norm_type} is not supported in stac:mlm" + f" >= 1.3. Therefore an automatic migration is not possible. " + f"Please migrate this normalization manually using " + f'type="processing".' + ) + input_obj["value_scaling"] = value_scaling_list + input_obj.pop("norm_by_channel", None) + input_obj.pop("norm_type", None) + input_obj.pop("norm_clip", None) + input_obj.pop("statistics", None) + def migrate( self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription ) -> None: # mo adjustments to objects needed when migrating yet # schema back to v1.0 is fully backwards compatible + + if SCHEMA_URI_PATTERN.format(version="1.0.0") in info.extensions: + self._migrate_1_0_to_1_1(obj) + self._migrate_1_1_to_1_2(obj) + self._migrate_1_2_to_1_3(obj) + self._migrate_1_3_to_1_4(obj) + + if SCHEMA_URI_PATTERN.format(version="1.1.0") in info.extensions: + self._migrate_1_1_to_1_2(obj) + self._migrate_1_2_to_1_3(obj) + self._migrate_1_3_to_1_4(obj) + + if SCHEMA_URI_PATTERN.format(version="1.2.0") in info.extensions: + self._migrate_1_2_to_1_3(obj) + self._migrate_1_3_to_1_4(obj) + + if SCHEMA_URI_PATTERN.format(version="1.3.0") in info.extensions: + self._migrate_1_3_to_1_4(obj) + super().migrate(obj, version, info) From d21d8d18ab6f088dd8fa373062ddd709e12e14af Mon Sep 17 00:00:00 2001 From: = Date: Wed, 23 Apr 2025 17:45:47 +0200 Subject: [PATCH 45/68] added testing of migrations --- tests/extensions/test_mlm.py | 125 +++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 8415b9d0f..0c30aff45 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -20,6 +20,7 @@ InputStructure, ItemMLMExtension, MLMExtension, + MLMExtensionHooks, ModelBand, ModelInput, ModelOutput, @@ -1071,3 +1072,127 @@ def test_raise_exception_on_mlm_extension_and_asset() -> None: ) with pytest.raises(TypeError): MLMExtension.ext(asset, add_if_missing=False) + + +@pytest.mark.parametrize( + "framework_old, framework_new", + ((None, None), ("Scikit-learn", "scikit-learn"), ("Huggingface", "Hugging Face")), +) +def test_migration_1_0_to_1_1( + framework_old: None | str, framework_new: None | str +) -> None: + data: dict[str, Any] = {"properties": {}} + + MLMExtensionHooks._migrate_1_0_to_1_1(data) + assert "mlm:framework" not in data["properties"] + + data["properties"]["mlm:framework"] = framework_old + MLMExtensionHooks._migrate_1_0_to_1_1(data) + assert data["properties"]["mlm:framework"] == framework_new + + +@pytest.mark.parametrize( + ("norm_by_channel", "norm_type", "norm_clip", "statistics", "value_scaling"), + ( + (None, None, None, None, None), + (False, None, None, None, None), + ( + False, + "z-score", + None, + [{"mean": 5, "stddev": 2}], + [ValueScaling.create(ValueScalingType.Z_SCORE, mean=5, stddev=2)], + ), + ( + False, + "min-max", + None, + [{"minimum": 10, "maximum": 20}], + [ValueScaling.create(ValueScalingType.MIN_MAX, minimum=10, maximum=20)], + ), + ( + True, + "z-score", + None, + [ + {"mean": 5, "stddev": 2}, + {"mean": 6, "stddev": 3}, + {"mean": 10, "stddev": 1}, + ], + [ + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=5, stddev=2), + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=6, stddev=3), + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=10, stddev=1), + ], + ), + ( + True, + "clip", + [3, 4, 5], + None, + [ + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 3, 0, 1)", + ), + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 4, 0, 1)", + ), + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 5, 0, 1)", + ), + ], + ), + ), +) +def test_migration_1_3_to_1_4( + norm_by_channel: bool | None, + norm_type: str | None, + norm_clip: list[int] | None, + statistics: list[dict[str, Any]] | None, + value_scaling: list[ValueScaling] | None, +) -> None: + data: dict[str, Any] = {"properties": {"mlm:input": []}} + MLMExtensionHooks._migrate_1_3_to_1_4(data) # nothing is supposed to happen here + + input_obj: dict[str, Any] = {} + if norm_by_channel is not None: + input_obj["norm_by_channel"] = norm_by_channel + if norm_type is not None: + input_obj["norm_type"] = norm_type + if norm_clip is not None: + input_obj["norm_clip"] = norm_clip + if statistics is not None: + input_obj["statistics"] = statistics + data["properties"]["mlm:input"].append(input_obj) + + MLMExtensionHooks._migrate_1_3_to_1_4(data) + if norm_type is not None and value_scaling is not None: + assert len(data["properties"]["mlm:input"][0]["value_scaling"]) == len( + value_scaling + ) + assert data["properties"]["mlm:input"][0]["value_scaling"] == [ + obj.to_dict() for obj in value_scaling + ] + + new_input_obj = data["properties"]["mlm:input"] + assert "norm_by_channel" not in new_input_obj + assert "norm_type" not in new_input_obj + assert "norm_clip" not in new_input_obj + assert "statistics" not in new_input_obj + + +@pytest.mark.parametrize( + "norm_type", + ("l1", "l2", "l2sqr", "hamming", "hamming2", "type-mask", "relative", "inf"), +) +def test_migration_1_3_to_1_4_failure(norm_type: str) -> None: + data: dict[str, Any] = {"properties": {"mlm:input": [{"norm_type": norm_type}]}} + + with pytest.raises(NotImplementedError): + MLMExtensionHooks._migrate_1_3_to_1_4(data) From 8b333b5202185cbff0317a60ae1887d7b7f005e1 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 24 Apr 2025 16:49:09 +0200 Subject: [PATCH 46/68] added regex constraint for mlm:framework to migration --- pystac/extensions/mlm.py | 30 ++++++++++++++++++++++++++++++ tests/extensions/test_mlm.py | 24 ++++++++++++++++++++---- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index b0b51b766..2be94cd0b 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -8,6 +8,7 @@ from __future__ import annotations +import warnings from abc import ABC from collections.abc import Iterable from typing import Any, Generic, Literal, TypeVar, cast @@ -2029,10 +2030,39 @@ class MLMExtensionHooks(ExtensionHooks): @staticmethod def _migrate_1_0_to_1_1(obj: dict[str, Any]) -> None: if "mlm:framework" in obj["properties"]: + framework = obj["properties"]["mlm:framework"] + # remove invalid characters at beginning and end + forbidden_chars = [".", "_", "-", " ", "\t", "\n", "\r", "\f", "\v"] + if framework[0] in forbidden_chars or framework[-1] in forbidden_chars: + warnings.warn( + "Value for mlm:framework is invalid in mlm>=1.1, as it must" + "not start or end with one of the following characters: " + "._- and whitespace. These characters are therefore removed while" + "migrating the STAC object to v1.1.", + SyntaxWarning, + ) + while obj["properties"]["mlm:framework"][0] in forbidden_chars: + new_str = obj["properties"]["mlm:framework"][1:] + obj["properties"]["mlm:framework"] = new_str + while obj["properties"]["mlm:framework"][-1] in forbidden_chars: + new_str = obj["properties"]["mlm:framework"][:-1] + obj["properties"]["mlm:framework"] = new_str + + # rename frameworks if obj["properties"]["mlm:framework"] == "Scikit-learn": obj["properties"]["mlm:framework"] = "scikit-learn" + warnings.warn( + "mlm:framework value Scikit-learn is no longer valid in mlm>=1.1. " + "Renaming it to scikit-learn", + SyntaxWarning, + ) if obj["properties"]["mlm:framework"] == "Huggingface": obj["properties"]["mlm:framework"] = "Hugging Face" + warnings.warn( + "mlm:framework value Huggingface is no longer valid in mlm>=1.1. " + "Renaming it to Hugging Face", + SyntaxWarning, + ) @staticmethod def _migrate_1_1_to_1_2(obj: dict[str, Any]) -> None: diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 0c30aff45..00e4fb91a 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1,5 +1,6 @@ import json import logging +import re from copy import deepcopy from typing import Any, cast @@ -1075,20 +1076,35 @@ def test_raise_exception_on_mlm_extension_and_asset() -> None: @pytest.mark.parametrize( - "framework_old, framework_new", - ((None, None), ("Scikit-learn", "scikit-learn"), ("Huggingface", "Hugging Face")), + "framework_old, framework_new, valid", + ( + ("Scikit-learn", "scikit-learn", False), + ("Huggingface", "Hugging Face", False), + ("-_ .asdf", "asdf", False), + ("asdf-_ .", "asdf", False), + ("-._ asdf-.", "asdf", False), + ("test_framework", "test_framework", True), + ), ) def test_migration_1_0_to_1_1( - framework_old: None | str, framework_new: None | str + framework_old: None | str, framework_new: None | str, valid: bool ) -> None: data: dict[str, Any] = {"properties": {}} MLMExtensionHooks._migrate_1_0_to_1_1(data) assert "mlm:framework" not in data["properties"] + pattern = r"^(?=[^\s._\-]).*[^\s._\-]$" data["properties"]["mlm:framework"] = framework_old - MLMExtensionHooks._migrate_1_0_to_1_1(data) + + if valid: + MLMExtensionHooks._migrate_1_0_to_1_1(data) + else: + with pytest.warns(SyntaxWarning): + MLMExtensionHooks._migrate_1_0_to_1_1(data) + assert data["properties"]["mlm:framework"] == framework_new + assert bool(re.match(pattern, data["properties"]["mlm:framework"])) @pytest.mark.parametrize( From a3817d4169d577e5a897da0850e35ac03f16ff32 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 25 Apr 2025 13:40:46 +0200 Subject: [PATCH 47/68] added migration from 1.1 to 1.2 --- pystac/extensions/mlm.py | 13 ++++++++++++- tests/extensions/test_mlm.py | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 2be94cd0b..0629ef875 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2066,7 +2066,18 @@ def _migrate_1_0_to_1_1(obj: dict[str, Any]) -> None: @staticmethod def _migrate_1_1_to_1_2(obj: dict[str, Any]) -> None: - pass + if "assets" not in obj: + return + assets = obj["assets"] + model_in_assets = any( + ["mlm:model" in assets[asset]["roles"] for asset in assets] + ) + if not model_in_assets: + raise pystac.errors.STACError( + 'Error migrating stac:mlm version: An asset with role "mlm:model" is ' + "required in mlm>=1.2. Include at least one asset with role " + '"mlm:model" ' + ) @staticmethod def _migrate_1_2_to_1_3(obj: dict[str, Any]) -> None: diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 00e4fb91a..1e9a6fb80 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1107,6 +1107,20 @@ def test_migration_1_0_to_1_1( assert bool(re.match(pattern, data["properties"]["mlm:framework"])) +def test_migration_1_1_to_1_2() -> None: + data: dict[str, Any] = {} + MLMExtensionHooks._migrate_1_1_to_1_2(data) + + data["assets"] = {"asset1": {"roles": ["data"]}, "asset2": {"roles": ["labels"]}} + + with pytest.raises(pystac.errors.STACError): + MLMExtensionHooks._migrate_1_1_to_1_2(data) + + data["assets"]["asset3"] = {"roles": ["mlm:model"]} + + MLMExtensionHooks._migrate_1_1_to_1_2(data) + + @pytest.mark.parametrize( ("norm_by_channel", "norm_type", "norm_clip", "statistics", "value_scaling"), ( From 0480c32e8134434be62b46209495565b22c422b4 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 25 Apr 2025 15:50:59 +0200 Subject: [PATCH 48/68] added migration from 1.2 to 1.3 --- pystac/extensions/mlm.py | 26 ++++++++++++++- tests/extensions/test_mlm.py | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 0629ef875..1d44ba63e 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2081,7 +2081,31 @@ def _migrate_1_1_to_1_2(obj: dict[str, Any]) -> None: @staticmethod def _migrate_1_2_to_1_3(obj: dict[str, Any]) -> None: - pass + bands_obj = obj["properties"]["mlm:input"] + + if not bands_obj: + return + + if "raster:bands" not in obj["properties"]: + return + raster_bands = obj["properties"]["raster:bands"] + + # make sure all raster_bands have a name prop with length>0 + names_properties_valid = all( + "name" in band and len(band["name"]) > 0 for band in raster_bands + ) + if not names_properties_valid: + raise STACError( + "Error migrating stac:mlm version: In mlm>=1.3, each band in " + 'raster:bands is required to have a property "name"' + ) + + # copy the raster:bands to assets + for asset_name in obj["assets"]: + asset = obj["assets"][asset_name] + if "mlm:model" not in asset["roles"]: + continue + asset["raster:bands"] = raster_bands @staticmethod def _migrate_1_3_to_1_4(obj: dict[str, Any]) -> None: diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 1e9a6fb80..5fddf167e 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1121,6 +1121,69 @@ def test_migration_1_1_to_1_2() -> None: MLMExtensionHooks._migrate_1_1_to_1_2(data) +@pytest.mark.parametrize( + "inp_bands, raster_bands, valid", + ( + ([], None, True), + ( + ["B02", "B03"], + [ + {"name": "B02", "data_type": "float64"}, + {"name": "B03", "data_type": "float64"}, + ], + True, + ), + ( + ["B02", "B03"], + [ + {"name": "", "data_type": "float64"}, + {"name": "", "data_type": "float64"}, + ], + False, + ), + ( + ["B02", "B03"], + [ + {"name": "B02", "data_type": "float64"}, + {"name": "", "data_type": "float64"}, + ], + False, + ), + ( + ["B02", "B03"], + [{"name": "B02", "data_type": "float64"}, {"data_type": "float64"}], + False, + ), + ( + ["B02", "B03"], + [{"name": "", "data_type": "float64"}, {"data_type": "float64"}], + False, + ), + (["B02", "B03"], [{"data_type": "float64"}, {"data_type": "float64"}], False), + ), +) +def test_migration_1_2_to_1_3( + inp_bands: list[str], raster_bands: list[dict[str, Any]], valid: bool +) -> None: + data: dict[str, Any] = { + "properties": {"mlm:input": {}}, + "assets": {"asset1": {"roles": ["data"]}, "asset2": {"roles": ["mlm:model"]}}, + } + + if inp_bands: + data["properties"]["mlm:input"]["bands"] = inp_bands + data["properties"]["raster:bands"] = raster_bands + + if valid: + MLMExtensionHooks._migrate_1_2_to_1_3(data) + if raster_bands: + assert "raster:bands" not in data["assets"]["asset1"] + assert "raster:bands" in data["assets"]["asset2"] + else: + with pytest.raises(STACError): + MLMExtensionHooks._migrate_1_2_to_1_3(data) + + @pytest.mark.parametrize( ("norm_by_channel", "norm_type", "norm_clip", "statistics", "value_scaling"), ( From 6d727a43bc712d6214615caec8a417e21c1a47ec Mon Sep 17 00:00:00 2001 From: = Date: Fri, 25 Apr 2025 16:50:02 +0200 Subject: [PATCH 49/68] migrate now forbidden properties in assets --- pystac/extensions/mlm.py | 104 +++++++++++++++++++++-------------- tests/extensions/test_mlm.py | 33 ++++++++++- 2 files changed, 96 insertions(+), 41 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 1d44ba63e..615b4cae6 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2109,46 +2109,70 @@ def _migrate_1_2_to_1_3(obj: dict[str, Any]) -> None: @staticmethod def _migrate_1_3_to_1_4(obj: dict[str, Any]) -> None: - for input_obj in obj["properties"]["mlm:input"]: - if "norm_type" in input_obj and input_obj["norm_type"] is not None: - norm_type = input_obj["norm_type"] - value_scaling_list = [] - if norm_type == "min-max": - for band_statistic in input_obj["statistics"]: - value_scaling_obj = { - "type": "min-max", - "minimum": band_statistic["minimum"], - "maximum": band_statistic["maximum"], - } - value_scaling_list.append(value_scaling_obj) - elif norm_type == "z-score": - for band_statistic in input_obj["statistics"]: - value_scaling_obj = { - "type": "z-score", - "mean": band_statistic["mean"], - "stddev": band_statistic["stddev"], - } - value_scaling_list.append(value_scaling_obj) - elif norm_type == "clip": - for clip_value in input_obj["norm_clip"]: - value_scaling_obj = { - "type": "processing", - "format": "gdal-calc", - "expression": f"numpy.clip(A / {clip_value}, 0, 1)", - } - value_scaling_list.append(value_scaling_obj) - else: - raise NotImplementedError( - f"Normalization type {norm_type} is not supported in stac:mlm" - f" >= 1.3. Therefore an automatic migration is not possible. " - f"Please migrate this normalization manually using " - f'type="processing".' - ) - input_obj["value_scaling"] = value_scaling_list - input_obj.pop("norm_by_channel", None) - input_obj.pop("norm_type", None) - input_obj.pop("norm_clip", None) - input_obj.pop("statistics", None) + # Migrate to value_scaling + if "mlm:input" in obj["properties"]: + for input_obj in obj["properties"]["mlm:input"]: + if "norm_type" in input_obj and input_obj["norm_type"] is not None: + norm_type = input_obj["norm_type"] + value_scaling_list = [] + if norm_type == "min-max": + for band_statistic in input_obj["statistics"]: + value_scaling_obj = { + "type": "min-max", + "minimum": band_statistic["minimum"], + "maximum": band_statistic["maximum"], + } + value_scaling_list.append(value_scaling_obj) + elif norm_type == "z-score": + for band_statistic in input_obj["statistics"]: + value_scaling_obj = { + "type": "z-score", + "mean": band_statistic["mean"], + "stddev": band_statistic["stddev"], + } + value_scaling_list.append(value_scaling_obj) + elif norm_type == "clip": + for clip_value in input_obj["norm_clip"]: + value_scaling_obj = { + "type": "processing", + "format": "gdal-calc", + "expression": f"numpy.clip(A / {clip_value}, 0, 1)", + } + value_scaling_list.append(value_scaling_obj) + else: + raise NotImplementedError( + f"Normalization type {norm_type} is not supported in " + f"stac:mlm >= 1.3. Therefore an automatic migration is not " + f"possible. Please migrate this normalization manually " + f'using type="processing".' + ) + input_obj["value_scaling"] = value_scaling_list + input_obj.pop("norm_by_channel", None) + input_obj.pop("norm_type", None) + input_obj.pop("norm_clip", None) + input_obj.pop("statistics", None) + + if "assets" in obj: + for asset in obj["assets"]: + # move forbidden fields from asset to properties + if "mlm:name" in obj["assets"][asset]: + obj["properties"]["mlm:name"] = obj["assets"][asset]["mlm:name"] + obj["assets"][asset].pop("mlm:name") + if "mlm:input" in obj["assets"][asset]: + obj["properties"]["mlm:input"] = obj["assets"][asset]["mlm:input"] + obj["assets"][asset].pop("mlm:input") + if "mlm:output" in obj["assets"][asset]: + obj["properties"]["mlm:output"] = obj["assets"][asset]["mlm:output"] + obj["assets"][asset].pop("mlm:output") + if "mlm:hyperparameters" in obj["assets"][asset]: + obj["properties"]["mlm:hyperparameters"] = obj["assets"][asset][ + "mlm:hyperparameters" + ] + obj["assets"][asset].pop("mlm:hyperparameters") + + # add new REQUIRED proretie mlm:artifact_type to asset + if "mlm:model" in obj["assets"][asset]["roles"]: + obj["assets"][asset]["mlm:artifact_type"] = "" def migrate( self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 5fddf167e..00af52053 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1243,7 +1243,7 @@ def test_migration_1_2_to_1_3( ), ), ) -def test_migration_1_3_to_1_4( +def test_migration_1_3_to_1_4_value_scaling( norm_by_channel: bool | None, norm_type: str | None, norm_clip: list[int] | None, @@ -1289,3 +1289,34 @@ def test_migration_1_3_to_1_4_failure(norm_type: str) -> None: with pytest.raises(NotImplementedError): MLMExtensionHooks._migrate_1_3_to_1_4(data) + + +def test_migration_1_3_to_1_4_assets() -> None: + data: dict[str, Any] = { + "properties": {}, + "assets": { + "asset1": { + "mlm:name": "asdf", + "mlm:input": {}, + "mlm:output": {}, + "mlm:hyperparameters": {}, + "roles": ["mlm:model"], + } + }, + } + + MLMExtensionHooks._migrate_1_3_to_1_4(data) + + assert "mlm:name" not in data["assets"]["asset1"] + assert "mlm:name" in data["properties"] + + assert "mlm:input" not in data["assets"]["asset1"] + assert "mlm:input" in data["properties"] + + assert "mlm:output" not in data["assets"]["asset1"] + assert "mlm:output" in data["properties"] + + assert "mlm:hyperparameters" not in data["assets"]["asset1"] + assert "mlm:hyperparameters" in data["properties"] + + assert "mlm:artifact_type" in data["assets"]["asset1"] From fa4ce93879c0166b59260c25c8319c9f721f3c0c Mon Sep 17 00:00:00 2001 From: = Date: Mon, 28 Apr 2025 12:19:26 +0200 Subject: [PATCH 50/68] fix: added missing parameter to docstring --- pystac/extensions/mlm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 615b4cae6..fcc57b964 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1182,7 +1182,7 @@ def create( Creates a new Output Args: - name:Name of the output variable defined by the model. If no explicit name + name: Name of the output variable defined by the model. If no explicit name is defined by the model, an informative name (e.g.: "CLASSIFICATION") can be used instead. tasks: Specifies the Machine Learning tasks for which the output can be used @@ -1192,6 +1192,7 @@ def create( from one model head. description: Additional details about the output such as describing its purpose or expected result that cannot be represented by other properties. + description: Description of output. classes: A list of class objects adhering to the Classification Extension. post_processing_function: Custom postprocessing function where normalization, rescaling, or any other significant operations takes From 72a0b4563cf3640a7a5e246605fbd4ee4f5cdc98 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 28 Apr 2025 13:59:18 +0200 Subject: [PATCH 51/68] refactor: move properties to parent classes --- pystac/extensions/mlm.py | 343 ++++++++++++++++++++------------------- 1 file changed, 176 insertions(+), 167 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index fcc57b964..61f17342b 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1341,156 +1341,68 @@ def to_dict(self) -> dict[str, Any]: return self.properties -class MLMExtension( - Generic[T], - PropertiesExtension, - ExtensionManagementMixin[pystac.Item | pystac.Collection], -): - """An abstract class that can be used to extend to properties of an - :class:`pystac.Item` or :class:`pystac.Collection` with properties from the - :stac-ext:`Machine Learning Model Extension `. - - This class can be used to extend :class:`pystac.Item`, :class:`pystac.Collection` - and :class:`pystac.ItemAssetDefinition`. For extending :class:`pystac.Asset`, use - either :class:`~AssetGeneralMLMExtension`: or :class:`AssetDetailedMLMExtension`. - """ - - name: Literal["mlm"] = "mlm" +class _ExcludedFromAssetProps(PropertiesExtension): properties: dict[str, Any] - def apply( - self, - name: str, - architecture: str, - tasks: list[TaskType], - input: list[ModelInput], - output: list[ModelOutput], - framework: str | None = None, - framework_version: str | None = None, - memory_size: int | None = None, - total_parameters: int | None = None, - pretrained: bool | None = None, - pretrained_source: str | None = None, - batch_size_suggestion: int | None = None, - accelerator: AcceleratorType | None = None, - accelerator_constrained: bool | None = None, - accelerator_summary: str | None = None, - accelerator_count: int | None = None, - hyperparameters: Hyperparameters | None = None, - *args: Any, - **kwargs: Any, - ) -> None: + @property + def mlm_name(self) -> str: """ - Sets the properties of a new MLMExtension - - Args: - name: name for the model - architecture: A generic and well established architecture name of the model - tasks: Specifies the Machine Learning tasks for which the model can be - used for - input: Describes the transformation between the EO data and the model input - output: Describes each model output and how to interpret it. - framework: Framework used to train the model - framework_version: The ``framework`` library version - memory_size: The in-memory size of the model on the accelerator during - inference (bytes) - total_parameters: Total number of model parameters, including trainable and - non-trainable parameters. - pretrained: Indicates if the model was pretrained. If the model was - pretrained, consider providing ``pretrained_source`` if it is known - pretrained_source: The source of the pretraining. - batch_size_suggestion: A suggested batch size for the accelerator and - summarized hardware. - accelerator: The intended computational hardware that runs inference - accelerator_constrained: Indicates if the intended ``accelerator`` is the - only accelerator that can run inference - accelerator_summary: A high level description of the ``accelerator`` - accelerator_count: A minimum amount of ``accelerator`` instances required to - run the model - hyperparameters: Additional hyperparameters relevant for the model - *args: Unused (no effect, only here for signature compliance with apply - method in derived classes - **kwargs: Unused (no effect, only here for signature compliance with apply - method in derived classes + Get or set the required (mlm) name property. It is named mlm_name in this + context to not break convention and overwrite the extension name class property. """ - self.mlm_name = name - self.architecture = architecture - self.tasks = tasks - self.input = input - self.output = output - self.framework = framework - self.framework_version = framework_version - self.memory_size = memory_size - self.total_parameters = total_parameters - self.pretrained = pretrained - self.pretrained_source = pretrained_source - self.batch_size_suggestion = batch_size_suggestion - self.accelerator = accelerator - self.accelerator_constrained = accelerator_constrained - self.accelerator_summary = accelerator_summary - self.accelerator_count = accelerator_count - self.hyperparameters = hyperparameters + return get_required(self.properties.get(NAME_PROP), self, NAME_PROP) - @classmethod - def get_schema_uri(cls) -> str: - """ - Retrieves this extension's schema URI + @mlm_name.setter + def mlm_name(self, v: str) -> None: + self._set_property(NAME_PROP, v) - Returns: - str: the schema URI + @property + def input(self) -> list[ModelInput]: """ - return SCHEMA_URI_PATTERN.format(version=DEFAULT_VERSION) - - @classmethod - def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: + Get or set the required input property """ - Extend a STAC object (``obj``) with the MLMExtension - - Args: - obj: The STAC object to be extended. - add_if_missing: Defines whether this extension's URI should be added to - this object's (or its parent's) list of extensions if it is not already - listed there. + return [ + ModelInput(inp) + for inp in get_required( + self._get_property(INPUT_PROP, list[dict[str, Any]]), self, INPUT_PROP + ) + ] - Returns: - MLMExtension[T]: The extended object + @input.setter + def input(self, v: list[ModelInput]) -> None: + self._set_property(INPUT_PROP, [inp.to_dict() for inp in v]) - Raises: - TypeError: When a :class:`pystac.Asset` object is passed as the - `obj` parameter - pystac.ExtensionTypeError: When any unsupported object is passed as the - `obj` parameter. If you see this extension in this context, please - raise an issue on github. + @property + def output(self) -> list[ModelOutput]: """ - if isinstance(obj, pystac.Item): - cls.ensure_has_extension(obj, add_if_missing) - return cast(MLMExtension[T], ItemMLMExtension(obj)) - elif isinstance(obj, pystac.Collection): - cls.ensure_has_extension(obj, add_if_missing) - return cast(MLMExtension[T], CollectionMLMExtension(obj)) - elif isinstance(obj, pystac.ItemAssetDefinition): - cls.ensure_owner_has_extension(obj, add_if_missing) - return cast(MLMExtension[T], ItemAssetMLMExtension(obj)) - elif isinstance(obj, pystac.Asset): - raise TypeError( - "This class cannot be used to extend STAC objects of type Assets. " - "To extend Asset objects, use either AssetGeneralMLMExtension or " - "AssetDetailedMLMExtension" + Get or set the required output property + """ + return [ + ModelOutput(outp) + for outp in get_required( + self._get_property(OUTPUT_PROP, list[dict[str, Any]]), self, OUTPUT_PROP ) - else: - raise pystac.ExtensionTypeError(cls._ext_error_message(obj)) + ] + + @output.setter + def output(self, v: list[ModelOutput]) -> None: + self._set_property(OUTPUT_PROP, [outp.to_dict() for outp in v]) @property - def mlm_name(self) -> str: + def hyperparameters(self) -> Hyperparameters | None: """ - Get or set the required (mlm) name property. It is named mlm_name in this - context to not break convention and overwrite the extension name class property. + Get or set the hyperparameters property """ - return get_required(self.properties.get(NAME_PROP), self, NAME_PROP) + prop = self._get_property(HYPERPARAMETERS_PROP, dict[str, Any]) + return Hyperparameters(prop) if prop is not None else None - @mlm_name.setter - def mlm_name(self, v: str) -> None: - self._set_property(NAME_PROP, v) + @hyperparameters.setter + def hyperparameters(self, v: Hyperparameters | None) -> None: + self._set_property(HYPERPARAMETERS_PROP, v.to_dict() if v is not None else None) + + +class _IncludedInAssetProps(PropertiesExtension): + properties: dict[str, Any] @property def architecture(self) -> str: @@ -1640,49 +1552,146 @@ def accelerator_count(self) -> int | None: def accelerator_count(self, v: int | None) -> None: self._set_property(ACCELERATOR_COUNT_PROP, v) - @property - def input(self) -> list[ModelInput]: - """ - Get or set the required input property - """ - return [ - ModelInput(inp) - for inp in get_required( - self._get_property(INPUT_PROP, list[dict[str, Any]]), self, INPUT_PROP - ) - ] - @input.setter - def input(self, v: list[ModelInput]) -> None: - self._set_property(INPUT_PROP, [inp.to_dict() for inp in v]) +class MLMExtension( + Generic[T], + _ExcludedFromAssetProps, + _IncludedInAssetProps, + ExtensionManagementMixin[pystac.Item | pystac.Collection], +): + """An abstract class that can be used to extend to properties of an + :class:`pystac.Item` or :class:`pystac.Collection` with properties from the + :stac-ext:`Machine Learning Model Extension `. - @property - def output(self) -> list[ModelOutput]: + This class can be used to extend :class:`pystac.Item`, :class:`pystac.Collection` + and :class:`pystac.ItemAssetDefinition`. For extending :class:`pystac.Asset`, use + either :class:`~AssetGeneralMLMExtension`: or :class:`AssetDetailedMLMExtension`. + """ + + name: Literal["mlm"] = "mlm" + properties: dict[str, Any] + + def apply( + self, + name: str, + architecture: str, + tasks: list[TaskType], + input: list[ModelInput], + output: list[ModelOutput], + framework: str | None = None, + framework_version: str | None = None, + memory_size: int | None = None, + total_parameters: int | None = None, + pretrained: bool | None = None, + pretrained_source: str | None = None, + batch_size_suggestion: int | None = None, + accelerator: AcceleratorType | None = None, + accelerator_constrained: bool | None = None, + accelerator_summary: str | None = None, + accelerator_count: int | None = None, + hyperparameters: Hyperparameters | None = None, + *args: Any, + **kwargs: Any, + ) -> None: """ - Get or set the required output property + Sets the properties of a new MLMExtension + + Args: + name: name for the model + architecture: A generic and well established architecture name of the model + tasks: Specifies the Machine Learning tasks for which the model can be + used for + input: Describes the transformation between the EO data and the model input + output: Describes each model output and how to interpret it. + framework: Framework used to train the model + framework_version: The ``framework`` library version + memory_size: The in-memory size of the model on the accelerator during + inference (bytes) + total_parameters: Total number of model parameters, including trainable and + non-trainable parameters. + pretrained: Indicates if the model was pretrained. If the model was + pretrained, consider providing ``pretrained_source`` if it is known + pretrained_source: The source of the pretraining. + batch_size_suggestion: A suggested batch size for the accelerator and + summarized hardware. + accelerator: The intended computational hardware that runs inference + accelerator_constrained: Indicates if the intended ``accelerator`` is the + only accelerator that can run inference + accelerator_summary: A high level description of the ``accelerator`` + accelerator_count: A minimum amount of ``accelerator`` instances required to + run the model + hyperparameters: Additional hyperparameters relevant for the model + *args: Unused (no effect, only here for signature compliance with apply + method in derived classes + **kwargs: Unused (no effect, only here for signature compliance with apply + method in derived classes """ - return [ - ModelOutput(outp) - for outp in get_required( - self._get_property(OUTPUT_PROP, list[dict[str, Any]]), self, OUTPUT_PROP - ) - ] + self.mlm_name = name + self.architecture = architecture + self.tasks = tasks + self.input = input + self.output = output + self.framework = framework + self.framework_version = framework_version + self.memory_size = memory_size + self.total_parameters = total_parameters + self.pretrained = pretrained + self.pretrained_source = pretrained_source + self.batch_size_suggestion = batch_size_suggestion + self.accelerator = accelerator + self.accelerator_constrained = accelerator_constrained + self.accelerator_summary = accelerator_summary + self.accelerator_count = accelerator_count + self.hyperparameters = hyperparameters - @output.setter - def output(self, v: list[ModelOutput]) -> None: - self._set_property(OUTPUT_PROP, [outp.to_dict() for outp in v]) + @classmethod + def get_schema_uri(cls) -> str: + """ + Retrieves this extension's schema URI - @property - def hyperparameters(self) -> Hyperparameters | None: + Returns: + str: the schema URI """ - Get or set the hyperparameters property + return SCHEMA_URI_PATTERN.format(version=DEFAULT_VERSION) + + @classmethod + def ext(cls, obj: T, add_if_missing: bool = False) -> MLMExtension[T]: """ - prop = self._get_property(HYPERPARAMETERS_PROP, dict[str, Any]) - return Hyperparameters(prop) if prop is not None else None + Extend a STAC object (``obj``) with the MLMExtension - @hyperparameters.setter - def hyperparameters(self, v: Hyperparameters | None) -> None: - self._set_property(HYPERPARAMETERS_PROP, v.to_dict() if v is not None else None) + Args: + obj: The STAC object to be extended. + add_if_missing: Defines whether this extension's URI should be added to + this object's (or its parent's) list of extensions if it is not already + listed there. + + Returns: + MLMExtension[T]: The extended object + + Raises: + TypeError: When a :class:`pystac.Asset` object is passed as the + `obj` parameter + pystac.ExtensionTypeError: When any unsupported object is passed as the + `obj` parameter. If you see this extension in this context, please + raise an issue on github. + """ + if isinstance(obj, pystac.Item): + cls.ensure_has_extension(obj, add_if_missing) + return cast(MLMExtension[T], ItemMLMExtension(obj)) + elif isinstance(obj, pystac.Collection): + cls.ensure_has_extension(obj, add_if_missing) + return cast(MLMExtension[T], CollectionMLMExtension(obj)) + elif isinstance(obj, pystac.ItemAssetDefinition): + cls.ensure_owner_has_extension(obj, add_if_missing) + return cast(MLMExtension[T], ItemAssetMLMExtension(obj)) + elif isinstance(obj, pystac.Asset): + raise TypeError( + "This class cannot be used to extend STAC objects of type Assets. " + "To extend Asset objects, use either AssetGeneralMLMExtension or " + "AssetDetailedMLMExtension" + ) + else: + raise pystac.ExtensionTypeError(cls._ext_error_message(obj)) def to_dict(self) -> dict[str, Any]: """ From c16430745e4ca592bf37c3284d5388b710ba76a9 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 28 Apr 2025 14:01:32 +0200 Subject: [PATCH 52/68] fix: removed forbidden properties from asset extension --- pystac/extensions/mlm.py | 55 +++++++++++++++------------------------- 1 file changed, 20 insertions(+), 35 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 61f17342b..60338defd 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -1590,8 +1590,6 @@ def apply( accelerator_summary: str | None = None, accelerator_count: int | None = None, hyperparameters: Hyperparameters | None = None, - *args: Any, - **kwargs: Any, ) -> None: """ Sets the properties of a new MLMExtension @@ -1621,10 +1619,6 @@ def apply( accelerator_count: A minimum amount of ``accelerator`` instances required to run the model hyperparameters: Additional hyperparameters relevant for the model - *args: Unused (no effect, only here for signature compliance with apply - method in derived classes - **kwargs: Unused (no effect, only here for signature compliance with apply - method in derived classes """ self.mlm_name = name self.architecture = architecture @@ -1896,7 +1890,11 @@ def __repr__(self) -> str: return f"" -class AssetDetailedMLMExtension(_AssetMLMExtension, MLMExtension[pystac.Asset]): +class AssetDetailedMLMExtension( + _AssetMLMExtension, + _IncludedInAssetProps, + ExtensionManagementMixin[pystac.Item | pystac.Collection], +): """A class that can be used to extend the properties of an :class:`pystac.Asset` object with properties from the :stac-ext:`Machine Learning Model Extension `. @@ -1934,11 +1932,8 @@ def ext( def apply( self, - name: str, architecture: str, tasks: list[TaskType], - input: list[ModelInput], - output: list[ModelOutput], framework: str | None = None, framework_version: str | None = None, memory_size: int | None = None, @@ -1950,7 +1945,6 @@ def apply( accelerator_constrained: bool | None = None, accelerator_summary: str | None = None, accelerator_count: int | None = None, - hyperparameters: Hyperparameters | None = None, artifact_type: str | None = None, compile_method: str | None = None, entrypoint: str | None = None, @@ -1959,12 +1953,9 @@ def apply( Sets the properties of a new AssetDetailedMLMExtensions Args: - name: name for the model architecture: A generic and well established architecture name of the model tasks: Specifies the Machine Learning tasks for which the model can be used for - input: Describes the transformation between the EO data and the model input - output: Describes each model output and how to interpret it. framework: Framework used to train the model framework_version: The ``framework`` library version memory_size: The in-memory size of the model on the accelerator during @@ -1982,7 +1973,6 @@ def apply( accelerator_summary: A high level description of the ``accelerator`` accelerator_count: A minimum amount of ``accelerator`` instances required to run the model - hyperparameters: Additional hyperparameters relevant for the model artifact_type: Specifies the kind of model artifact, any string is allowed. Typically related to a particular ML framework. This property is required when ``mlm:model`` is listed as a role of this asset @@ -1991,26 +1981,21 @@ def apply( entrypoint: Specific entrypoint reference in the code to use for running model inference. """ - MLMExtension.apply( - self, - name=name, - architecture=architecture, - tasks=tasks, - input=input, - output=output, - framework=framework, - framework_version=framework_version, - memory_size=memory_size, - total_parameters=total_parameters, - pretrained=pretrained, - pretrained_source=pretrained_source, - batch_size_suggestion=batch_size_suggestion, - accelerator=accelerator, - accelerator_constrained=accelerator_constrained, - accelerator_summary=accelerator_summary, - accelerator_count=accelerator_count, - hyperparameters=hyperparameters, - ) + + self.architecture = architecture + self.tasks = tasks + self.framework = framework + self.framework_version = framework_version + self.memory_size = memory_size + self.total_parameters = total_parameters + self.pretrained = pretrained + self.pretrained_source = pretrained_source + self.batch_size_suggestion = batch_size_suggestion + self.accelerator = accelerator + self.accelerator_constrained = accelerator_constrained + self.accelerator_summary = accelerator_summary + self.accelerator_count = accelerator_count + self.artifact_type = artifact_type self.compile_method = compile_method self.entrypoint = entrypoint From 3daa5b57f930bebcb582e350ab5dd00c252d1b8f Mon Sep 17 00:00:00 2001 From: = Date: Mon, 28 Apr 2025 14:02:25 +0200 Subject: [PATCH 53/68] changed asset extension mechanism from name to architecture --- pystac/extensions/ext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pystac/extensions/ext.py b/pystac/extensions/ext.py index 84f60c39d..fd2c7a646 100644 --- a/pystac/extensions/ext.py +++ b/pystac/extensions/ext.py @@ -406,7 +406,7 @@ def file(self) -> FileExtension[Asset]: @property def mlm(self) -> AssetGeneralMLMExtension[Asset] | AssetDetailedMLMExtension: - if "mlm:name" in self.stac_object.extra_fields: + if "mlm:architecture" in self.stac_object.extra_fields: return AssetDetailedMLMExtension.ext(self.stac_object) else: return AssetGeneralMLMExtension.ext(self.stac_object) From 9a8b37e8ea868ea70dc9b4ef3b4a65e0db1bf63d Mon Sep 17 00:00:00 2001 From: = Date: Mon, 28 Apr 2025 14:03:04 +0200 Subject: [PATCH 54/68] removed forbidden properties from detailled asset tests --- tests/extensions/test_mlm.py | 70 +----------------------------------- 1 file changed, 1 insertion(+), 69 deletions(-) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 00af52053..11130b16b 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -12,7 +12,6 @@ from pystac.extensions.classification import Classification from pystac.extensions.mlm import ( ARCHITECTURE_PROP, - NAME_PROP, TASKS_PROP, AcceleratorType, AssetDetailedMLMExtension, @@ -770,20 +769,16 @@ def test_add_to_asset(plain_item: Item) -> None: MLMExtension.ext(plain_item, add_if_missing=True) asset = plain_item.assets["analytic"] - assert NAME_PROP not in asset.extra_fields.keys() assert ARCHITECTURE_PROP not in asset.extra_fields.keys() assert TASKS_PROP not in asset.extra_fields.keys() asset_ext = AssetDetailedMLMExtension.ext(asset) - asset_ext.mlm_name = "asdf" asset_ext.architecture = "ResNet" asset_ext.tasks = [TaskType.CLASSIFICATION] - assert NAME_PROP in asset.extra_fields.keys() assert ARCHITECTURE_PROP in asset.extra_fields.keys() assert TASKS_PROP in asset.extra_fields.keys() - assert asset.extra_fields[NAME_PROP] == "asdf" assert asset.extra_fields[ARCHITECTURE_PROP] == "ResNet" assert asset.extra_fields[TASKS_PROP] == [TaskType.CLASSIFICATION] @@ -866,21 +861,6 @@ def test_to_dict_asset_generic() -> None: def test_add_to_detailled_asset() -> None: - model_input = ModelInput.create( - name="model", - bands=["B02"], - input=InputStructure.create( - shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 - ), - ) - model_output = ModelOutput.create( - name="output", - tasks=[TaskType.CLASSIFICATION], - result=ResultStructure.create( - shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 - ), - ) - asset = pystac.Asset( href="http://example.com/test.tiff", title="image", @@ -888,11 +868,8 @@ def test_add_to_detailled_asset() -> None: media_type="application/tiff", roles=["mlm:model"], extra_fields={ - "mlm:name": "asdf", "mlm:architecture": "ResNet", "mlm:tasks": [TaskType.CLASSIFICATION], - "mlm:input": [model_input.to_dict()], - "mlm:output": [model_output.to_dict()], "mlm:artifact_type": "foo", "mlm:compile_method": "bar", "mlm:entrypoint": "baz", @@ -901,11 +878,8 @@ def test_add_to_detailled_asset() -> None: asset_ext = AssetDetailedMLMExtension.ext(asset, add_if_missing=False) - assert asset_ext.mlm_name == "asdf" assert asset_ext.architecture == "ResNet" assert asset_ext.tasks == [TaskType.CLASSIFICATION] - assert asset_ext.input == [model_input] - assert asset_ext.output == [model_output] assert asset_ext.artifact_type == "foo" assert asset_ext.compile_method == "bar" assert asset_ext.entrypoint == "baz" @@ -930,7 +904,7 @@ def test_correct_asset_extension_is_used() -> None: asset = Asset("https://example.com") assert isinstance(asset.ext.mlm, AssetGeneralMLMExtension) - asset.extra_fields["mlm:name"] = "asdf" + asset.extra_fields["mlm:architecture"] = "ResNet" assert isinstance(asset.ext.mlm, AssetDetailedMLMExtension) @@ -951,37 +925,16 @@ def test_apply_detailled_asset() -> None: ) asset_ext = AssetDetailedMLMExtension.ext(asset, add_if_missing=False) - model_input = ModelInput.create( - name="model", - bands=["B02"], - input=InputStructure.create( - shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 - ), - ) - model_output = ModelOutput.create( - name="output", - tasks=[TaskType.CLASSIFICATION], - result=ResultStructure.create( - shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 - ), - ) - asset_ext.apply( - "asdf", "ResNet", [TaskType.CLASSIFICATION], - [model_input], - [model_output], artifact_type="foo", compile_method="bar", entrypoint="baz", ) - assert asset_ext.mlm_name == "asdf" assert asset_ext.architecture == "ResNet" assert asset_ext.tasks == [TaskType.CLASSIFICATION] - assert asset_ext.input == [model_input] - assert asset_ext.output == [model_output] assert asset_ext.artifact_type == "foo" assert asset_ext.compile_method == "bar" assert asset_ext.entrypoint == "baz" @@ -997,38 +950,17 @@ def test_to_dict_detailed_asset() -> None: ) asset_ext = AssetDetailedMLMExtension.ext(asset, add_if_missing=False) - model_input = ModelInput.create( - name="model", - bands=["B02"], - input=InputStructure.create( - shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 - ), - ) - model_output = ModelOutput.create( - name="output", - tasks=[TaskType.CLASSIFICATION], - result=ResultStructure.create( - shape=[1], dim_order=["batch"], data_type=DataType.FLOAT64 - ), - ) - asset_ext.apply( - "asdf", "ResNet", [TaskType.CLASSIFICATION], - [model_input], - [model_output], artifact_type="foo", compile_method="bar", entrypoint="baz", ) d = { - "mlm:name": "asdf", "mlm:architecture": "ResNet", "mlm:tasks": [TaskType.CLASSIFICATION], - "mlm:input": [model_input.to_dict()], - "mlm:output": [model_output.to_dict()], "mlm:artifact_type": "foo", "mlm:compile_method": "bar", "mlm:entrypoint": "baz", From c7298e2121989792d19f75a9889e7bcf5cf40571 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 28 Apr 2025 15:40:40 +0200 Subject: [PATCH 55/68] enabled migration for Item, Collection, assets, item-assets --- pystac/extensions/mlm.py | 84 ++++++++++++++++++++++++---------------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 60338defd..77f1080ba 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2024,40 +2024,56 @@ class MLMExtensionHooks(ExtensionHooks): @staticmethod def _migrate_1_0_to_1_1(obj: dict[str, Any]) -> None: - if "mlm:framework" in obj["properties"]: - framework = obj["properties"]["mlm:framework"] - # remove invalid characters at beginning and end - forbidden_chars = [".", "_", "-", " ", "\t", "\n", "\r", "\f", "\v"] - if framework[0] in forbidden_chars or framework[-1] in forbidden_chars: - warnings.warn( - "Value for mlm:framework is invalid in mlm>=1.1, as it must" - "not start or end with one of the following characters: " - "._- and whitespace. These characters are therefore removed while" - "migrating the STAC object to v1.1.", - SyntaxWarning, - ) - while obj["properties"]["mlm:framework"][0] in forbidden_chars: - new_str = obj["properties"]["mlm:framework"][1:] - obj["properties"]["mlm:framework"] = new_str - while obj["properties"]["mlm:framework"][-1] in forbidden_chars: - new_str = obj["properties"]["mlm:framework"][:-1] - obj["properties"]["mlm:framework"] = new_str - - # rename frameworks - if obj["properties"]["mlm:framework"] == "Scikit-learn": - obj["properties"]["mlm:framework"] = "scikit-learn" - warnings.warn( - "mlm:framework value Scikit-learn is no longer valid in mlm>=1.1. " - "Renaming it to scikit-learn", - SyntaxWarning, - ) - if obj["properties"]["mlm:framework"] == "Huggingface": - obj["properties"]["mlm:framework"] = "Hugging Face" - warnings.warn( - "mlm:framework value Huggingface is no longer valid in mlm>=1.1. " - "Renaming it to Hugging Face", - SyntaxWarning, - ) + def migrate(props_obj: dict[str, Any]) -> None: + if "mlm:framework" in props_obj: + framework = props_obj["mlm:framework"] + # remove invalid characters at beginning and end + forbidden_chars = [".", "_", "-", " ", "\t", "\n", "\r", "\f", "\v"] + if framework[0] in forbidden_chars or framework[-1] in forbidden_chars: + warnings.warn( + "Value for mlm:framework is invalid in mlm>=1.1, as it must" + "not start or end with one of the following characters: " + "._- and whitespace. These characters are therefore removed " + "while migrating the STAC object to v1.1.", + SyntaxWarning, + ) + while props_obj["mlm:framework"][0] in forbidden_chars: + new_str = props_obj["mlm:framework"][1:] + props_obj["mlm:framework"] = new_str + while props_obj["mlm:framework"][-1] in forbidden_chars: + new_str = props_obj["mlm:framework"][:-1] + props_obj["mlm:framework"] = new_str + + # rename frameworks + if props_obj["mlm:framework"] == "Scikit-learn": + props_obj["mlm:framework"] = "scikit-learn" + warnings.warn( + "mlm:framework value Scikit-learn is no longer valid in " + "mlm>=1.1. Renaming it to scikit-learn", + SyntaxWarning, + ) + if props_obj["mlm:framework"] == "Huggingface": + props_obj["mlm:framework"] = "Hugging Face" + warnings.warn( + "mlm:framework value Huggingface is no longer valid in " + "mlm>=1.1. Renaming it to Hugging Face", + SyntaxWarning, + ) + + if obj["type"] == "Feature": + migrate(obj["properties"]) + if obj["type"] == "Collection": + migrate(obj) + + if "assets" in obj: + for asset_name in obj["assets"]: + asset = obj["assets"][asset_name] + migrate(asset) + + if obj["type"] == "Collection" and "item_assets" in obj: + for asset_name in obj["item_assets"]: + asset = obj["item_assets"][asset_name] + migrate(asset) @staticmethod def _migrate_1_1_to_1_2(obj: dict[str, Any]) -> None: From db68c9f6f4ea3327e7a9c5032429ff9648985caf Mon Sep 17 00:00:00 2001 From: = Date: Mon, 28 Apr 2025 17:34:30 +0200 Subject: [PATCH 56/68] added more version migrations --- pystac/extensions/mlm.py | 125 ++++++++++++++++++++++++++------------- 1 file changed, 85 insertions(+), 40 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 77f1080ba..83e529e13 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2077,52 +2077,76 @@ def migrate(props_obj: dict[str, Any]) -> None: @staticmethod def _migrate_1_1_to_1_2(obj: dict[str, Any]) -> None: - if "assets" not in obj: - return - assets = obj["assets"] - model_in_assets = any( - ["mlm:model" in assets[asset]["roles"] for asset in assets] - ) - if not model_in_assets: - raise pystac.errors.STACError( - 'Error migrating stac:mlm version: An asset with role "mlm:model" is ' - "required in mlm>=1.2. Include at least one asset with role " - '"mlm:model" ' + def migrate(obj_assets: dict[str, Any]) -> None: + model_in_assets = any( + ["mlm:model" in obj_assets[asset]["roles"] for asset in obj_assets] ) + if not model_in_assets: + raise pystac.errors.STACError( + 'Error migrating stac:mlm version: An asset with role "mlm:model" ' + "is required in mlm>=1.2. Include at least one asset with role " + '"mlm:model" ' + ) + + if "assets" in obj: + migrate(obj["assets"]) + if "item_assets" in obj: + migrate(obj["item_assets"]) @staticmethod def _migrate_1_2_to_1_3(obj: dict[str, Any]) -> None: - bands_obj = obj["properties"]["mlm:input"] + def migrate(props_obj: dict[str, Any]) -> None: + if "mlm:input" not in props_obj: + return - if not bands_obj: - return + bands_objs_present = any("bands" in inp for inp in props_obj["mlm:input"]) - if "raster:bands" not in obj["properties"]: - return - raster_bands = obj["properties"]["raster:bands"] + if not bands_objs_present: + return - # make sure all raster_bands have a name prop with length>0 - names_properties_valid = all( - "name" in band and len(band["name"]) > 0 for band in raster_bands - ) - if not names_properties_valid: - raise STACError( - "Error migrating stac:mlm version: In mlm>=1.3, each band in " - 'raster:bands is required to have a property "name"' + if "raster:bands" not in props_obj: + return + raster_bands = props_obj["raster:bands"] + + # make sure all raster_bands have a name prop with length>0 + names_properties_valid = all( + "name" in band and len(band["name"]) > 0 for band in raster_bands ) + if not names_properties_valid: + raise STACError( + "Error migrating stac:mlm version: In mlm>=1.3, each band in " + 'raster:bands is required to have a property "name"' + ) + + # no need to perform the actions below if props_obj is an asset + # this is checked by the presence of "roles" prop + if "roles" in props_obj: + return - # copy the raster:bands to assets - for asset_name in obj["assets"]: - asset = obj["assets"][asset_name] - if "mlm:model" not in asset["roles"]: - continue - asset["raster:bands"] = raster_bands + # copy the raster:bands to assets + for inner_asset_name in obj["assets"]: + inner_asset = obj["assets"][inner_asset_name] + if "mlm:model" not in inner_asset["roles"]: + continue + inner_asset["raster:bands"] = raster_bands + + if obj["type"] == "Feature" and "mlm:input" in obj["properties"]: + migrate(obj["properties"]) + if obj["type"] == "Collection": + migrate(obj) + if "assets" in obj: + for asset_name in obj["assets"]: + asset = obj["assets"][asset_name] + migrate(asset) @staticmethod def _migrate_1_3_to_1_4(obj: dict[str, Any]) -> None: - # Migrate to value_scaling - if "mlm:input" in obj["properties"]: - for input_obj in obj["properties"]["mlm:input"]: + def migrate(props_obj: dict[str, Any]) -> None: + if "mlm:input" not in props_obj: + return + + # Migrate to value_scaling + for input_obj in props_obj["mlm:input"]: if "norm_type" in input_obj and input_obj["norm_type"] is not None: norm_type = input_obj["norm_type"] value_scaling_list = [] @@ -2163,22 +2187,43 @@ def _migrate_1_3_to_1_4(obj: dict[str, Any]) -> None: input_obj.pop("norm_clip", None) input_obj.pop("statistics", None) + if obj["type"] == "Feature": + migrate(obj["properties"]) + if obj["type"] == "Collection": + migrate(obj) + if "assets" in obj: for asset in obj["assets"]: + migrate(obj["assets"][asset]) + # move forbidden fields from asset to properties if "mlm:name" in obj["assets"][asset]: - obj["properties"]["mlm:name"] = obj["assets"][asset]["mlm:name"] + mlm_name = obj["assets"][asset]["mlm:name"] + if obj["type"] == "Feature": + obj["properties"]["mlm:name"] = mlm_name + if obj["type"] == "Collection": + obj["mlm:name"] = mlm_name obj["assets"][asset].pop("mlm:name") if "mlm:input" in obj["assets"][asset]: - obj["properties"]["mlm:input"] = obj["assets"][asset]["mlm:input"] + inp = obj["assets"][asset]["mlm:input"] + if obj["type"] == "Feature": + obj["properties"]["mlm:input"] = inp + if obj["type"] == "Collection": + obj["mlm:input"] = inp obj["assets"][asset].pop("mlm:input") if "mlm:output" in obj["assets"][asset]: - obj["properties"]["mlm:output"] = obj["assets"][asset]["mlm:output"] + outp = obj["assets"][asset]["mlm:output"] + if obj["type"] == "Feature": + obj["properties"]["mlm:output"] = outp + if obj["type"] == "Collection": + obj["mlm:output"] = outp obj["assets"][asset].pop("mlm:output") if "mlm:hyperparameters" in obj["assets"][asset]: - obj["properties"]["mlm:hyperparameters"] = obj["assets"][asset][ - "mlm:hyperparameters" - ] + hyp = obj["assets"][asset]["mlm:hyperparameters"] + if obj["type"] == "Feature": + obj["properties"]["mlm:hyperparameters"] = hyp + if obj["type"] == "Collection": + obj["mlm:hyperparameters"] = hyp obj["assets"][asset].pop("mlm:hyperparameters") # add new REQUIRED proretie mlm:artifact_type to asset From b666f3033ba3598f399891f2a4ce393a2e80f7c4 Mon Sep 17 00:00:00 2001 From: = Date: Mon, 28 Apr 2025 17:40:16 +0200 Subject: [PATCH 57/68] test new migrations --- tests/extensions/test_mlm.py | 495 ++++++++++++++++++++++++++++++++++- 1 file changed, 481 insertions(+), 14 deletions(-) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 11130b16b..51e4ea4b6 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1018,10 +1018,10 @@ def test_raise_exception_on_mlm_extension_and_asset() -> None: ("test_framework", "test_framework", True), ), ) -def test_migration_1_0_to_1_1( +def test_migration_1_0_to_1_1_item( framework_old: None | str, framework_new: None | str, valid: bool ) -> None: - data: dict[str, Any] = {"properties": {}} + data: dict[str, Any] = {"type": "Feature", "properties": {}} MLMExtensionHooks._migrate_1_0_to_1_1(data) assert "mlm:framework" not in data["properties"] @@ -1039,16 +1039,116 @@ def test_migration_1_0_to_1_1( assert bool(re.match(pattern, data["properties"]["mlm:framework"])) -def test_migration_1_1_to_1_2() -> None: +@pytest.mark.parametrize( + "framework_old, framework_new, valid", + ( + ("Scikit-learn", "scikit-learn", False), + ("Huggingface", "Hugging Face", False), + ("-_ .asdf", "asdf", False), + ("asdf-_ .", "asdf", False), + ("-._ asdf-.", "asdf", False), + ("test_framework", "test_framework", True), + ), +) +def test_migration_1_0_to_1_1_collection( + framework_old: None | str, framework_new: None | str, valid: bool +) -> None: + data: dict[str, Any] = {"type": "Collection"} + + MLMExtensionHooks._migrate_1_0_to_1_1(data) + assert "mlm:framework" not in data + + pattern = r"^(?=[^\s._\-]).*[^\s._\-]$" + data["mlm:framework"] = framework_old + + if valid: + MLMExtensionHooks._migrate_1_0_to_1_1(data) + else: + with pytest.warns(SyntaxWarning): + MLMExtensionHooks._migrate_1_0_to_1_1(data) + + assert data["mlm:framework"] == framework_new + assert bool(re.match(pattern, data["mlm:framework"])) + + +@pytest.mark.parametrize( + "framework_old, framework_new, valid", + ( + ("Scikit-learn", "scikit-learn", False), + ("Huggingface", "Hugging Face", False), + ("-_ .asdf", "asdf", False), + ("asdf-_ .", "asdf", False), + ("-._ asdf-.", "asdf", False), + ("test_framework", "test_framework", True), + ), +) +def test_migration_1_0_to_1_1_asset( + framework_old: None | str, framework_new: None | str, valid: bool +) -> None: + data: dict[str, Any] = { + "type": "Item", + "assets": {"asset1": Asset("https://asdf.com").to_dict()}, + } + + MLMExtensionHooks._migrate_1_0_to_1_1(data) + assert "mlm:framework" not in data["assets"]["asset1"] + + pattern = r"^(?=[^\s._\-]).*[^\s._\-]$" + data["assets"]["asset1"]["mlm:framework"] = framework_old + + if valid: + MLMExtensionHooks._migrate_1_0_to_1_1(data) + else: + with pytest.warns(SyntaxWarning): + MLMExtensionHooks._migrate_1_0_to_1_1(data) + + assert data["assets"]["asset1"]["mlm:framework"] == framework_new + assert bool(re.match(pattern, data["assets"]["asset1"]["mlm:framework"])) + + +@pytest.mark.parametrize( + "framework_old, framework_new, valid", + ( + ("Scikit-learn", "scikit-learn", False), + ("Huggingface", "Hugging Face", False), + ("-_ .asdf", "asdf", False), + ("asdf-_ .", "asdf", False), + ("-._ asdf-.", "asdf", False), + ("test_framework", "test_framework", True), + ), +) +def test_migration_1_0_to_1_1_item_assets( + framework_old: None | str, framework_new: None | str, valid: bool +) -> None: + data: dict[str, Any] = {"type": "Collection", "item_assets": {"asset1": {}}} + + MLMExtensionHooks._migrate_1_0_to_1_1(data) + assert "mlm:framework" not in data + + pattern = r"^(?=[^\s._\-]).*[^\s._\-]$" + data["item_assets"]["asset1"]["mlm:framework"] = framework_old + + if valid: + MLMExtensionHooks._migrate_1_0_to_1_1(data) + else: + with pytest.warns(SyntaxWarning): + MLMExtensionHooks._migrate_1_0_to_1_1(data) + + assert data["item_assets"]["asset1"]["mlm:framework"] == framework_new + assert bool(re.match(pattern, data["item_assets"]["asset1"]["mlm:framework"])) + + +@pytest.mark.parametrize("asset_type", ("assets", "item_assets")) +def test_migration_1_1_to_1_2(asset_type: str) -> None: data: dict[str, Any] = {} MLMExtensionHooks._migrate_1_1_to_1_2(data) - data["assets"] = {"asset1": {"roles": ["data"]}, "asset2": {"roles": ["labels"]}} + data[asset_type] = {"asset1": {"roles": ["data"]}, "asset2": {"roles": ["labels"]}} with pytest.raises(pystac.errors.STACError): MLMExtensionHooks._migrate_1_1_to_1_2(data) - data["assets"]["asset3"] = {"roles": ["mlm:model"]} + data[asset_type]["asset3"] = {"roles": ["mlm:model"]} MLMExtensionHooks._migrate_1_1_to_1_2(data) @@ -1094,16 +1194,17 @@ def test_migration_1_1_to_1_2() -> None: (["B02", "B03"], [{"data_type": "float64"}, {"data_type": "float64"}], False), ), ) -def test_migration_1_2_to_1_3( +def test_migration_1_2_to_1_3_item( inp_bands: list[str], raster_bands: list[dict[str, Any]], valid: bool ) -> None: data: dict[str, Any] = { - "properties": {"mlm:input": {}}, + "type": "Feature", + "properties": {"mlm:input": [{}]}, "assets": {"asset1": {"roles": ["data"]}, "asset2": {"roles": ["mlm:model"]}}, } - if inp_bands: - data["properties"]["mlm:input"]["bands"] = inp_bands + data["properties"]["mlm:input"][0]["bands"] = inp_bands + if raster_bands: data["properties"]["raster:bands"] = raster_bands if valid: @@ -1116,6 +1217,137 @@ def test_migration_1_2_to_1_3( MLMExtensionHooks._migrate_1_2_to_1_3(data) +@pytest.mark.parametrize( + "inp_bands, raster_bands, valid", + ( + ([], None, True), + ( + ["B02", "B03"], + [ + {"name": "B02", "data_type": "float64"}, + {"name": "B03", "data_type": "float64"}, + ], + True, + ), + ( + ["B02", "B03"], + [ + {"name": "", "data_type": "float64"}, + {"name": "", "data_type": "float64"}, + ], + False, + ), + ( + ["B02", "B03"], + [ + {"name": "B02", "data_type": "float64"}, + {"name": "", "data_type": "float64"}, + ], + False, + ), + ( + ["B02", "B03"], + [{"name": "B02", "data_type": "float64"}, {"data_type": "float64"}], + False, + ), + ( + ["B02", "B03"], + [{"name": "", "data_type": "float64"}, {"data_type": "float64"}], + False, + ), + (["B02", "B03"], [{"data_type": "float64"}, {"data_type": "float64"}], False), + ), +) +def test_migration_1_2_to_1_3_collection( + inp_bands: list[str], raster_bands: list[dict[str, Any]], valid: bool +) -> None: + data: dict[str, Any] = { + "type": "Collection", + "mlm:input": [{}], + "assets": {"asset1": {"roles": ["data"]}, "asset2": {"roles": ["mlm:model"]}}, + } + + data["mlm:input"][0]["bands"] = inp_bands + if raster_bands: + data["raster:bands"] = raster_bands + + if valid: + MLMExtensionHooks._migrate_1_2_to_1_3(data) + if raster_bands: + assert "raster:bands" not in data["assets"]["asset1"] + assert "raster:bands" in data["assets"]["asset2"] + else: + with pytest.raises(STACError): + MLMExtensionHooks._migrate_1_2_to_1_3(data) + + +@pytest.mark.parametrize( + "inp_bands, raster_bands, valid", + ( + ([], None, True), + ( + ["B02", "B03"], + [ + {"name": "B02", "data_type": "float64"}, + {"name": "B03", "data_type": "float64"}, + ], + True, + ), + ( + ["B02", "B03"], + [ + {"name": "", "data_type": "float64"}, + {"name": "", "data_type": "float64"}, + ], + False, + ), + ( + ["B02", "B03"], + [ + {"name": "B02", "data_type": "float64"}, + {"name": "", "data_type": "float64"}, + ], + False, + ), + ( + ["B02", "B03"], + [{"name": "B02", "data_type": "float64"}, {"data_type": "float64"}], + False, + ), + ( + ["B02", "B03"], + [{"name": "", "data_type": "float64"}, {"data_type": "float64"}], + False, + ), + (["B02", "B03"], [{"data_type": "float64"}, {"data_type": "float64"}], False), + ), +) +def test_migration_1_2_to_1_3_asset( + inp_bands: list[str], raster_bands: list[dict[str, Any]], valid: bool +) -> None: + data: dict[str, Any] = { + "type": "Feature", + "properties": [], + "assets": { + "asset1": {"roles": ["data"]}, + "asset2": {"roles": ["mlm:model"], "mlm:input": [{}]}, + }, + } + + data["assets"]["asset2"]["mlm:input"][0]["bands"] = inp_bands + if raster_bands: + data["assets"]["asset2"]["raster:bands"] = raster_bands + + if valid: + MLMExtensionHooks._migrate_1_2_to_1_3(data) + if raster_bands: + assert "raster:bands" not in data["assets"]["asset1"] + assert "raster:bands" in data["assets"]["asset2"] + else: + with pytest.raises(STACError): + MLMExtensionHooks._migrate_1_2_to_1_3(data) + + @pytest.mark.parametrize( ("norm_by_channel", "norm_type", "norm_clip", "statistics", "value_scaling"), ( @@ -1175,14 +1407,14 @@ def test_migration_1_2_to_1_3( ), ), ) -def test_migration_1_3_to_1_4_value_scaling( +def test_migration_1_3_to_1_4_value_scaling_item( norm_by_channel: bool | None, norm_type: str | None, norm_clip: list[int] | None, statistics: list[dict[str, Any]] | None, value_scaling: list[ValueScaling] | None, ) -> None: - data: dict[str, Any] = {"properties": {"mlm:input": []}} + data: dict[str, Any] = {"type": "Feature", "properties": {"mlm:input": []}} MLMExtensionHooks._migrate_1_3_to_1_4(data) # nothing is supposed to happen here input_obj: dict[str, Any] = {} @@ -1205,7 +1437,206 @@ def test_migration_1_3_to_1_4_value_scaling( obj.to_dict() for obj in value_scaling ] - new_input_obj = data["properties"]["mlm:input"] + new_input_obj = data["properties"]["mlm:input"][0] + assert "norm_by_channel" not in new_input_obj + assert "norm_type" not in new_input_obj + assert "norm_clip" not in new_input_obj + assert "statistics" not in new_input_obj + + +@pytest.mark.parametrize( + ("norm_by_channel", "norm_type", "norm_clip", "statistics", "value_scaling"), + ( + (None, None, None, None, None), + (False, None, None, None, None), + ( + False, + "z-score", + None, + [{"mean": 5, "stddev": 2}], + [ValueScaling.create(ValueScalingType.Z_SCORE, mean=5, stddev=2)], + ), + ( + False, + "min-max", + None, + [{"minimum": 10, "maximum": 20}], + [ValueScaling.create(ValueScalingType.MIN_MAX, minimum=10, maximum=20)], + ), + ( + True, + "z-score", + None, + [ + {"mean": 5, "stddev": 2}, + {"mean": 6, "stddev": 3}, + {"mean": 10, "stddev": 1}, + ], + [ + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=5, stddev=2), + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=6, stddev=3), + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=10, stddev=1), + ], + ), + ( + True, + "clip", + [3, 4, 5], + None, + [ + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 3, 0, 1)", + ), + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 4, 0, 1)", + ), + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 5, 0, 1)", + ), + ], + ), + ), +) +def test_migration_1_3_to_1_4_value_scaling_collection( + norm_by_channel: bool | None, + norm_type: str | None, + norm_clip: list[int] | None, + statistics: list[dict[str, Any]] | None, + value_scaling: list[ValueScaling] | None, +) -> None: + data: dict[str, Any] = {"type": "Collection", "mlm:input": []} + MLMExtensionHooks._migrate_1_3_to_1_4(data) # nothing is supposed to happen here + + input_obj: dict[str, Any] = {} + if norm_by_channel is not None: + input_obj["norm_by_channel"] = norm_by_channel + if norm_type is not None: + input_obj["norm_type"] = norm_type + if norm_clip is not None: + input_obj["norm_clip"] = norm_clip + if statistics is not None: + input_obj["statistics"] = statistics + data["mlm:input"].append(input_obj) + + MLMExtensionHooks._migrate_1_3_to_1_4(data) + if norm_type is not None and value_scaling is not None: + assert len(data["mlm:input"][0]["value_scaling"]) == len(value_scaling) + assert data["mlm:input"][0]["value_scaling"] == [ + obj.to_dict() for obj in value_scaling + ] + + new_input_obj = data["mlm:input"][0] + assert "norm_by_channel" not in new_input_obj + assert "norm_type" not in new_input_obj + assert "norm_clip" not in new_input_obj + assert "statistics" not in new_input_obj + + +@pytest.mark.parametrize( + ("norm_by_channel", "norm_type", "norm_clip", "statistics", "value_scaling"), + ( + (None, None, None, None, None), + (False, None, None, None, None), + ( + False, + "z-score", + None, + [{"mean": 5, "stddev": 2}], + [ValueScaling.create(ValueScalingType.Z_SCORE, mean=5, stddev=2)], + ), + ( + False, + "min-max", + None, + [{"minimum": 10, "maximum": 20}], + [ValueScaling.create(ValueScalingType.MIN_MAX, minimum=10, maximum=20)], + ), + ( + True, + "z-score", + None, + [ + {"mean": 5, "stddev": 2}, + {"mean": 6, "stddev": 3}, + {"mean": 10, "stddev": 1}, + ], + [ + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=5, stddev=2), + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=6, stddev=3), + ValueScaling.create(type=ValueScalingType.Z_SCORE, mean=10, stddev=1), + ], + ), + ( + True, + "clip", + [3, 4, 5], + None, + [ + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 3, 0, 1)", + ), + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 4, 0, 1)", + ), + ValueScaling.create( + type=ValueScalingType.PROCESSING, + format="gdal-calc", + expression="numpy.clip(A / 5, 0, 1)", + ), + ], + ), + ), +) +def test_migration_1_3_to_1_4_value_scaling_asset( + norm_by_channel: bool | None, + norm_type: str | None, + norm_clip: list[int] | None, + statistics: list[dict[str, Any]] | None, + value_scaling: list[ValueScaling] | None, +) -> None: + data: dict[str, Any] = { + "type": "Feature", + "properties": {}, + "assets": {"asset1": {"href": "https://example.com", "roles": ["mlm:model"]}}, + } + MLMExtensionHooks._migrate_1_3_to_1_4(data) # nothing is supposed to happen here + + input_obj: dict[str, Any] = {} + if norm_by_channel is not None: + input_obj["norm_by_channel"] = norm_by_channel + if norm_type is not None: + input_obj["norm_type"] = norm_type + if norm_clip is not None: + input_obj["norm_clip"] = norm_clip + if statistics is not None: + input_obj["statistics"] = statistics + + data["assets"]["asset1"]["mlm:input"] = [] + data["assets"]["asset1"]["mlm:input"].append(input_obj) + + MLMExtensionHooks._migrate_1_3_to_1_4(data) + + assert "mlm:input" not in data["assets"]["asset1"] + + if norm_type is not None and value_scaling is not None: + assert len(data["properties"]["mlm:input"][0]["value_scaling"]) == len( + value_scaling + ) + assert data["properties"]["mlm:input"][0]["value_scaling"] == [ + obj.to_dict() for obj in value_scaling + ] + + new_input_obj = data["properties"]["mlm:input"][0] assert "norm_by_channel" not in new_input_obj assert "norm_type" not in new_input_obj assert "norm_clip" not in new_input_obj @@ -1217,14 +1648,19 @@ def test_migration_1_3_to_1_4_value_scaling( ("l1", "l2", "l2sqr", "hamming", "hamming2", "type-mask", "relative", "inf"), ) def test_migration_1_3_to_1_4_failure(norm_type: str) -> None: - data: dict[str, Any] = {"properties": {"mlm:input": [{"norm_type": norm_type}]}} + # test that previously supported but now unsupported types raise an error + data: dict[str, Any] = { + "type": "Feature", + "properties": {"mlm:input": [{"norm_type": norm_type}]}, + } with pytest.raises(NotImplementedError): MLMExtensionHooks._migrate_1_3_to_1_4(data) -def test_migration_1_3_to_1_4_assets() -> None: +def test_migration_1_3_to_1_4_assets_item() -> None: data: dict[str, Any] = { + "type": "Feature", "properties": {}, "assets": { "asset1": { @@ -1252,3 +1688,34 @@ def test_migration_1_3_to_1_4_assets() -> None: assert "mlm:hyperparameters" in data["properties"] assert "mlm:artifact_type" in data["assets"]["asset1"] + + +def test_migration_1_3_to_1_4_collection() -> None: + data: dict[str, Any] = { + "type": "Collection", + "assets": { + "asset1": { + "mlm:name": "asdf", + "mlm:input": {}, + "mlm:output": {}, + "mlm:hyperparameters": {}, + "roles": ["mlm:model"], + } + }, + } + + MLMExtensionHooks._migrate_1_3_to_1_4(data) + + assert "mlm:name" not in data["assets"]["asset1"] + assert "mlm:name" in data + + assert "mlm:input" not in data["assets"]["asset1"] + assert "mlm:input" in data + + assert "mlm:output" not in data["assets"]["asset1"] + assert "mlm:output" in data + + assert "mlm:hyperparameters" not in data["assets"]["asset1"] + assert "mlm:hyperparameters" in data + + assert "mlm:artifact_type" in data["assets"]["asset1"] From 9c24f6410802e09015d271f16c5781be18c7f109 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 29 Apr 2025 10:59:04 +0200 Subject: [PATCH 58/68] added types-requests --- pyproject.toml | 1 + uv.lock | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 74bf4d847..7d741bb35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -53,6 +53,7 @@ dev = [ "types-jsonschema>=4.23.0.20240813", "types-orjson>=3.6.2", "types-python-dateutil>=2.9.0.20241003", + "types-requests>=2.32.0.20250328", "types-urllib3>=1.26.25.14", "virtualenv>=20.26.6", ] diff --git a/uv.lock b/uv.lock index a7d4cb4a8..cc4a64401 100644 --- a/uv.lock +++ b/uv.lock @@ -1989,6 +1989,7 @@ dev = [ { name = "types-jsonschema" }, { name = "types-orjson" }, { name = "types-python-dateutil" }, + { name = "types-requests" }, { name = "types-urllib3" }, { name = "virtualenv" }, ] @@ -2041,6 +2042,7 @@ dev = [ { name = "types-jsonschema", specifier = ">=4.23.0.20240813" }, { name = "types-orjson", specifier = ">=3.6.2" }, { name = "types-python-dateutil", specifier = ">=2.9.0.20241003" }, + { name = "types-requests", specifier = ">=2.32.0.20250328" }, { name = "types-urllib3", specifier = ">=1.26.25.14" }, { name = "virtualenv", specifier = ">=20.26.6" }, ] @@ -2923,6 +2925,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0f/b3/ca41df24db5eb99b00d97f89d7674a90cb6b3134c52fb8121b6d8d30f15c/types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53", size = 14384 }, ] +[[package]] +name = "types-requests" +version = "2.32.0.20250328" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/00/7d/eb174f74e3f5634eaacb38031bbe467dfe2e545bc255e5c90096ec46bc46/types_requests-2.32.0.20250328.tar.gz", hash = "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32", size = 22995 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/15/3700282a9d4ea3b37044264d3e4d1b1f0095a4ebf860a99914fd544e3be3/types_requests-2.32.0.20250328-py3-none-any.whl", hash = "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", size = 20663 }, +] + [[package]] name = "types-urllib3" version = "1.26.25.14" From 4ef3a479e2284adaf5740d32388ae685f039246d Mon Sep 17 00:00:00 2001 From: = Date: Tue, 29 Apr 2025 10:59:27 +0200 Subject: [PATCH 59/68] added tests for migration --- ...sion}-examples-item_basic.json-1.0.0].yaml | 110 ++++++++ ...sion}-examples-item_basic.json-1.1.0].yaml | 110 ++++++++ ...sion}-examples-item_basic.json-1.2.0].yaml | 110 ++++++++ ...sion}-examples-item_basic.json-1.3.0].yaml | 110 ++++++++ ...n}-examples-item_eo_bands.json-1.0.0].yaml | 255 ++++++++++++++++++ ...n}-examples-item_eo_bands.json-1.1.0].yaml | 209 ++++++++++++++ ...n}-examples-item_eo_bands.json-1.2.0].yaml | 209 ++++++++++++++ ...n}-examples-item_eo_bands.json-1.3.0].yaml | 209 ++++++++++++++ ...n}-examples-item_multi_io.json-1.0.0].yaml | 156 +++++++++++ ...n}-examples-item_multi_io.json-1.1.0].yaml | 162 +++++++++++ ...n}-examples-item_multi_io.json-1.2.0].yaml | 162 +++++++++++ ...n}-examples-item_multi_io.json-1.3.0].yaml | 166 ++++++++++++ ...xamples-item_raster_bands.json-1.0.0].yaml | 194 +++++++++++++ ...xamples-item_raster_bands.json-1.1.0].yaml | 194 +++++++++++++ ...xamples-item_raster_bands.json-1.2.0].yaml | 194 +++++++++++++ ...xamples-item_raster_bands.json-1.3.0].yaml | 201 ++++++++++++++ tests/extensions/test_mlm.py | 43 +++ 17 files changed, 2794 insertions(+) create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.0.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.1.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.2.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.3.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.0.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.1.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.2.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.3.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.0.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.1.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.2.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.3.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.0.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.1.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.2.0].yaml create mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.3.0].yaml diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.0.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.0.0].yaml new file mode 100644 index 000000000..6a337b634 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.0.0].yaml @@ -0,0 +1,110 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.0.0/examples/item_basic.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.0.0/schema.json\"\n + \ ],\n \"type\": \"Feature\",\n \"id\": \"example-model\",\n \"collection\": + \"ml-model-examples\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": + [\n [\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ]\n ]\n ]\n },\n \"bbox\": [\n -7.882190080512502,\n + \ 37.13739173208318,\n 27.911651652899923,\n 58.21798141355221\n ],\n + \ \"properties\": {\n \"description\": \"Basic STAC Item with only the + MLM extension and no other extension cross-references.\",\n \"datetime\": + null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n \"end_datetime\": + \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"example-model\",\n \"mlm:tasks\": + [\n \"classification\"\n ],\n \"mlm:architecture\": \"ResNet\",\n + \ \"mlm:input\": [\n {\n \"name\": \"Model with RGB input that + does not refer to any band.\",\n \"bands\": [],\n \"input\": + {\n \"shape\": [\n -1,\n 3,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n }\n }\n ],\n + \ \"mlm:output\": [\n {\n \"name\": \"classification\",\n \"tasks\": + [\n \"classification\"\n ],\n \"result\": {\n \"shape\": + [\n -1,\n 1\n ],\n \"dim_order\": + [\n \"batch\",\n \"class\"\n ],\n \"data_type\": + \"uint8\"\n },\n \"classification_classes\": [\n {\n + \ \"value\": 0,\n \"name\": \"BACKGROUND\",\n \"description\": + \"Background non-city.\",\n \"color_hint\": [\n 0,\n + \ 0,\n 0\n ]\n },\n {\n + \ \"value\": 1,\n \"name\": \"CITY\",\n \"description\": + \"A city is detected.\",\n \"color_hint\": [\n 0,\n + \ 0,\n 255\n ]\n }\n ]\n + \ }\n ]\n },\n \"assets\": {\n \"model\": {\n \"href\": \"https://huggingface.co/example/model-card\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"Example + model.\",\n \"type\": \"text/html\",\n \"roles\": [\n \"mlm:model\"\n + \ ]\n }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n + \ \"href\": \"./collection.json\",\n \"type\": \"application/json\"\n + \ },\n {\n \"rel\": \"self\",\n \"href\": \"./item_basic.json\",\n + \ \"type\": \"application/geo+json\"\n }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '931' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:25 GMT + ETag: + - W/"06b55e576e4efab26350d8d7551913db9419014d18bff024af7e5818a2366f4b" + Expires: + - Tue, 29 Apr 2025 08:35:25 GMT + Source-Age: + - '120' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - cbb71f87565b003068eeb6cf1d44e3e92f0b75fb + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - EAA8:1C62A6:B8FA8E:D1A987:681087C4 + X-Served-By: + - cache-fra-etou8220124-FRA + X-Timer: + - S1745915426.697783,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.1.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.1.0].yaml new file mode 100644 index 000000000..572c95588 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.1.0].yaml @@ -0,0 +1,110 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.1.0/examples/item_basic.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.1.0/schema.json\"\n + \ ],\n \"type\": \"Feature\",\n \"id\": \"example-model\",\n \"collection\": + \"ml-model-examples\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": + [\n [\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ]\n ]\n ]\n },\n \"bbox\": [\n -7.882190080512502,\n + \ 37.13739173208318,\n 27.911651652899923,\n 58.21798141355221\n ],\n + \ \"properties\": {\n \"description\": \"Basic STAC Item with only the + MLM extension and no other extension cross-references.\",\n \"datetime\": + null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n \"end_datetime\": + \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"example-model\",\n \"mlm:tasks\": + [\n \"classification\"\n ],\n \"mlm:architecture\": \"ResNet\",\n + \ \"mlm:input\": [\n {\n \"name\": \"Model with RGB input that + does not refer to any band.\",\n \"bands\": [],\n \"input\": + {\n \"shape\": [\n -1,\n 3,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n }\n }\n ],\n + \ \"mlm:output\": [\n {\n \"name\": \"classification\",\n \"tasks\": + [\n \"classification\"\n ],\n \"result\": {\n \"shape\": + [\n -1,\n 1\n ],\n \"dim_order\": + [\n \"batch\",\n \"class\"\n ],\n \"data_type\": + \"uint8\"\n },\n \"classification_classes\": [\n {\n + \ \"value\": 0,\n \"name\": \"BACKGROUND\",\n \"description\": + \"Background non-city.\",\n \"color_hint\": [\n 0,\n + \ 0,\n 0\n ]\n },\n {\n + \ \"value\": 1,\n \"name\": \"CITY\",\n \"description\": + \"A city is detected.\",\n \"color_hint\": [\n 0,\n + \ 0,\n 255\n ]\n }\n ]\n + \ }\n ]\n },\n \"assets\": {\n \"model\": {\n \"href\": \"https://huggingface.co/example/model-card\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"Example + model.\",\n \"type\": \"text/html\",\n \"roles\": [\n \"mlm:model\"\n + \ ]\n }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n + \ \"href\": \"./collection.json\",\n \"type\": \"application/json\"\n + \ },\n {\n \"rel\": \"self\",\n \"href\": \"./item_basic.json\",\n + \ \"type\": \"application/geo+json\"\n }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '933' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:25 GMT + ETag: + - W/"60cdf768894e2f4eaafeb01906736c2bb03903640b6615f1cea0dd4ace03a6d3" + Expires: + - Tue, 29 Apr 2025 08:35:25 GMT + Source-Age: + - '120' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - fdaeae3f7f917f5f4c801f2719acdc47c172defb + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 5BEA:6EEED:C4E1E6:DE2B23:68108DA9 + X-Served-By: + - cache-fra-etou8220128-FRA + X-Timer: + - S1745915426.768525,VS0,VE2 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.2.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.2.0].yaml new file mode 100644 index 000000000..d209d5ba6 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.2.0].yaml @@ -0,0 +1,110 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.2.0/examples/item_basic.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.2.0/schema.json\"\n + \ ],\n \"type\": \"Feature\",\n \"id\": \"example-model\",\n \"collection\": + \"ml-model-examples\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": + [\n [\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ]\n ]\n ]\n },\n \"bbox\": [\n -7.882190080512502,\n + \ 37.13739173208318,\n 27.911651652899923,\n 58.21798141355221\n ],\n + \ \"properties\": {\n \"description\": \"Basic STAC Item with only the + MLM extension and no other extension cross-references.\",\n \"datetime\": + null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n \"end_datetime\": + \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"example-model\",\n \"mlm:tasks\": + [\n \"classification\"\n ],\n \"mlm:architecture\": \"ResNet\",\n + \ \"mlm:input\": [\n {\n \"name\": \"Model with RGB input that + does not refer to any band.\",\n \"bands\": [],\n \"input\": + {\n \"shape\": [\n -1,\n 3,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n }\n }\n ],\n + \ \"mlm:output\": [\n {\n \"name\": \"classification\",\n \"tasks\": + [\n \"classification\"\n ],\n \"result\": {\n \"shape\": + [\n -1,\n 1\n ],\n \"dim_order\": + [\n \"batch\",\n \"class\"\n ],\n \"data_type\": + \"uint8\"\n },\n \"classification_classes\": [\n {\n + \ \"value\": 0,\n \"name\": \"BACKGROUND\",\n \"description\": + \"Background non-city.\",\n \"color_hint\": [\n 0,\n + \ 0,\n 0\n ]\n },\n {\n + \ \"value\": 1,\n \"name\": \"CITY\",\n \"description\": + \"A city is detected.\",\n \"color_hint\": [\n 0,\n + \ 0,\n 255\n ]\n }\n ]\n + \ }\n ]\n },\n \"assets\": {\n \"model\": {\n \"href\": \"https://huggingface.co/example/model-card\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"Example + model.\",\n \"type\": \"text/html\",\n \"roles\": [\n \"mlm:model\"\n + \ ]\n }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n + \ \"href\": \"./collection.json\",\n \"type\": \"application/json\"\n + \ },\n {\n \"rel\": \"self\",\n \"href\": \"./item_basic.json\",\n + \ \"type\": \"application/geo+json\"\n }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '934' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:25 GMT + ETag: + - W/"4214e4c8a7e047eaff361f43a1e2ce58d4e403d064c4a0a9fc870a0f02f3ffd4" + Expires: + - Tue, 29 Apr 2025 08:35:25 GMT + Source-Age: + - '119' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - dea14443ef6f269978dd64c2bb7cf0811dcd3102 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 6822:112C:B53704:CD19F6:68108DA9 + X-Served-By: + - cache-fra-etou8220154-FRA + X-Timer: + - S1745915426.841928,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.3.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.3.0].yaml new file mode 100644 index 000000000..6e9c29315 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.3.0].yaml @@ -0,0 +1,110 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.3.0/examples/item_basic.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.3.0/schema.json\"\n + \ ],\n \"type\": \"Feature\",\n \"id\": \"example-model\",\n \"collection\": + \"ml-model-examples\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": + [\n [\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ]\n ]\n ]\n },\n \"bbox\": [\n -7.882190080512502,\n + \ 37.13739173208318,\n 27.911651652899923,\n 58.21798141355221\n ],\n + \ \"properties\": {\n \"description\": \"Basic STAC Item with only the + MLM extension and no other extension cross-references.\",\n \"datetime\": + null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n \"end_datetime\": + \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"example-model\",\n \"mlm:tasks\": + [\n \"classification\"\n ],\n \"mlm:architecture\": \"ResNet\",\n + \ \"mlm:input\": [\n {\n \"name\": \"Model with RGB input that + does not refer to any band.\",\n \"bands\": [],\n \"input\": + {\n \"shape\": [\n -1,\n 3,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n }\n }\n ],\n + \ \"mlm:output\": [\n {\n \"name\": \"classification\",\n \"tasks\": + [\n \"classification\"\n ],\n \"result\": {\n \"shape\": + [\n -1,\n 1\n ],\n \"dim_order\": + [\n \"batch\",\n \"class\"\n ],\n \"data_type\": + \"uint8\"\n },\n \"classification_classes\": [\n {\n + \ \"value\": 0,\n \"name\": \"BACKGROUND\",\n \"description\": + \"Background non-city.\",\n \"color_hint\": [\n 0,\n + \ 0,\n 0\n ]\n },\n {\n + \ \"value\": 1,\n \"name\": \"CITY\",\n \"description\": + \"A city is detected.\",\n \"color_hint\": [\n 0,\n + \ 0,\n 255\n ]\n }\n ]\n + \ }\n ]\n },\n \"assets\": {\n \"model\": {\n \"href\": \"https://huggingface.co/example/model-card\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"Example + model.\",\n \"type\": \"text/html\",\n \"roles\": [\n \"mlm:model\"\n + \ ]\n }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n + \ \"href\": \"./collection.json\",\n \"type\": \"application/json\"\n + \ },\n {\n \"rel\": \"self\",\n \"href\": \"./item_basic.json\",\n + \ \"type\": \"application/geo+json\"\n }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '934' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:25 GMT + ETag: + - W/"2cdd4c4b8a96681920b5e858cc552b3b19ca2a40e211d9ef29e3863f040782c2" + Expires: + - Tue, 29 Apr 2025 08:35:25 GMT + Source-Age: + - '118' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - d9048815950a3df8944faf506270689c94e931a6 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 6444:183F83:C9B8F0:E30245:68108DA9 + X-Served-By: + - cache-fra-etou8220020-FRA + X-Timer: + - S1745915426.908529,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.0.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.0.0].yaml new file mode 100644 index 000000000..47ab3e239 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.0.0].yaml @@ -0,0 +1,255 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.0.0/examples/item_eo_bands.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/eo/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n + \ \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ ],\n \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 + Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n + \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n + \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n + \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 13,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n },\n \"norm_by_channel\": + true,\n \"norm_type\": \"z-score\",\n \"resize_type\": null,\n + \ \"statistics\": [\n {\n \"mean\": 1354.40546513,\n + \ \"stddev\": 245.71762908\n },\n {\n \"mean\": + 1118.24399958,\n \"stddev\": 333.00778264\n },\n {\n + \ \"mean\": 1042.92983953,\n \"stddev\": 395.09249139\n + \ },\n {\n \"mean\": 947.62620298,\n \"stddev\": + 593.75055589\n },\n {\n \"mean\": 1199.47283961,\n + \ \"stddev\": 566.4170017\n },\n {\n \"mean\": + 1999.79090914,\n \"stddev\": 861.18399006\n },\n {\n + \ \"mean\": 2369.22292565,\n \"stddev\": 1086.63139075\n + \ },\n {\n \"mean\": 2296.82608323,\n \"stddev\": + 1117.98170791\n },\n {\n \"mean\": 732.08340178,\n + \ \"stddev\": 404.91978886\n },\n {\n \"mean\": + 12.11327804,\n \"stddev\": 4.77584468\n },\n {\n + \ \"mean\": 1819.01027855,\n \"stddev\": 1002.58768311\n + \ },\n {\n \"mean\": 1118.92391149,\n \"stddev\": + 761.30323499\n },\n {\n \"mean\": 2594.14080798,\n + \ \"stddev\": 1231.58581042\n }\n ],\n \"pre_processing_function\": + {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"classification\",\n \"tasks\": [\n \"classification\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 10\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"float32\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n + \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 1,\n \"name\": + \"Forest\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 2,\n \"name\": \"Herbaceous + Vegetation\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 3,\n \"name\": + \"Highway\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 4,\n \"name\": \"Industrial + Buildings\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n },\n {\n \"value\": + 6,\n \"name\": \"Permanent Crop\",\n \"description\": + null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 7,\n \"name\": + \"Residential Buildings\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 8,\n \"name\": + \"River\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": + null\n }\n ],\n \"eo:bands\": [\n {\n \"name\": \"B01\",\n + \ \"common_name\": \"coastal\",\n \"description\": \"Coastal + aerosol (band 1)\",\n \"center_wavelength\": 0.443,\n \"full_width_half_max\": + 0.027\n },\n {\n \"name\": \"B02\",\n \"common_name\": + \"blue\",\n \"description\": \"Blue (band 2)\",\n \"center_wavelength\": + 0.49,\n \"full_width_half_max\": 0.098\n },\n {\n \"name\": + \"B03\",\n \"common_name\": \"green\",\n \"description\": \"Green + (band 3)\",\n \"center_wavelength\": 0.56,\n \"full_width_half_max\": + 0.045\n },\n {\n \"name\": \"B04\",\n \"common_name\": + \"red\",\n \"description\": \"Red (band 4)\",\n \"center_wavelength\": + 0.665,\n \"full_width_half_max\": 0.038\n },\n {\n \"name\": + \"B05\",\n \"common_name\": \"rededge\",\n \"description\": + \"Red edge 1 (band 5)\",\n \"center_wavelength\": 0.704,\n \"full_width_half_max\": + 0.019\n },\n {\n \"name\": \"B06\",\n \"common_name\": + \"rededge\",\n \"description\": \"Red edge 2 (band 6)\",\n \"center_wavelength\": + 0.74,\n \"full_width_half_max\": 0.018\n },\n {\n \"name\": + \"B07\",\n \"common_name\": \"rededge\",\n \"description\": + \"Red edge 3 (band 7)\",\n \"center_wavelength\": 0.783,\n \"full_width_half_max\": + 0.028\n },\n {\n \"name\": \"B08\",\n \"common_name\": + \"nir\",\n \"description\": \"NIR 1 (band 8)\",\n \"center_wavelength\": + 0.842,\n \"full_width_half_max\": 0.145\n },\n {\n \"name\": + \"B8A\",\n \"common_name\": \"nir08\",\n \"description\": \"NIR + 2 (band 8A)\",\n \"center_wavelength\": 0.865,\n \"full_width_half_max\": + 0.033\n },\n {\n \"name\": \"B09\",\n \"common_name\": + \"nir09\",\n \"description\": \"NIR 3 (band 9)\",\n \"center_wavelength\": + 0.945,\n \"full_width_half_max\": 0.026\n },\n {\n \"name\": + \"B10\",\n \"common_name\": \"cirrus\",\n \"description\": \"SWIR + - Cirrus (band 10)\",\n \"center_wavelength\": 1.375,\n \"full_width_half_max\": + 0.026\n },\n {\n \"name\": \"B11\",\n \"common_name\": + \"swir16\",\n \"description\": \"SWIR 1 (band 11)\",\n \"center_wavelength\": + 1.61,\n \"full_width_half_max\": 0.143\n },\n {\n \"name\": + \"B12\",\n \"common_name\": \"swir22\",\n \"description\": \"SWIR + 2 (band 12)\",\n \"center_wavelength\": 2.19,\n \"full_width_half_max\": + 0.242\n }\n ],\n \"raster:bands\": [\n {\n \"name\": + \"B01\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B02\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B03\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B04\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B05\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B06\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B07\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B08\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B8A\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B09\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B10\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B11\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B12\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"assets\": {\n \"weights\": + {\n \"href\": \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-18 classification model trained on normalized Sentinel-2 imagery with + Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; + application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n + \ ],\n \"$comment\": \"Following 'eo:bands' is required to fulfil + schema validation of 'eo' extension.\",\n \"eo:bands\": [\n {\n + \ \"name\": \"coastal\"\n },\n {\n \"name\": + \"blue\"\n },\n {\n \"name\": \"green\"\n },\n + \ {\n \"name\": \"red\"\n },\n {\n \"name\": + \"rededge1\"\n },\n {\n \"name\": \"rededge2\"\n },\n + \ {\n \"name\": \"rededge3\"\n },\n {\n \"name\": + \"nir\"\n },\n {\n \"name\": \"nir08\"\n },\n + \ {\n \"name\": \"nir09\"\n },\n {\n \"name\": + \"cirrus\"\n },\n {\n \"name\": \"swir16\"\n },\n + \ {\n \"name\": \"swir22\"\n }\n ]\n },\n \"source_code\": + {\n \"href\": \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n + \ \"title\": \"Model implementation.\",\n \"description\": \"Source + code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": + [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n + \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": + \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n + \ \"rel\": \"self\",\n \"href\": \"./item_eo_bands.json\",\n \"type\": + \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '2632' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:25 GMT + ETag: + - W/"550b55c5cc7ce92969a64bfa5a8325141386d01c7b097671b9d659f7c70764b5" + Expires: + - Tue, 29 Apr 2025 08:35:25 GMT + Source-Age: + - '118' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 044807e148068029fbd9f42554c471a3460a5b19 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - EF89:6EEED:B9770B:D225ED:681087C7 + X-Served-By: + - cache-fra-etou8220031-FRA + X-Timer: + - S1745915426.975329,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.1.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.1.0].yaml new file mode 100644 index 000000000..7a6c18763 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.1.0].yaml @@ -0,0 +1,209 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.1.0/examples/item_eo_bands.json + response: + body: + string: "{\n \"$comment\": \"Demonstrate the use of MLM and EO for bands description, + with EO bands directly in the Model Asset.\",\n \"stac_version\": \"1.0.0\",\n + \ \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.1.0/schema.json\",\n + \ \"https://stac-extensions.github.io/eo/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n + \ \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ ],\n \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 + Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n + \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n + \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n + \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 13,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n },\n \"norm_by_channel\": + true,\n \"norm_type\": \"z-score\",\n \"resize_type\": null,\n + \ \"statistics\": [\n {\n \"mean\": 1354.40546513,\n + \ \"stddev\": 245.71762908\n },\n {\n \"mean\": + 1118.24399958,\n \"stddev\": 333.00778264\n },\n {\n + \ \"mean\": 1042.92983953,\n \"stddev\": 395.09249139\n + \ },\n {\n \"mean\": 947.62620298,\n \"stddev\": + 593.75055589\n },\n {\n \"mean\": 1199.47283961,\n + \ \"stddev\": 566.4170017\n },\n {\n \"mean\": + 1999.79090914,\n \"stddev\": 861.18399006\n },\n {\n + \ \"mean\": 2369.22292565,\n \"stddev\": 1086.63139075\n + \ },\n {\n \"mean\": 2296.82608323,\n \"stddev\": + 1117.98170791\n },\n {\n \"mean\": 732.08340178,\n + \ \"stddev\": 404.91978886\n },\n {\n \"mean\": + 12.11327804,\n \"stddev\": 4.77584468\n },\n {\n + \ \"mean\": 1819.01027855,\n \"stddev\": 1002.58768311\n + \ },\n {\n \"mean\": 1118.92391149,\n \"stddev\": + 761.30323499\n },\n {\n \"mean\": 2594.14080798,\n + \ \"stddev\": 1231.58581042\n }\n ],\n \"pre_processing_function\": + {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"classification\",\n \"tasks\": [\n \"classification\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 10\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"float32\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n + \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 1,\n \"name\": + \"Forest\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 2,\n \"name\": \"Herbaceous + Vegetation\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 3,\n \"name\": + \"Highway\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 4,\n \"name\": \"Industrial + Buildings\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n },\n {\n \"value\": + 6,\n \"name\": \"Permanent Crop\",\n \"description\": + null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 7,\n \"name\": + \"Residential Buildings\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 8,\n \"name\": + \"River\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": + null\n }\n ]\n },\n \"assets\": {\n \"weights\": {\n \"href\": + \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-18 classification model trained on normalized Sentinel-2 imagery with + Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; + application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n + \ ],\n \"$comment\": \"Following 'eo:bands' is required to fulfil + schema validation of 'eo' extension.\",\n \"eo:bands\": [\n {\n + \ \"name\": \"B01\",\n \"common_name\": \"coastal\",\n \"description\": + \"Coastal aerosol (band 1)\",\n \"center_wavelength\": 0.443,\n \"full_width_half_max\": + 0.027\n },\n {\n \"name\": \"B02\",\n \"common_name\": + \"blue\",\n \"description\": \"Blue (band 2)\",\n \"center_wavelength\": + 0.49,\n \"full_width_half_max\": 0.098\n },\n {\n \"name\": + \"B03\",\n \"common_name\": \"green\",\n \"description\": + \"Green (band 3)\",\n \"center_wavelength\": 0.56,\n \"full_width_half_max\": + 0.045\n },\n {\n \"name\": \"B04\",\n \"common_name\": + \"red\",\n \"description\": \"Red (band 4)\",\n \"center_wavelength\": + 0.665,\n \"full_width_half_max\": 0.038\n },\n {\n + \ \"name\": \"B05\",\n \"common_name\": \"rededge\",\n \"description\": + \"Red edge 1 (band 5)\",\n \"center_wavelength\": 0.704,\n \"full_width_half_max\": + 0.019\n },\n {\n \"name\": \"B06\",\n \"common_name\": + \"rededge\",\n \"description\": \"Red edge 2 (band 6)\",\n \"center_wavelength\": + 0.74,\n \"full_width_half_max\": 0.018\n },\n {\n \"name\": + \"B07\",\n \"common_name\": \"rededge\",\n \"description\": + \"Red edge 3 (band 7)\",\n \"center_wavelength\": 0.783,\n \"full_width_half_max\": + 0.028\n },\n {\n \"name\": \"B08\",\n \"common_name\": + \"nir\",\n \"description\": \"NIR 1 (band 8)\",\n \"center_wavelength\": + 0.842,\n \"full_width_half_max\": 0.145\n },\n {\n + \ \"name\": \"B8A\",\n \"common_name\": \"nir08\",\n \"description\": + \"NIR 2 (band 8A)\",\n \"center_wavelength\": 0.865,\n \"full_width_half_max\": + 0.033\n },\n {\n \"name\": \"B09\",\n \"common_name\": + \"nir09\",\n \"description\": \"NIR 3 (band 9)\",\n \"center_wavelength\": + 0.945,\n \"full_width_half_max\": 0.026\n },\n {\n + \ \"name\": \"B10\",\n \"common_name\": \"cirrus\",\n \"description\": + \"SWIR - Cirrus (band 10)\",\n \"center_wavelength\": 1.375,\n \"full_width_half_max\": + 0.026\n },\n {\n \"name\": \"B11\",\n \"common_name\": + \"swir16\",\n \"description\": \"SWIR 1 (band 11)\",\n \"center_wavelength\": + 1.61,\n \"full_width_half_max\": 0.143\n },\n {\n \"name\": + \"B12\",\n \"common_name\": \"swir22\",\n \"description\": + \"SWIR 2 (band 12)\",\n \"center_wavelength\": 2.19,\n \"full_width_half_max\": + 0.242\n }\n ]\n },\n \"source_code\": {\n \"href\": + \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n + \ \"title\": \"Model implementation.\",\n \"description\": \"Source + code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": + [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n + \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": + \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n + \ \"rel\": \"self\",\n \"href\": \"./item_eo_bands.json\",\n \"type\": + \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '2476' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:26 GMT + ETag: + - W/"538e8345f63973b9ad1ad9f7aaaa0dacd3ab3ffeea7255d6da75ff0299bd8896" + Expires: + - Tue, 29 Apr 2025 08:35:26 GMT + Source-Age: + - '118' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 24eec2b921a253b550ede66e2a1d9adc7bd2de44 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 5E94:6EEED:C4E611:DE2F9E:68108DAB + X-Served-By: + - cache-fra-etou8220158-FRA + X-Timer: + - S1745915426.042527,VS0,VE2 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.2.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.2.0].yaml new file mode 100644 index 000000000..b9de4ecf5 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.2.0].yaml @@ -0,0 +1,209 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.2.0/examples/item_eo_bands.json + response: + body: + string: "{\n \"$comment\": \"Demonstrate the use of MLM and EO for bands description, + with EO bands directly in the Model Asset.\",\n \"stac_version\": \"1.0.0\",\n + \ \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.2.0/schema.json\",\n + \ \"https://stac-extensions.github.io/eo/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n + \ \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ ],\n \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 + Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n + \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n + \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n + \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 13,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n },\n \"norm_by_channel\": + true,\n \"norm_type\": \"z-score\",\n \"resize_type\": null,\n + \ \"statistics\": [\n {\n \"mean\": 1354.40546513,\n + \ \"stddev\": 245.71762908\n },\n {\n \"mean\": + 1118.24399958,\n \"stddev\": 333.00778264\n },\n {\n + \ \"mean\": 1042.92983953,\n \"stddev\": 395.09249139\n + \ },\n {\n \"mean\": 947.62620298,\n \"stddev\": + 593.75055589\n },\n {\n \"mean\": 1199.47283961,\n + \ \"stddev\": 566.4170017\n },\n {\n \"mean\": + 1999.79090914,\n \"stddev\": 861.18399006\n },\n {\n + \ \"mean\": 2369.22292565,\n \"stddev\": 1086.63139075\n + \ },\n {\n \"mean\": 2296.82608323,\n \"stddev\": + 1117.98170791\n },\n {\n \"mean\": 732.08340178,\n + \ \"stddev\": 404.91978886\n },\n {\n \"mean\": + 12.11327804,\n \"stddev\": 4.77584468\n },\n {\n + \ \"mean\": 1819.01027855,\n \"stddev\": 1002.58768311\n + \ },\n {\n \"mean\": 1118.92391149,\n \"stddev\": + 761.30323499\n },\n {\n \"mean\": 2594.14080798,\n + \ \"stddev\": 1231.58581042\n }\n ],\n \"pre_processing_function\": + {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"classification\",\n \"tasks\": [\n \"classification\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 10\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"float32\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n + \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 1,\n \"name\": + \"Forest\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 2,\n \"name\": \"Herbaceous + Vegetation\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 3,\n \"name\": + \"Highway\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 4,\n \"name\": \"Industrial + Buildings\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n },\n {\n \"value\": + 6,\n \"name\": \"Permanent Crop\",\n \"description\": + null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 7,\n \"name\": + \"Residential Buildings\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 8,\n \"name\": + \"River\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": + null\n }\n ]\n },\n \"assets\": {\n \"weights\": {\n \"href\": + \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-18 classification model trained on normalized Sentinel-2 imagery with + Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; + application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n + \ ],\n \"$comment\": \"Following 'eo:bands' is required to fulfil + schema validation of 'eo' extension.\",\n \"eo:bands\": [\n {\n + \ \"name\": \"B01\",\n \"common_name\": \"coastal\",\n \"description\": + \"Coastal aerosol (band 1)\",\n \"center_wavelength\": 0.443,\n \"full_width_half_max\": + 0.027\n },\n {\n \"name\": \"B02\",\n \"common_name\": + \"blue\",\n \"description\": \"Blue (band 2)\",\n \"center_wavelength\": + 0.49,\n \"full_width_half_max\": 0.098\n },\n {\n \"name\": + \"B03\",\n \"common_name\": \"green\",\n \"description\": + \"Green (band 3)\",\n \"center_wavelength\": 0.56,\n \"full_width_half_max\": + 0.045\n },\n {\n \"name\": \"B04\",\n \"common_name\": + \"red\",\n \"description\": \"Red (band 4)\",\n \"center_wavelength\": + 0.665,\n \"full_width_half_max\": 0.038\n },\n {\n + \ \"name\": \"B05\",\n \"common_name\": \"rededge\",\n \"description\": + \"Red edge 1 (band 5)\",\n \"center_wavelength\": 0.704,\n \"full_width_half_max\": + 0.019\n },\n {\n \"name\": \"B06\",\n \"common_name\": + \"rededge\",\n \"description\": \"Red edge 2 (band 6)\",\n \"center_wavelength\": + 0.74,\n \"full_width_half_max\": 0.018\n },\n {\n \"name\": + \"B07\",\n \"common_name\": \"rededge\",\n \"description\": + \"Red edge 3 (band 7)\",\n \"center_wavelength\": 0.783,\n \"full_width_half_max\": + 0.028\n },\n {\n \"name\": \"B08\",\n \"common_name\": + \"nir\",\n \"description\": \"NIR 1 (band 8)\",\n \"center_wavelength\": + 0.842,\n \"full_width_half_max\": 0.145\n },\n {\n + \ \"name\": \"B8A\",\n \"common_name\": \"nir08\",\n \"description\": + \"NIR 2 (band 8A)\",\n \"center_wavelength\": 0.865,\n \"full_width_half_max\": + 0.033\n },\n {\n \"name\": \"B09\",\n \"common_name\": + \"nir09\",\n \"description\": \"NIR 3 (band 9)\",\n \"center_wavelength\": + 0.945,\n \"full_width_half_max\": 0.026\n },\n {\n + \ \"name\": \"B10\",\n \"common_name\": \"cirrus\",\n \"description\": + \"SWIR - Cirrus (band 10)\",\n \"center_wavelength\": 1.375,\n \"full_width_half_max\": + 0.026\n },\n {\n \"name\": \"B11\",\n \"common_name\": + \"swir16\",\n \"description\": \"SWIR 1 (band 11)\",\n \"center_wavelength\": + 1.61,\n \"full_width_half_max\": 0.143\n },\n {\n \"name\": + \"B12\",\n \"common_name\": \"swir22\",\n \"description\": + \"SWIR 2 (band 12)\",\n \"center_wavelength\": 2.19,\n \"full_width_half_max\": + 0.242\n }\n ]\n },\n \"source_code\": {\n \"href\": + \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n + \ \"title\": \"Model implementation.\",\n \"description\": \"Source + code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": + [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n + \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": + \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n + \ \"rel\": \"self\",\n \"href\": \"./item_eo_bands.json\",\n \"type\": + \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '2478' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:26 GMT + ETag: + - W/"90ccd29b9b5252a7bbcc61aa942d67f941d72bd60900fe3ecbc41c4afc124914" + Expires: + - Tue, 29 Apr 2025 08:35:26 GMT + Source-Age: + - '118' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 87443c348f0fef4024b71e8a678690f98fce5dc3 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 8A6E:1A88C7:CA3CAF:E38650:68108DAC + X-Served-By: + - cache-fra-etou8220022-FRA + X-Timer: + - S1745915426.110658,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.3.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.3.0].yaml new file mode 100644 index 000000000..b96072e8c --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.3.0].yaml @@ -0,0 +1,209 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.3.0/examples/item_eo_bands.json + response: + body: + string: "{\n \"$comment\": \"Demonstrate the use of MLM and EO for bands description, + with EO bands directly in the Model Asset.\",\n \"stac_version\": \"1.0.0\",\n + \ \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.3.0/schema.json\",\n + \ \"https://stac-extensions.github.io/eo/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n + \ \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ ],\n \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 + Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n + \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n + \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n + \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 13,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n },\n \"norm_by_channel\": + true,\n \"norm_type\": \"z-score\",\n \"resize_type\": null,\n + \ \"statistics\": [\n {\n \"mean\": 1354.40546513,\n + \ \"stddev\": 245.71762908\n },\n {\n \"mean\": + 1118.24399958,\n \"stddev\": 333.00778264\n },\n {\n + \ \"mean\": 1042.92983953,\n \"stddev\": 395.09249139\n + \ },\n {\n \"mean\": 947.62620298,\n \"stddev\": + 593.75055589\n },\n {\n \"mean\": 1199.47283961,\n + \ \"stddev\": 566.4170017\n },\n {\n \"mean\": + 1999.79090914,\n \"stddev\": 861.18399006\n },\n {\n + \ \"mean\": 2369.22292565,\n \"stddev\": 1086.63139075\n + \ },\n {\n \"mean\": 2296.82608323,\n \"stddev\": + 1117.98170791\n },\n {\n \"mean\": 732.08340178,\n + \ \"stddev\": 404.91978886\n },\n {\n \"mean\": + 12.11327804,\n \"stddev\": 4.77584468\n },\n {\n + \ \"mean\": 1819.01027855,\n \"stddev\": 1002.58768311\n + \ },\n {\n \"mean\": 1118.92391149,\n \"stddev\": + 761.30323499\n },\n {\n \"mean\": 2594.14080798,\n + \ \"stddev\": 1231.58581042\n }\n ],\n \"pre_processing_function\": + {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"classification\",\n \"tasks\": [\n \"classification\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 10\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"float32\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n + \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 1,\n \"name\": + \"Forest\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 2,\n \"name\": \"Herbaceous + Vegetation\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 3,\n \"name\": + \"Highway\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 4,\n \"name\": \"Industrial + Buildings\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n },\n {\n \"value\": + 6,\n \"name\": \"Permanent Crop\",\n \"description\": + null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 7,\n \"name\": + \"Residential Buildings\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 8,\n \"name\": + \"River\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": + null\n }\n ]\n },\n \"assets\": {\n \"weights\": {\n \"href\": + \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-18 classification model trained on normalized Sentinel-2 imagery with + Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; + application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n + \ ],\n \"$comment\": \"Following 'eo:bands' is required to fulfil + schema validation of 'eo' extension.\",\n \"eo:bands\": [\n {\n + \ \"name\": \"B01\",\n \"common_name\": \"coastal\",\n \"description\": + \"Coastal aerosol (band 1)\",\n \"center_wavelength\": 0.443,\n \"full_width_half_max\": + 0.027\n },\n {\n \"name\": \"B02\",\n \"common_name\": + \"blue\",\n \"description\": \"Blue (band 2)\",\n \"center_wavelength\": + 0.49,\n \"full_width_half_max\": 0.098\n },\n {\n \"name\": + \"B03\",\n \"common_name\": \"green\",\n \"description\": + \"Green (band 3)\",\n \"center_wavelength\": 0.56,\n \"full_width_half_max\": + 0.045\n },\n {\n \"name\": \"B04\",\n \"common_name\": + \"red\",\n \"description\": \"Red (band 4)\",\n \"center_wavelength\": + 0.665,\n \"full_width_half_max\": 0.038\n },\n {\n + \ \"name\": \"B05\",\n \"common_name\": \"rededge\",\n \"description\": + \"Red edge 1 (band 5)\",\n \"center_wavelength\": 0.704,\n \"full_width_half_max\": + 0.019\n },\n {\n \"name\": \"B06\",\n \"common_name\": + \"rededge\",\n \"description\": \"Red edge 2 (band 6)\",\n \"center_wavelength\": + 0.74,\n \"full_width_half_max\": 0.018\n },\n {\n \"name\": + \"B07\",\n \"common_name\": \"rededge\",\n \"description\": + \"Red edge 3 (band 7)\",\n \"center_wavelength\": 0.783,\n \"full_width_half_max\": + 0.028\n },\n {\n \"name\": \"B08\",\n \"common_name\": + \"nir\",\n \"description\": \"NIR 1 (band 8)\",\n \"center_wavelength\": + 0.842,\n \"full_width_half_max\": 0.145\n },\n {\n + \ \"name\": \"B8A\",\n \"common_name\": \"nir08\",\n \"description\": + \"NIR 2 (band 8A)\",\n \"center_wavelength\": 0.865,\n \"full_width_half_max\": + 0.033\n },\n {\n \"name\": \"B09\",\n \"common_name\": + \"nir09\",\n \"description\": \"NIR 3 (band 9)\",\n \"center_wavelength\": + 0.945,\n \"full_width_half_max\": 0.026\n },\n {\n + \ \"name\": \"B10\",\n \"common_name\": \"cirrus\",\n \"description\": + \"SWIR - Cirrus (band 10)\",\n \"center_wavelength\": 1.375,\n \"full_width_half_max\": + 0.026\n },\n {\n \"name\": \"B11\",\n \"common_name\": + \"swir16\",\n \"description\": \"SWIR 1 (band 11)\",\n \"center_wavelength\": + 1.61,\n \"full_width_half_max\": 0.143\n },\n {\n \"name\": + \"B12\",\n \"common_name\": \"swir22\",\n \"description\": + \"SWIR 2 (band 12)\",\n \"center_wavelength\": 2.19,\n \"full_width_half_max\": + 0.242\n }\n ]\n },\n \"source_code\": {\n \"href\": + \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n + \ \"title\": \"Model implementation.\",\n \"description\": \"Source + code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": + [\n \"mlm:source_code\",\n \"code\",\n \"metadata\"\n + \ ]\n }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n + \ \"href\": \"./collection.json\",\n \"type\": \"application/json\"\n + \ },\n {\n \"rel\": \"self\",\n \"href\": \"./item_eo_bands.json\",\n + \ \"type\": \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '2481' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:26 GMT + ETag: + - W/"a8e548ba2f956af68d095ed3395f558cd02489363df3b2b44052cc4a5a15653e" + Expires: + - Tue, 29 Apr 2025 08:35:26 GMT + Source-Age: + - '118' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 9f483102ad1f4e924b4bcd84a00606d99516dc4a + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - FCEF:117E:6F0439:7ED11A:68108DAC + X-Served-By: + - cache-fra-etou8220123-FRA + X-Timer: + - S1745915426.179302,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.0.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.0.0].yaml new file mode 100644 index 000000000..4339f6716 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.0.0].yaml @@ -0,0 +1,156 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.0.0/examples/item_multi_io.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n ],\n + \ \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"RGB\",\n \"bands\": + [\n \"B04\",\n \"B03\",\n \"B02\"\n ],\n + \ \"input\": {\n \"shape\": [\n -1,\n 3,\n + \ 64,\n 64\n ],\n \"dim_order\": [\n + \ \"batch\",\n \"channel\",\n \"height\",\n + \ \"width\"\n ],\n \"data_type\": \"uint16\"\n + \ },\n \"norm_by_channel\": false,\n \"norm_type\": null,\n + \ \"resize_type\": null\n },\n {\n \"name\": \"NDVI\",\n + \ \"bands\": [\n \"B04\",\n \"B08\"\n ],\n + \ \"pre_processing_function\": {\n \"format\": \"gdal-calc\",\n + \ \"expression\": \"(A - B) / (A + B)\"\n },\n \"input\": + {\n \"shape\": [\n -1,\n 1,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"ndvi\",\n \"height\",\n \"width\"\n ],\n + \ \"data_type\": \"uint16\"\n }\n }\n ],\n \"mlm:output\": + [\n {\n \"name\": \"vegetation-segmentation\",\n \"tasks\": + [\n \"semantic-segmentation\"\n ],\n \"result\": {\n + \ \"shape\": [\n -1,\n 1\n ],\n \"dim_order\": + [\n \"batch\",\n \"class\"\n ],\n \"data_type\": + \"uint8\"\n },\n \"classification_classes\": [\n {\n + \ \"value\": 0,\n \"name\": \"NON_VEGETATION\",\n \"description\": + \"background pixels\",\n \"color_hint\": null\n },\n {\n + \ \"value\": 1,\n \"name\": \"VEGETATION\",\n \"description\": + \"pixels where vegetation was detected\",\n \"color_hint\": [\n + \ 0,\n 255,\n 0\n ]\n }\n + \ ],\n \"post_processing_function\": null\n },\n {\n + \ \"name\": \"inverse-mask\",\n \"tasks\": [\n \"semantic-segmentation\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 1\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"uint8\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"NON_VEGETATION\",\n \"description\": + \"background pixels\",\n \"color_hint\": [\n 255,\n + \ 255,\n 255\n ]\n },\n {\n + \ \"value\": 1,\n \"name\": \"VEGETATION\",\n \"description\": + \"pixels where vegetation was detected\",\n \"color_hint\": [\n + \ 0,\n 0,\n 0\n ]\n }\n + \ ],\n \"post_processing_function\": {\n \"format\": + \"gdal-calc\",\n \"expression\": \"logical_not(A)\"\n }\n + \ }\n ],\n \"raster:bands\": [\n {\n \"name\": \"B02 + - blue\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B03 - + green\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B04 - + red\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B08 - + nir\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"assets\": {\n \"weights\": + {\n \"href\": \"https://huggingface.co/torchgeo/resnet50_sentinel2_rgb_moco/blob/main/resnet50_sentinel2_rgb_moco.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-50 classification model trained on Sentinel-2 RGB imagery with torchgeo.\",\n + \ \"type\": \"application/octet-stream; application=pytorch\",\n \"roles\": + [\n \"mlm:model\",\n \"mlm:weights\"\n ]\n }\n },\n + \ \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": \"./collection.json\",\n + \ \"type\": \"application/json\"\n },\n {\n \"rel\": \"self\",\n + \ \"href\": \"./item_multi_io.json\",\n \"type\": \"application/geo+json\"\n + \ },\n {\n \"rel\": \"derived_from\",\n \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1572' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:26 GMT + ETag: + - W/"b8e68364828f14a588c33c8a9e49382d8e69c2bbe5052e997e7a039f612178aa" + Expires: + - Tue, 29 Apr 2025 08:35:26 GMT + Source-Age: + - '117' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 34f627f96ddccc8c605c63326973840c9edb02a9 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 7B49:230D29:B4D608:CD825C:681087AA + X-Served-By: + - cache-fra-etou8220118-FRA + X-Timer: + - S1745915426.247149,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.1.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.1.0].yaml new file mode 100644 index 000000000..1ca0e4513 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.1.0].yaml @@ -0,0 +1,162 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.1.0/examples/item_multi_io.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.1.0/schema.json\",\n + \ \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n ],\n + \ \"type\": \"Feature\",\n \"id\": \"model-multi-input\",\n \"collection\": + \"ml-model-examples\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": + [\n [\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ]\n ]\n ]\n },\n \"bbox\": [\n -7.882190080512502,\n + \ 37.13739173208318,\n 27.911651652899923,\n 58.21798141355221\n ],\n + \ \"properties\": {\n \"description\": \"Generic model that employs multiple + input sources with different combination of bands.\",\n \"datetime\": null,\n + \ \"start_datetime\": \"1900-01-01T00:00:00Z\",\n \"end_datetime\": \"9999-12-31T23:59:59Z\",\n + \ \"mlm:name\": \"Resnet-18 Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n + \ \"classification\"\n ],\n \"mlm:architecture\": \"ResNet\",\n + \ \"mlm:framework\": \"pytorch\",\n \"mlm:framework_version\": \"2.1.2+cu121\",\n + \ \"file:size\": 43000000,\n \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": + 11700000,\n \"mlm:pretrained_source\": \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": + \"cuda\",\n \"mlm:accelerator_constrained\": false,\n \"mlm:accelerator_summary\": + \"Unknown\",\n \"mlm:batch_size_suggestion\": 256,\n \"mlm:input\": + [\n {\n \"name\": \"RGB\",\n \"bands\": [\n \"B04\",\n + \ \"B03\",\n \"B02\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 3,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"uint16\"\n },\n \"norm_by_channel\": + false,\n \"norm_type\": null,\n \"resize_type\": null\n },\n + \ {\n \"name\": \"NDVI\",\n \"bands\": [\n \"B04\",\n + \ \"B08\"\n ],\n \"pre_processing_function\": {\n \"format\": + \"gdal-calc\",\n \"expression\": \"(A - B) / (A + B)\"\n },\n + \ \"input\": {\n \"shape\": [\n -1,\n 1,\n + \ 64,\n 64\n ],\n \"dim_order\": [\n + \ \"batch\",\n \"ndvi\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"uint16\"\n }\n },\n {\n + \ \"name\": \"DEM\",\n \"description\": \"Digital elevation model. + Comes from another source than the Sentinel bands. Therefore, no 'bands' associated + to it.\",\n \"bands\": [],\n \"input\": {\n \"shape\": + [\n -1,\n 1,\n 64,\n 64\n ],\n + \ \"dim_order\": [\n \"batch\",\n \"ndvi\",\n + \ \"height\",\n \"width\"\n ],\n \"data_type\": + \"float32\"\n }\n }\n ],\n \"mlm:output\": [\n {\n + \ \"name\": \"vegetation-segmentation\",\n \"tasks\": [\n \"semantic-segmentation\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 1\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"uint8\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"NON_VEGETATION\",\n \"description\": + \"background pixels\",\n \"color_hint\": null\n },\n {\n + \ \"value\": 1,\n \"name\": \"VEGETATION\",\n \"description\": + \"pixels where vegetation was detected\",\n \"color_hint\": [\n + \ 0,\n 255,\n 0\n ]\n }\n + \ ],\n \"post_processing_function\": null\n },\n {\n + \ \"name\": \"inverse-mask\",\n \"tasks\": [\n \"semantic-segmentation\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 1\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"uint8\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"NON_VEGETATION\",\n \"description\": + \"background pixels\",\n \"color_hint\": [\n 255,\n + \ 255,\n 255\n ]\n },\n {\n + \ \"value\": 1,\n \"name\": \"VEGETATION\",\n \"description\": + \"pixels where vegetation was detected\",\n \"color_hint\": [\n + \ 0,\n 0,\n 0\n ]\n }\n + \ ],\n \"post_processing_function\": {\n \"format\": + \"gdal-calc\",\n \"expression\": \"logical_not(A)\"\n }\n + \ }\n ],\n \"raster:bands\": [\n {\n \"name\": \"B02 + - blue\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B03 - + green\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B04 - + red\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B08 - + nir\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"assets\": {\n \"weights\": + {\n \"href\": \"https://huggingface.co/torchgeo/resnet50_sentinel2_rgb_moco/blob/main/resnet50_sentinel2_rgb_moco.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-50 classification model trained on Sentinel-2 RGB imagery with torchgeo.\",\n + \ \"type\": \"application/octet-stream; application=pytorch\",\n \"roles\": + [\n \"mlm:model\",\n \"mlm:weights\"\n ]\n }\n },\n + \ \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": \"./collection.json\",\n + \ \"type\": \"application/json\"\n },\n {\n \"rel\": \"self\",\n + \ \"href\": \"./item_multi_io.json\",\n \"type\": \"application/geo+json\"\n + \ },\n {\n \"rel\": \"derived_from\",\n \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1657' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:26 GMT + ETag: + - W/"ec315329c9c65996484f1dab9aa184c19906db9ce90cb2300b05235d2c2faddb" + Expires: + - Tue, 29 Apr 2025 08:35:26 GMT + Source-Age: + - '117' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - d1512db233f19b44296c533246a0494b557881f9 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 2968:1A88C7:CA3EAE:E38854:68108DAC + X-Served-By: + - cache-fra-etou8220163-FRA + X-Timer: + - S1745915426.311474,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.2.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.2.0].yaml new file mode 100644 index 000000000..c9f57f8bb --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.2.0].yaml @@ -0,0 +1,162 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.2.0/examples/item_multi_io.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.2.0/schema.json\",\n + \ \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n ],\n + \ \"type\": \"Feature\",\n \"id\": \"model-multi-input\",\n \"collection\": + \"ml-model-examples\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": + [\n [\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ]\n ]\n ]\n },\n \"bbox\": [\n -7.882190080512502,\n + \ 37.13739173208318,\n 27.911651652899923,\n 58.21798141355221\n ],\n + \ \"properties\": {\n \"description\": \"Generic model that employs multiple + input sources with different combination of bands.\",\n \"datetime\": null,\n + \ \"start_datetime\": \"1900-01-01T00:00:00Z\",\n \"end_datetime\": \"9999-12-31T23:59:59Z\",\n + \ \"mlm:name\": \"Resnet-18 Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n + \ \"classification\"\n ],\n \"mlm:architecture\": \"ResNet\",\n + \ \"mlm:framework\": \"pytorch\",\n \"mlm:framework_version\": \"2.1.2+cu121\",\n + \ \"file:size\": 43000000,\n \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": + 11700000,\n \"mlm:pretrained_source\": \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": + \"cuda\",\n \"mlm:accelerator_constrained\": false,\n \"mlm:accelerator_summary\": + \"Unknown\",\n \"mlm:batch_size_suggestion\": 256,\n \"mlm:input\": + [\n {\n \"name\": \"RGB\",\n \"bands\": [\n \"B04\",\n + \ \"B03\",\n \"B02\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 3,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"uint16\"\n },\n \"norm_by_channel\": + false,\n \"norm_type\": null,\n \"resize_type\": null\n },\n + \ {\n \"name\": \"NDVI\",\n \"bands\": [\n \"B04\",\n + \ \"B08\"\n ],\n \"pre_processing_function\": {\n \"format\": + \"gdal-calc\",\n \"expression\": \"(A - B) / (A + B)\"\n },\n + \ \"input\": {\n \"shape\": [\n -1,\n 1,\n + \ 64,\n 64\n ],\n \"dim_order\": [\n + \ \"batch\",\n \"ndvi\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"uint16\"\n }\n },\n {\n + \ \"name\": \"DEM\",\n \"description\": \"Digital elevation model. + Comes from another source than the Sentinel bands. Therefore, no 'bands' associated + to it.\",\n \"bands\": [],\n \"input\": {\n \"shape\": + [\n -1,\n 1,\n 64,\n 64\n ],\n + \ \"dim_order\": [\n \"batch\",\n \"ndvi\",\n + \ \"height\",\n \"width\"\n ],\n \"data_type\": + \"float32\"\n }\n }\n ],\n \"mlm:output\": [\n {\n + \ \"name\": \"vegetation-segmentation\",\n \"tasks\": [\n \"semantic-segmentation\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 1\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"uint8\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"NON_VEGETATION\",\n \"description\": + \"background pixels\",\n \"color_hint\": null\n },\n {\n + \ \"value\": 1,\n \"name\": \"VEGETATION\",\n \"description\": + \"pixels where vegetation was detected\",\n \"color_hint\": [\n + \ 0,\n 255,\n 0\n ]\n }\n + \ ],\n \"post_processing_function\": null\n },\n {\n + \ \"name\": \"inverse-mask\",\n \"tasks\": [\n \"semantic-segmentation\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 1\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"uint8\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"NON_VEGETATION\",\n \"description\": + \"background pixels\",\n \"color_hint\": [\n 255,\n + \ 255,\n 255\n ]\n },\n {\n + \ \"value\": 1,\n \"name\": \"VEGETATION\",\n \"description\": + \"pixels where vegetation was detected\",\n \"color_hint\": [\n + \ 0,\n 0,\n 0\n ]\n }\n + \ ],\n \"post_processing_function\": {\n \"format\": + \"gdal-calc\",\n \"expression\": \"logical_not(A)\"\n }\n + \ }\n ],\n \"raster:bands\": [\n {\n \"name\": \"B02 + - blue\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B03 - + green\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B04 - + red\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B08 - + nir\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"assets\": {\n \"weights\": + {\n \"href\": \"https://huggingface.co/torchgeo/resnet50_sentinel2_rgb_moco/blob/main/resnet50_sentinel2_rgb_moco.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-50 classification model trained on Sentinel-2 RGB imagery with torchgeo.\",\n + \ \"type\": \"application/octet-stream; application=pytorch\",\n \"roles\": + [\n \"mlm:model\",\n \"mlm:weights\"\n ]\n }\n },\n + \ \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": \"./collection.json\",\n + \ \"type\": \"application/json\"\n },\n {\n \"rel\": \"self\",\n + \ \"href\": \"./item_multi_io.json\",\n \"type\": \"application/geo+json\"\n + \ },\n {\n \"rel\": \"derived_from\",\n \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1659' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:26 GMT + ETag: + - W/"2615bf01905ff85470ba440bcbe17f54614988b43e703b01bcc24b09a5cba5df" + Expires: + - Tue, 29 Apr 2025 08:35:26 GMT + Source-Age: + - '117' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 618e8a2e789630ca9088426bec20bd24148f0408 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 8060:1BD2FE:C7A2CC:E0EC97:68108DA9 + X-Served-By: + - cache-fra-etou8220084-FRA + X-Timer: + - S1745915426.372021,VS0,VE1 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.3.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.3.0].yaml new file mode 100644 index 000000000..3522b97cb --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.3.0].yaml @@ -0,0 +1,166 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.3.0/examples/item_multi_io.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.3.0/schema.json\",\n + \ \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n ],\n + \ \"type\": \"Feature\",\n \"id\": \"model-multi-input\",\n \"collection\": + \"ml-model-examples\",\n \"geometry\": {\n \"type\": \"Polygon\",\n \"coordinates\": + [\n [\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 58.21798141355221\n + \ ],\n [\n 27.911651652899923,\n 37.13739173208318\n + \ ],\n [\n -7.882190080512502,\n 37.13739173208318\n + \ ]\n ]\n ]\n },\n \"bbox\": [\n -7.882190080512502,\n + \ 37.13739173208318,\n 27.911651652899923,\n 58.21798141355221\n ],\n + \ \"properties\": {\n \"description\": \"Generic model that employs multiple + input sources with different combination of bands, and some inputs without + any band at all.\",\n \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"RGB\",\n \"bands\": + [\n \"B04\",\n \"B03\",\n \"B02\"\n ],\n + \ \"input\": {\n \"shape\": [\n -1,\n 3,\n + \ 64,\n 64\n ],\n \"dim_order\": [\n + \ \"batch\",\n \"channel\",\n \"height\",\n + \ \"width\"\n ],\n \"data_type\": \"uint16\"\n + \ },\n \"norm_by_channel\": false,\n \"norm_type\": null,\n + \ \"resize_type\": null\n },\n {\n \"name\": \"NDVI\",\n + \ \"bands\": [\n \"B04\",\n \"B08\"\n ],\n + \ \"pre_processing_function\": {\n \"format\": \"gdal-calc\",\n + \ \"expression\": \"(A - B) / (A + B)\"\n },\n \"input\": + {\n \"shape\": [\n -1,\n 1,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"ndvi\",\n \"height\",\n \"width\"\n ],\n + \ \"data_type\": \"uint16\"\n }\n },\n {\n \"name\": + \"DEM\",\n \"description\": \"Digital elevation model. Comes from another + source than the Sentinel bands. Therefore, no 'bands' associated to it.\",\n + \ \"bands\": [],\n \"input\": {\n \"shape\": [\n -1,\n + \ 1,\n 64,\n 64\n ],\n \"dim_order\": + [\n \"batch\",\n \"ndvi\",\n \"height\",\n + \ \"width\"\n ],\n \"data_type\": \"float32\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"vegetation-segmentation\",\n \"tasks\": [\n \"semantic-segmentation\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 1\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"uint8\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"NON_VEGETATION\",\n \"description\": + \"background pixels\",\n \"color_hint\": null\n },\n {\n + \ \"value\": 1,\n \"name\": \"VEGETATION\",\n \"description\": + \"pixels where vegetation was detected\",\n \"color_hint\": [\n + \ 0,\n 255,\n 0\n ]\n }\n + \ ],\n \"post_processing_function\": null\n },\n {\n + \ \"name\": \"inverse-mask\",\n \"tasks\": [\n \"semantic-segmentation\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 1\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"uint8\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"NON_VEGETATION\",\n \"description\": + \"background pixels\",\n \"color_hint\": [\n 255,\n + \ 255,\n 255\n ]\n },\n {\n + \ \"value\": 1,\n \"name\": \"VEGETATION\",\n \"description\": + \"pixels where vegetation was detected\",\n \"color_hint\": [\n + \ 0,\n 0,\n 0\n ]\n }\n + \ ],\n \"post_processing_function\": {\n \"format\": + \"gdal-calc\",\n \"expression\": \"logical_not(A)\"\n }\n + \ }\n ]\n },\n \"assets\": {\n \"weights\": {\n \"href\": + \"https://huggingface.co/torchgeo/resnet50_sentinel2_rgb_moco/blob/main/resnet50_sentinel2_rgb_moco.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-50 classification model trained on Sentinel-2 RGB imagery with torchgeo.\",\n + \ \"type\": \"application/octet-stream; application=pytorch\",\n \"roles\": + [\n \"mlm:model\",\n \"mlm:weights\"\n ],\n \"raster:bands\": + [\n {\n \"name\": \"B02 - blue\",\n \"nodata\": 0,\n + \ \"data_type\": \"uint16\",\n \"bits_per_sample\": 15,\n + \ \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": + \"B03 - green\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n + \ \"bits_per_sample\": 15,\n \"spatial_resolution\": 10,\n + \ \"scale\": 0.0001,\n \"offset\": 0,\n \"unit\": + \"m\"\n },\n {\n \"name\": \"B04 - red\",\n \"nodata\": + 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": 15,\n + \ \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": + \"B08 - nir\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n + \ \"bits_per_sample\": 15,\n \"spatial_resolution\": 10,\n + \ \"scale\": 0.0001,\n \"offset\": 0,\n \"unit\": + \"m\"\n }\n ]\n }\n },\n \"links\": [\n {\n \"rel\": + \"collection\",\n \"href\": \"./collection.json\",\n \"type\": \"application/json\"\n + \ },\n {\n \"rel\": \"self\",\n \"href\": \"./item_multi_io.json\",\n + \ \"type\": \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1688' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:26 GMT + ETag: + - W/"8a7f7c7f98bd63496692bef25107288160f2763f8618048b0a4516885afcdd1d" + Expires: + - Tue, 29 Apr 2025 08:35:26 GMT + Source-Age: + - '116' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 4938841ea8995faf87ddb8a113c0a9870e55984a + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 1E08:5B0FB:C3A5C2:DCEFDB:68108DAD + X-Served-By: + - cache-fra-etou8220064-FRA + X-Timer: + - S1745915426.436633,VS0,VE7 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.0.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.0.0].yaml new file mode 100644 index 000000000..0711a31b0 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.0.0].yaml @@ -0,0 +1,194 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.0.0/examples/item_raster_bands.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n ],\n + \ \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 + Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n + \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n + \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n + \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 13,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n },\n \"norm_type\": + null,\n \"resize_type\": null,\n \"pre_processing_function\": + {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"classification\",\n \"tasks\": [\n \"classification\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 10\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"float32\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n + \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 1,\n \"name\": + \"Forest\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 2,\n \"name\": \"Herbaceous + Vegetation\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 3,\n \"name\": + \"Highway\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 4,\n \"name\": \"Industrial + Buildings\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n },\n {\n \"value\": + 6,\n \"name\": \"Permanent Crop\",\n \"description\": + null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 7,\n \"name\": + \"Residential Buildings\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 8,\n \"name\": + \"River\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": + null\n }\n ],\n \"raster:bands\": [\n {\n \"name\": + \"B01\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B02\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B03\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B04\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B05\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B06\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B07\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B08\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B8A\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B09\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B10\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B11\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B12\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"assets\": {\n \"weights\": + {\n \"href\": \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-18 classification model trained on normalized Sentinel-2 imagery with + Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; + application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n + \ ]\n },\n \"source_code\": {\n \"href\": \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n + \ \"title\": \"Model implementation.\",\n \"description\": \"Source + code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": + [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n + \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": + \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n + \ \"rel\": \"self\",\n \"href\": \"./item_raster_bands.json\",\n + \ \"type\": \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1785' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:26 GMT + ETag: + - W/"27c40ee3df9fd2dbdd00cea8e1d17b857c904ad289a6c2c0a9fe0cf407d24bf0" + Expires: + - Tue, 29 Apr 2025 08:35:26 GMT + Source-Age: + - '0' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 7a36ae34b722807e93dfe3f48c400e27363eb6c9 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - A91B:10BA70:79A76D:8B30B1:681087AC + X-Served-By: + - cache-fra-etou8220051-FRA + X-Timer: + - S1745915427.502040,VS0,VE227 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.1.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.1.0].yaml new file mode 100644 index 000000000..551738ac0 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.1.0].yaml @@ -0,0 +1,194 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.1.0/examples/item_raster_bands.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.1.0/schema.json\",\n + \ \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n ],\n + \ \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 + Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n + \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n + \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n + \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 13,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n },\n \"norm_type\": + null,\n \"resize_type\": null,\n \"pre_processing_function\": + {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"classification\",\n \"tasks\": [\n \"classification\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 10\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"float32\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n + \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 1,\n \"name\": + \"Forest\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 2,\n \"name\": \"Herbaceous + Vegetation\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 3,\n \"name\": + \"Highway\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 4,\n \"name\": \"Industrial + Buildings\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n },\n {\n \"value\": + 6,\n \"name\": \"Permanent Crop\",\n \"description\": + null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 7,\n \"name\": + \"Residential Buildings\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 8,\n \"name\": + \"River\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": + null\n }\n ],\n \"raster:bands\": [\n {\n \"name\": + \"B01\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B02\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B03\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B04\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B05\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B06\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B07\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B08\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B8A\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B09\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B10\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B11\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B12\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"assets\": {\n \"weights\": + {\n \"href\": \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-18 classification model trained on normalized Sentinel-2 imagery with + Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; + application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n + \ ]\n },\n \"source_code\": {\n \"href\": \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n + \ \"title\": \"Model implementation.\",\n \"description\": \"Source + code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": + [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n + \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": + \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n + \ \"rel\": \"self\",\n \"href\": \"./item_raster_bands.json\",\n + \ \"type\": \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1786' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:27 GMT + ETag: + - W/"5c22d84a039a443127d9a87f0dbfdee885ebebdcfd2e1ee13a7ade60744f8d7f" + Expires: + - Tue, 29 Apr 2025 08:35:27 GMT + Source-Age: + - '0' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - b542ececad89b84b1b8a4a56f77a9f239ae2b83b + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 47BB:112C:B623DB:CE12ED:68108E20 + X-Served-By: + - cache-fra-etou8220107-FRA + X-Timer: + - S1745915427.782391,VS0,VE241 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.2.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.2.0].yaml new file mode 100644 index 000000000..da2c6d7dd --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.2.0].yaml @@ -0,0 +1,194 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.2.0/examples/item_raster_bands.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.2.0/schema.json\",\n + \ \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n ],\n + \ \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 + Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n + \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n + \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n + \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 13,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n },\n \"norm_type\": + null,\n \"resize_type\": null,\n \"pre_processing_function\": + {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"classification\",\n \"tasks\": [\n \"classification\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 10\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"float32\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n + \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 1,\n \"name\": + \"Forest\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 2,\n \"name\": \"Herbaceous + Vegetation\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 3,\n \"name\": + \"Highway\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 4,\n \"name\": \"Industrial + Buildings\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n },\n {\n \"value\": + 6,\n \"name\": \"Permanent Crop\",\n \"description\": + null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 7,\n \"name\": + \"Residential Buildings\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 8,\n \"name\": + \"River\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": + null\n }\n ],\n \"raster:bands\": [\n {\n \"name\": + \"B01\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B02\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B03\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B04\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B05\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B06\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B07\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B08\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B8A\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B09\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B10\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B11\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B12\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"assets\": {\n \"weights\": + {\n \"href\": \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-18 classification model trained on normalized Sentinel-2 imagery with + Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; + application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n + \ ]\n },\n \"source_code\": {\n \"href\": \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n + \ \"title\": \"Model implementation.\",\n \"description\": \"Source + code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": + [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n + \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": + \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n + \ \"rel\": \"self\",\n \"href\": \"./item_raster_bands.json\",\n + \ \"type\": \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1788' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:29 GMT + ETag: + - W/"610fc710cdb0bf17cbf12e94e38d37a3c0111ca0df0c43ce55494f377c599a06" + Expires: + - Tue, 29 Apr 2025 08:35:29 GMT + Source-Age: + - '0' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 5a178a4473df1ba0e3f07ad649af698a1176e9d6 + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - EF74:230D29:C11A4E:DA7002:68108E23 + X-Served-By: + - cache-fra-etou8220096-FRA + X-Timer: + - S1745915427.183779,VS0,VE1942 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.3.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.3.0].yaml new file mode 100644 index 000000000..87b445e48 --- /dev/null +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.3.0].yaml @@ -0,0 +1,201 @@ +interactions: +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate + Connection: + - keep-alive + User-Agent: + - python-requests/2.32.3 + method: GET + uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.3.0/examples/item_raster_bands.json + response: + body: + string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.3.0/schema.json\",\n + \ \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n + \ \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n ],\n + \ \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n + \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": + \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n + \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n + \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": + [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n + \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced + from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n + \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n + \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 + Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n + \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n + \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n + \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": + \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": + false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": + 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 + Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n + \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n + \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n + \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n + \ \"shape\": [\n -1,\n 13,\n 64,\n + \ 64\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"channel\",\n \"height\",\n \"width\"\n + \ ],\n \"data_type\": \"float32\"\n },\n \"norm_type\": + null,\n \"resize_type\": null,\n \"pre_processing_function\": + {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n + \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": + \"classification\",\n \"tasks\": [\n \"classification\"\n + \ ],\n \"result\": {\n \"shape\": [\n -1,\n + \ 10\n ],\n \"dim_order\": [\n \"batch\",\n + \ \"class\"\n ],\n \"data_type\": \"float32\"\n + \ },\n \"classification_classes\": [\n {\n \"value\": + 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n + \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 1,\n \"name\": + \"Forest\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 2,\n \"name\": \"Herbaceous + Vegetation\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 3,\n \"name\": + \"Highway\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 4,\n \"name\": \"Industrial + Buildings\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n },\n {\n \"value\": + 6,\n \"name\": \"Permanent Crop\",\n \"description\": + null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": + false\n },\n {\n \"value\": 7,\n \"name\": + \"Residential Buildings\",\n \"description\": null,\n \"title\": + null,\n \"color_hint\": null,\n \"nodata\": false\n + \ },\n {\n \"value\": 8,\n \"name\": + \"River\",\n \"description\": null,\n \"title\": null,\n + \ \"color_hint\": null,\n \"nodata\": false\n },\n + \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n + \ \"description\": null,\n \"title\": null,\n \"color_hint\": + null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": + null\n }\n ]\n },\n \"assets\": {\n \"weights\": {\n \"href\": + \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n + \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A + Resnet-18 classification model trained on normalized Sentinel-2 imagery with + Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; + application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n + \ ],\n \"raster:bands\": [\n {\n \"name\": \"B01\",\n + \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": + 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n + \ \"offset\": 0,\n \"unit\": \"m\"\n },\n {\n + \ \"name\": \"B02\",\n \"nodata\": 0,\n \"data_type\": + \"uint16\",\n \"bits_per_sample\": 15,\n \"spatial_resolution\": + 10,\n \"scale\": 0.0001,\n \"offset\": 0,\n \"unit\": + \"m\"\n },\n {\n \"name\": \"B03\",\n \"nodata\": + 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": 15,\n + \ \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": + \"B04\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n + \ \"bits_per_sample\": 15,\n \"spatial_resolution\": 10,\n + \ \"scale\": 0.0001,\n \"offset\": 0,\n \"unit\": + \"m\"\n },\n {\n \"name\": \"B05\",\n \"nodata\": + 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": 15,\n + \ \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": + \"B06\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n + \ \"bits_per_sample\": 15,\n \"spatial_resolution\": 20,\n + \ \"scale\": 0.0001,\n \"offset\": 0,\n \"unit\": + \"m\"\n },\n {\n \"name\": \"B07\",\n \"nodata\": + 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": 15,\n + \ \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": + \"B08\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n + \ \"bits_per_sample\": 15,\n \"spatial_resolution\": 10,\n + \ \"scale\": 0.0001,\n \"offset\": 0,\n \"unit\": + \"m\"\n },\n {\n \"name\": \"B8A\",\n \"nodata\": + 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": 15,\n + \ \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": + \"B09\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n + \ \"bits_per_sample\": 15,\n \"spatial_resolution\": 60,\n + \ \"scale\": 0.0001,\n \"offset\": 0,\n \"unit\": + \"m\"\n },\n {\n \"name\": \"B10\",\n \"nodata\": + 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": 15,\n + \ \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n },\n {\n \"name\": + \"B11\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n + \ \"bits_per_sample\": 15,\n \"spatial_resolution\": 20,\n + \ \"scale\": 0.0001,\n \"offset\": 0,\n \"unit\": + \"m\"\n },\n {\n \"name\": \"B12\",\n \"nodata\": + 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": 15,\n + \ \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": + 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"source_code\": + {\n \"href\": \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n + \ \"title\": \"Model implementation.\",\n \"description\": \"Source + code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": + [\n \"mlm:source_code\",\n \"code\",\n \"metadata\"\n + \ ]\n }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n + \ \"href\": \"./collection.json\",\n \"type\": \"application/json\"\n + \ },\n {\n \"rel\": \"self\",\n \"href\": \"./item_raster_bands.json\",\n + \ \"type\": \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n + \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n + \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n + \ }\n ]\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - max-age=300 + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Length: + - '1803' + Content-Security-Policy: + - default-src 'none'; style-src 'unsafe-inline'; sandbox + Content-Type: + - text/plain; charset=utf-8 + Cross-Origin-Resource-Policy: + - cross-origin + Date: + - Tue, 29 Apr 2025 08:30:29 GMT + ETag: + - W/"a27ee1f30298ca82fea5f599426403c50df7b1da816b19a0b602af5649b1531b" + Expires: + - Tue, 29 Apr 2025 08:35:29 GMT + Source-Age: + - '0' + Strict-Transport-Security: + - max-age=31536000 + Vary: + - Authorization,Accept-Encoding,Origin + Via: + - 1.1 varnish + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Content-Type-Options: + - nosniff + X-Fastly-Request-ID: + - 540e6adbd74fc013400f20084d209c94f3eb67ea + X-Frame-Options: + - deny + X-GitHub-Request-Id: + - 45A5:5B0FB:C4898F:DDDFF6:68108E25 + X-Served-By: + - cache-fra-etou8220178-FRA + X-Timer: + - S1745915429.237875,VS0,VE295 + X-XSS-Protection: + - 1; mode=block + status: + code: 200 + message: OK +version: 1 diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 51e4ea4b6..4edaedf85 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1,3 +1,4 @@ +import itertools import json import logging import re @@ -5,6 +6,7 @@ from typing import Any, cast import pytest +import requests import pystac.errors from pystac import Asset, Collection, Item, ItemAssetDefinition @@ -1719,3 +1721,44 @@ def test_migration_1_3_to_1_4_collection() -> None: assert "mlm:hyperparameters" in data assert "mlm:artifact_type" in data["assets"]["asset1"] + + +@pytest.mark.vcr +@pytest.mark.parametrize( + "url_template, version", + tuple( + itertools.product( + ( + "https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/" + "v{version}/examples/item_basic.json", + "https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/" + "v{version}/examples/item_eo_bands.json", + "https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/" + "v{version}/examples/item_multi_io.json", + "https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/" + "v{version}/examples/item_raster_bands.json", + ), + ("1.0.0", "1.1.0", "1.2.0", "1.3.0"), + ) + ), +) +def test_migrate(url_template: str, version: str) -> None: + url = url_template.format(version=version) + r = requests.get(url) + data = r.json() + + old_uri = f"https://crim-ca.github.io/mlm-extension/v{version}/schema.json" + new_uri = f"https://stac-extensions.github.io/mlm/v{version}/schema.json" + + try: + i = data["stac_extensions"].index(old_uri) + data["stac_extensions"][i] = new_uri + except ValueError: + if new_uri not in data["stac_extensions"]: + raise Exception("Stac object does not list stac:mlm as extension") + + item = pystac.Item.from_dict(data) + + assert MLMExtension.get_schema_uri() in item.stac_extensions + assert old_uri not in item.stac_extensions + assert new_uri not in item.stac_extensions From d3e45afd18b297e36867a71d56938b0ba5342050 Mon Sep 17 00:00:00 2001 From: = Date: Tue, 29 Apr 2025 16:36:35 +0200 Subject: [PATCH 60/68] added migration --- pystac/extensions/mlm.py | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 83e529e13..40b5d8f0d 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2060,6 +2060,59 @@ def migrate(props_obj: dict[str, Any]) -> None: SyntaxWarning, ) + # if no bands definition is given in mlm:input (bands=[]), + # raster definitions are not allowed to be in the assets object that is + # stac:mlm + if "mlm:input" in props_obj: + no_bands_present = all( + not inp["bands"] for inp in props_obj["mlm:input"] + ) + + if no_bands_present: + for inner_asset_name in obj["assets"]: + inner_asset = obj["assets"][inner_asset_name] + + if "mlm:model" not in inner_asset["roles"]: + continue + + if "raster:bands" in inner_asset: + bands_obj = inner_asset["raster:bands"] + + warnings.warn( + "stac:mlm does not allow 'raster:bands' in mlm:model " + "asset if mlm:input.bands is empty. Moving it to " + "properties if it contains values, or deleting it if " + "it does not contain any values.", + SyntaxWarning, + ) + + # move the bands_obj if it is not an empty list + if bands_obj: + if obj["type"] == "Feature": + obj["properties"]["raster:bands"] = bands_obj + if obj["type"] == "Collection": + obj["raster:bands"] = bands_obj + inner_asset.pop("raster:bands") + + if "eo:bands" in inner_asset: + bands_obj = inner_asset["eo:bands"] + + warnings.warn( + "stac:mlm does not allow 'raster:bands' in mlm:model " + "asset if mlm:input.bands is empty. Moving it to " + "properties if it contains values, or deleting it if " + "it does not contain any values.", + SyntaxWarning, + ) + + # move the bands_obj if it is not an empty list + if bands_obj: + if obj["type"] == "Feature": + obj["properties"]["eo:bands"] = bands_obj + if obj["type"] == "Collection": + obj["eo:bands"] = bands_obj + inner_asset.pop("eo:bands") + if obj["type"] == "Feature": migrate(obj["properties"]) if obj["type"] == "Collection": From 4d683efacf23a0b8207455f78f5345254946e65a Mon Sep 17 00:00:00 2001 From: = Date: Tue, 29 Apr 2025 16:36:43 +0200 Subject: [PATCH 61/68] added tests --- tests/extensions/test_mlm.py | 86 ++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 4edaedf85..ebbe09cb5 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1140,6 +1140,92 @@ def test_migration_1_0_to_1_1_item_assets( assert bool(re.match(pattern, data["item_assets"]["asset1"]["mlm:framework"])) +@pytest.mark.parametrize( + "bands_obj_name, bands_obj", + ( + ("raster:bands", {"raster:bands": []}), + ("raster:bands", {"raster:bands": [{"name": "B01"}, {"name": "B02"}]}), + ("eo:bands", {"eo:bands": []}), + ("eo:bands", {"eo:bands": [{"name": "B01"}, {"name": "B02"}]}), + ), +) +def test_migration_1_0_to_1_1_asset_bands_item( + bands_obj_name: str, bands_obj: dict[str, Any] +) -> None: + data: dict[str, Any] = { + "type": "Feature", + "properties": {"mlm:input": [{"bands": []}]}, + "assets": { + "asset1": {"href": "https://example.com", "roles": ["analytic"]}, + "asset2": { + "href": "https://example.com", + "roles": ["analytic"], + **bands_obj, + }, + "asset3": { + "href": "https://example.com", + "roles": ["mlm:model"], + **bands_obj, + }, + }, + } + + with pytest.warns(SyntaxWarning): + MLMExtensionHooks._migrate_1_0_to_1_1(data) + + if bands_obj[bands_obj_name]: + assert bands_obj_name in data["assets"]["asset2"] + assert bands_obj_name not in data["assets"]["asset3"] + assert bands_obj_name in data["properties"] + else: + assert bands_obj_name in data["assets"]["asset2"] + assert bands_obj_name not in data["assets"]["asset3"] + assert bands_obj_name not in data["properties"] + + +@pytest.mark.parametrize( + "bands_obj_name, bands_obj", + ( + ("raster:bands", {"raster:bands": []}), + ("raster:bands", {"raster:bands": [{"name": "B01"}, {"name": "B02"}]}), + ("eo:bands", {"eo:bands": []}), + ("eo:bands", {"eo:bands": [{"name": "B01"}, {"name": "B02"}]}), + ), +) +def test_migration_1_0_to_1_1_asset_bands_collection( + bands_obj_name: str, bands_obj: dict[str, Any] +) -> None: + data: dict[str, Any] = { + "type": "Collection", + "mlm:input": [{"bands": []}], + "assets": { + "asset1": {"href": "https://example.com", "roles": ["analytic"]}, + "asset2": { + "href": "https://example.com", + "roles": ["analytic"], + **bands_obj, + }, + "asset3": { + "href": "https://example.com", + "roles": ["mlm:model"], + **bands_obj, + }, + }, + } + + with pytest.warns(SyntaxWarning): + MLMExtensionHooks._migrate_1_0_to_1_1(data) + + if bands_obj[bands_obj_name]: + assert bands_obj_name in data["assets"]["asset2"] + assert bands_obj_name not in data["assets"]["asset3"] + assert bands_obj_name in data + else: + assert bands_obj_name in data["assets"]["asset2"] + assert bands_obj_name not in data["assets"]["asset3"] + assert bands_obj_name not in data + + @pytest.mark.parametrize("asset_type", ("assets", "item_assets")) def test_migration_1_1_to_1_2(asset_type: str) -> None: data: dict[str, Any] = {} From 9d388fbc8dd24edd854e3ff01883bdda5873fc17 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 30 Apr 2025 10:50:33 +0200 Subject: [PATCH 62/68] updated tests --- ...sion}-examples-item_basic.json-1.0.0].yaml | 665 ++++++++++- ...sion}-examples-item_basic.json-1.1.0].yaml | 665 ++++++++++- ...sion}-examples-item_basic.json-1.2.0].yaml | 665 ++++++++++- ...sion}-examples-item_basic.json-1.3.0].yaml | 665 ++++++++++- ...n}-examples-item_eo_bands.json-1.0.0].yaml | 255 ---- ...n}-examples-item_eo_bands.json-1.1.0].yaml | 209 ---- ...n}-examples-item_eo_bands.json-1.2.0].yaml | 209 ---- ...n}-examples-item_eo_bands.json-1.3.0].yaml | 209 ---- ...n}-examples-item_multi_io.json-1.0.0].yaml | 1054 ++++++++++++++++- ...n}-examples-item_multi_io.json-1.1.0].yaml | 1054 ++++++++++++++++- ...n}-examples-item_multi_io.json-1.2.0].yaml | 1054 ++++++++++++++++- ...n}-examples-item_multi_io.json-1.3.0].yaml | 1054 ++++++++++++++++- ...xamples-item_raster_bands.json-1.0.0].yaml | 1052 +++++++++++++++- ...xamples-item_raster_bands.json-1.1.0].yaml | 1050 +++++++++++++++- ...xamples-item_raster_bands.json-1.2.0].yaml | 1050 +++++++++++++++- ...xamples-item_raster_bands.json-1.3.0].yaml | 1050 +++++++++++++++- tests/extensions/test_mlm.py | 4 +- 17 files changed, 10991 insertions(+), 973 deletions(-) delete mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.0.0].yaml delete mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.1.0].yaml delete mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.2.0].yaml delete mode 100644 tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.3.0].yaml diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.0.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.0.0].yaml index 6a337b634..6833292b2 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.0.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.0.0].yaml @@ -73,13 +73,13 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:25 GMT + - Wed, 30 Apr 2025 08:48:43 GMT ETag: - W/"06b55e576e4efab26350d8d7551913db9419014d18bff024af7e5818a2366f4b" Expires: - - Tue, 29 Apr 2025 08:35:25 GMT + - Wed, 30 Apr 2025 08:53:43 GMT Source-Age: - - '120' + - '0' Strict-Transport-Security: - max-age=31536000 Vary: @@ -87,24 +87,673 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - cbb71f87565b003068eeb6cf1d44e3e92f0b75fb + - fe2615757ad9d5339688e39fb8c970b71f222932 X-Frame-Options: - deny X-GitHub-Request-Id: - - EAA8:1C62A6:B8FA8E:D1A987:681087C4 + - 7E10:242658:8C51EB:96C1B3:6811E3EA X-Served-By: - - cache-fra-etou8220124-FRA + - cache-fra-etou8220138-FRA X-Timer: - - S1745915426.697783,VS0,VE1 + - S1746002923.164423,VS0,VE198 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '0' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:43 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - 585f28d624a7b4f5c9c871c09fd669aa472f58fd + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220055-FRA + X-Timer: + - S1746002924.509723,VS0,VE112 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '356' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:43 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - bb24e9c9ded013b390fc64ccd78acbbad3385dae + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220084-FRA + X-Timer: + - S1746002924.793418,VS0,VE3 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.1.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.1.0].yaml index 572c95588..d0dd017c3 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.1.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.1.0].yaml @@ -73,13 +73,13 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:25 GMT + - Wed, 30 Apr 2025 08:48:44 GMT ETag: - W/"60cdf768894e2f4eaafeb01906736c2bb03903640b6615f1cea0dd4ace03a6d3" Expires: - - Tue, 29 Apr 2025 08:35:25 GMT + - Wed, 30 Apr 2025 08:53:44 GMT Source-Age: - - '120' + - '0' Strict-Transport-Security: - max-age=31536000 Vary: @@ -87,24 +87,673 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - fdaeae3f7f917f5f4c801f2719acdc47c172defb + - bf6693b35b4566740830843d786c128a9675bbab X-Frame-Options: - deny X-GitHub-Request-Id: - - 5BEA:6EEED:C4E1E6:DE2B23:68108DA9 + - 8CA2:119D:9A5F86:A5AD73:6811E3E9 X-Served-By: - - cache-fra-etou8220128-FRA + - cache-fra-etou8220112-FRA X-Timer: - - S1745915426.768525,VS0,VE2 + - S1746002924.883973,VS0,VE172 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '1' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:44 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 52f5131bf4481a9ca075ab040545a8acd55f1b35 + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220176-FRA + X-Timer: + - S1746002924.192519,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '356' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:44 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 85f9d20bac6ba5cae559ab4d282fbbccab0c0ff4 + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220116-FRA + X-Timer: + - S1746002924.334339,VS0,VE2 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.2.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.2.0].yaml index d209d5ba6..ddefcd3ec 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.2.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.2.0].yaml @@ -73,13 +73,13 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:25 GMT + - Wed, 30 Apr 2025 08:48:44 GMT ETag: - W/"4214e4c8a7e047eaff361f43a1e2ce58d4e403d064c4a0a9fc870a0f02f3ffd4" Expires: - - Tue, 29 Apr 2025 08:35:25 GMT + - Wed, 30 Apr 2025 08:53:44 GMT Source-Age: - - '119' + - '0' Strict-Transport-Security: - max-age=31536000 Vary: @@ -87,24 +87,673 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - dea14443ef6f269978dd64c2bb7cf0811dcd3102 + - d88738ce45c7c7ca88a02eb8b695fa53f146e42e X-Frame-Options: - deny X-GitHub-Request-Id: - - 6822:112C:B53704:CD19F6:68108DA9 + - 8E2D:1D50CE:2AE04ED:2EA689B:6811E3EC X-Served-By: - - cache-fra-etou8220154-FRA + - cache-fra-etou8220034-FRA X-Timer: - - S1745915426.841928,VS0,VE1 + - S1746002924.433248,VS0,VE164 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '1' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:44 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - c781a0966d4eada9d4e33c7e9601a23b9c43f7f9 + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220157-FRA + X-Timer: + - S1746002925.743663,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '357' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:44 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 13aab05cd7ea17b8fc8c9084d6789922b1108b77 + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220106-FRA + X-Timer: + - S1746002925.852991,VS0,VE2 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.3.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.3.0].yaml index 6e9c29315..2642bdfff 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.3.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_basic.json-1.3.0].yaml @@ -73,13 +73,13 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:25 GMT + - Wed, 30 Apr 2025 08:48:45 GMT ETag: - W/"2cdd4c4b8a96681920b5e858cc552b3b19ca2a40e211d9ef29e3863f040782c2" Expires: - - Tue, 29 Apr 2025 08:35:25 GMT + - Wed, 30 Apr 2025 08:53:45 GMT Source-Age: - - '118' + - '0' Strict-Transport-Security: - max-age=31536000 Vary: @@ -87,24 +87,673 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - d9048815950a3df8944faf506270689c94e931a6 + - c47fe1bd7eb6e2683b454f2e4dab7077cef1508b X-Frame-Options: - deny X-GitHub-Request-Id: - - 6444:183F83:C9B8F0:E30245:68108DA9 + - 0F29:112C:2960BD2:2D10907:6811E3EC X-Served-By: - - cache-fra-etou8220020-FRA + - cache-fra-etou8220083-FRA X-Timer: - - S1745915426.908529,VS0,VE1 + - S1746002925.933909,VS0,VE204 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '2' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:45 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - c9f3ae052220e429bc406f0dec0d36a862b0f614 + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220069-FRA + X-Timer: + - S1746002925.331941,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '357' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:45 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 3177e6d1a3ce1848ca4ef432ae3e0f1d13ec176e + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220059-FRA + X-Timer: + - S1746002925.456796,VS0,VE1 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.0.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.0.0].yaml deleted file mode 100644 index 47ab3e239..000000000 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.0.0].yaml +++ /dev/null @@ -1,255 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - python-requests/2.32.3 - method: GET - uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.0.0/examples/item_eo_bands.json - response: - body: - string: "{\n \"stac_version\": \"1.0.0\",\n \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.0.0/schema.json\",\n - \ \"https://stac-extensions.github.io/eo/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n - \ \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n - \ ],\n \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n - \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": - \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n - \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n - \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n - \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n - \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n - \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": - [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n - \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced - from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n - \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n - \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 - Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n - \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n - \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n - \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": - \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": - false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": - 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 - Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n - \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n - \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n - \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n - \ \"shape\": [\n -1,\n 13,\n 64,\n - \ 64\n ],\n \"dim_order\": [\n \"batch\",\n - \ \"channel\",\n \"height\",\n \"width\"\n - \ ],\n \"data_type\": \"float32\"\n },\n \"norm_by_channel\": - true,\n \"norm_type\": \"z-score\",\n \"resize_type\": null,\n - \ \"statistics\": [\n {\n \"mean\": 1354.40546513,\n - \ \"stddev\": 245.71762908\n },\n {\n \"mean\": - 1118.24399958,\n \"stddev\": 333.00778264\n },\n {\n - \ \"mean\": 1042.92983953,\n \"stddev\": 395.09249139\n - \ },\n {\n \"mean\": 947.62620298,\n \"stddev\": - 593.75055589\n },\n {\n \"mean\": 1199.47283961,\n - \ \"stddev\": 566.4170017\n },\n {\n \"mean\": - 1999.79090914,\n \"stddev\": 861.18399006\n },\n {\n - \ \"mean\": 2369.22292565,\n \"stddev\": 1086.63139075\n - \ },\n {\n \"mean\": 2296.82608323,\n \"stddev\": - 1117.98170791\n },\n {\n \"mean\": 732.08340178,\n - \ \"stddev\": 404.91978886\n },\n {\n \"mean\": - 12.11327804,\n \"stddev\": 4.77584468\n },\n {\n - \ \"mean\": 1819.01027855,\n \"stddev\": 1002.58768311\n - \ },\n {\n \"mean\": 1118.92391149,\n \"stddev\": - 761.30323499\n },\n {\n \"mean\": 2594.14080798,\n - \ \"stddev\": 1231.58581042\n }\n ],\n \"pre_processing_function\": - {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n - \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": - \"classification\",\n \"tasks\": [\n \"classification\"\n - \ ],\n \"result\": {\n \"shape\": [\n -1,\n - \ 10\n ],\n \"dim_order\": [\n \"batch\",\n - \ \"class\"\n ],\n \"data_type\": \"float32\"\n - \ },\n \"classification_classes\": [\n {\n \"value\": - 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n - \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": - false\n },\n {\n \"value\": 1,\n \"name\": - \"Forest\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 2,\n \"name\": \"Herbaceous - Vegetation\",\n \"description\": null,\n \"title\": - null,\n \"color_hint\": null,\n \"nodata\": false\n - \ },\n {\n \"value\": 3,\n \"name\": - \"Highway\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 4,\n \"name\": \"Industrial - Buildings\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n - \ \"description\": null,\n \"title\": null,\n \"color_hint\": - null,\n \"nodata\": false\n },\n {\n \"value\": - 6,\n \"name\": \"Permanent Crop\",\n \"description\": - null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": - false\n },\n {\n \"value\": 7,\n \"name\": - \"Residential Buildings\",\n \"description\": null,\n \"title\": - null,\n \"color_hint\": null,\n \"nodata\": false\n - \ },\n {\n \"value\": 8,\n \"name\": - \"River\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n - \ \"description\": null,\n \"title\": null,\n \"color_hint\": - null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": - null\n }\n ],\n \"eo:bands\": [\n {\n \"name\": \"B01\",\n - \ \"common_name\": \"coastal\",\n \"description\": \"Coastal - aerosol (band 1)\",\n \"center_wavelength\": 0.443,\n \"full_width_half_max\": - 0.027\n },\n {\n \"name\": \"B02\",\n \"common_name\": - \"blue\",\n \"description\": \"Blue (band 2)\",\n \"center_wavelength\": - 0.49,\n \"full_width_half_max\": 0.098\n },\n {\n \"name\": - \"B03\",\n \"common_name\": \"green\",\n \"description\": \"Green - (band 3)\",\n \"center_wavelength\": 0.56,\n \"full_width_half_max\": - 0.045\n },\n {\n \"name\": \"B04\",\n \"common_name\": - \"red\",\n \"description\": \"Red (band 4)\",\n \"center_wavelength\": - 0.665,\n \"full_width_half_max\": 0.038\n },\n {\n \"name\": - \"B05\",\n \"common_name\": \"rededge\",\n \"description\": - \"Red edge 1 (band 5)\",\n \"center_wavelength\": 0.704,\n \"full_width_half_max\": - 0.019\n },\n {\n \"name\": \"B06\",\n \"common_name\": - \"rededge\",\n \"description\": \"Red edge 2 (band 6)\",\n \"center_wavelength\": - 0.74,\n \"full_width_half_max\": 0.018\n },\n {\n \"name\": - \"B07\",\n \"common_name\": \"rededge\",\n \"description\": - \"Red edge 3 (band 7)\",\n \"center_wavelength\": 0.783,\n \"full_width_half_max\": - 0.028\n },\n {\n \"name\": \"B08\",\n \"common_name\": - \"nir\",\n \"description\": \"NIR 1 (band 8)\",\n \"center_wavelength\": - 0.842,\n \"full_width_half_max\": 0.145\n },\n {\n \"name\": - \"B8A\",\n \"common_name\": \"nir08\",\n \"description\": \"NIR - 2 (band 8A)\",\n \"center_wavelength\": 0.865,\n \"full_width_half_max\": - 0.033\n },\n {\n \"name\": \"B09\",\n \"common_name\": - \"nir09\",\n \"description\": \"NIR 3 (band 9)\",\n \"center_wavelength\": - 0.945,\n \"full_width_half_max\": 0.026\n },\n {\n \"name\": - \"B10\",\n \"common_name\": \"cirrus\",\n \"description\": \"SWIR - - Cirrus (band 10)\",\n \"center_wavelength\": 1.375,\n \"full_width_half_max\": - 0.026\n },\n {\n \"name\": \"B11\",\n \"common_name\": - \"swir16\",\n \"description\": \"SWIR 1 (band 11)\",\n \"center_wavelength\": - 1.61,\n \"full_width_half_max\": 0.143\n },\n {\n \"name\": - \"B12\",\n \"common_name\": \"swir22\",\n \"description\": \"SWIR - 2 (band 12)\",\n \"center_wavelength\": 2.19,\n \"full_width_half_max\": - 0.242\n }\n ],\n \"raster:bands\": [\n {\n \"name\": - \"B01\",\n \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B02\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B03\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B04\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B05\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B06\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B07\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B08\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 10,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B8A\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B09\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B10\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 60,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B11\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n },\n {\n \"name\": \"B12\",\n - \ \"nodata\": 0,\n \"data_type\": \"uint16\",\n \"bits_per_sample\": - 15,\n \"spatial_resolution\": 20,\n \"scale\": 0.0001,\n \"offset\": - 0,\n \"unit\": \"m\"\n }\n ]\n },\n \"assets\": {\n \"weights\": - {\n \"href\": \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n - \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A - Resnet-18 classification model trained on normalized Sentinel-2 imagery with - Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; - application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n - \ ],\n \"$comment\": \"Following 'eo:bands' is required to fulfil - schema validation of 'eo' extension.\",\n \"eo:bands\": [\n {\n - \ \"name\": \"coastal\"\n },\n {\n \"name\": - \"blue\"\n },\n {\n \"name\": \"green\"\n },\n - \ {\n \"name\": \"red\"\n },\n {\n \"name\": - \"rededge1\"\n },\n {\n \"name\": \"rededge2\"\n },\n - \ {\n \"name\": \"rededge3\"\n },\n {\n \"name\": - \"nir\"\n },\n {\n \"name\": \"nir08\"\n },\n - \ {\n \"name\": \"nir09\"\n },\n {\n \"name\": - \"cirrus\"\n },\n {\n \"name\": \"swir16\"\n },\n - \ {\n \"name\": \"swir22\"\n }\n ]\n },\n \"source_code\": - {\n \"href\": \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n - \ \"title\": \"Model implementation.\",\n \"description\": \"Source - code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": - [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n - \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": - \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n - \ \"rel\": \"self\",\n \"href\": \"./item_eo_bands.json\",\n \"type\": - \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n - \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n - \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n - \ }\n ]\n}\n" - headers: - Accept-Ranges: - - bytes - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - max-age=300 - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '2632' - Content-Security-Policy: - - default-src 'none'; style-src 'unsafe-inline'; sandbox - Content-Type: - - text/plain; charset=utf-8 - Cross-Origin-Resource-Policy: - - cross-origin - Date: - - Tue, 29 Apr 2025 08:30:25 GMT - ETag: - - W/"550b55c5cc7ce92969a64bfa5a8325141386d01c7b097671b9d659f7c70764b5" - Expires: - - Tue, 29 Apr 2025 08:35:25 GMT - Source-Age: - - '118' - Strict-Transport-Security: - - max-age=31536000 - Vary: - - Authorization,Accept-Encoding,Origin - Via: - - 1.1 varnish - X-Cache: - - HIT - X-Cache-Hits: - - '0' - X-Content-Type-Options: - - nosniff - X-Fastly-Request-ID: - - 044807e148068029fbd9f42554c471a3460a5b19 - X-Frame-Options: - - deny - X-GitHub-Request-Id: - - EF89:6EEED:B9770B:D225ED:681087C7 - X-Served-By: - - cache-fra-etou8220031-FRA - X-Timer: - - S1745915426.975329,VS0,VE1 - X-XSS-Protection: - - 1; mode=block - status: - code: 200 - message: OK -version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.1.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.1.0].yaml deleted file mode 100644 index 7a6c18763..000000000 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.1.0].yaml +++ /dev/null @@ -1,209 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - python-requests/2.32.3 - method: GET - uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.1.0/examples/item_eo_bands.json - response: - body: - string: "{\n \"$comment\": \"Demonstrate the use of MLM and EO for bands description, - with EO bands directly in the Model Asset.\",\n \"stac_version\": \"1.0.0\",\n - \ \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.1.0/schema.json\",\n - \ \"https://stac-extensions.github.io/eo/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n - \ \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n - \ ],\n \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n - \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": - \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n - \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n - \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n - \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n - \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n - \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": - [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n - \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced - from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n - \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n - \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 - Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n - \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n - \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n - \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": - \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": - false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": - 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 - Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n - \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n - \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n - \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n - \ \"shape\": [\n -1,\n 13,\n 64,\n - \ 64\n ],\n \"dim_order\": [\n \"batch\",\n - \ \"channel\",\n \"height\",\n \"width\"\n - \ ],\n \"data_type\": \"float32\"\n },\n \"norm_by_channel\": - true,\n \"norm_type\": \"z-score\",\n \"resize_type\": null,\n - \ \"statistics\": [\n {\n \"mean\": 1354.40546513,\n - \ \"stddev\": 245.71762908\n },\n {\n \"mean\": - 1118.24399958,\n \"stddev\": 333.00778264\n },\n {\n - \ \"mean\": 1042.92983953,\n \"stddev\": 395.09249139\n - \ },\n {\n \"mean\": 947.62620298,\n \"stddev\": - 593.75055589\n },\n {\n \"mean\": 1199.47283961,\n - \ \"stddev\": 566.4170017\n },\n {\n \"mean\": - 1999.79090914,\n \"stddev\": 861.18399006\n },\n {\n - \ \"mean\": 2369.22292565,\n \"stddev\": 1086.63139075\n - \ },\n {\n \"mean\": 2296.82608323,\n \"stddev\": - 1117.98170791\n },\n {\n \"mean\": 732.08340178,\n - \ \"stddev\": 404.91978886\n },\n {\n \"mean\": - 12.11327804,\n \"stddev\": 4.77584468\n },\n {\n - \ \"mean\": 1819.01027855,\n \"stddev\": 1002.58768311\n - \ },\n {\n \"mean\": 1118.92391149,\n \"stddev\": - 761.30323499\n },\n {\n \"mean\": 2594.14080798,\n - \ \"stddev\": 1231.58581042\n }\n ],\n \"pre_processing_function\": - {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n - \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": - \"classification\",\n \"tasks\": [\n \"classification\"\n - \ ],\n \"result\": {\n \"shape\": [\n -1,\n - \ 10\n ],\n \"dim_order\": [\n \"batch\",\n - \ \"class\"\n ],\n \"data_type\": \"float32\"\n - \ },\n \"classification_classes\": [\n {\n \"value\": - 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n - \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": - false\n },\n {\n \"value\": 1,\n \"name\": - \"Forest\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 2,\n \"name\": \"Herbaceous - Vegetation\",\n \"description\": null,\n \"title\": - null,\n \"color_hint\": null,\n \"nodata\": false\n - \ },\n {\n \"value\": 3,\n \"name\": - \"Highway\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 4,\n \"name\": \"Industrial - Buildings\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n - \ \"description\": null,\n \"title\": null,\n \"color_hint\": - null,\n \"nodata\": false\n },\n {\n \"value\": - 6,\n \"name\": \"Permanent Crop\",\n \"description\": - null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": - false\n },\n {\n \"value\": 7,\n \"name\": - \"Residential Buildings\",\n \"description\": null,\n \"title\": - null,\n \"color_hint\": null,\n \"nodata\": false\n - \ },\n {\n \"value\": 8,\n \"name\": - \"River\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n - \ \"description\": null,\n \"title\": null,\n \"color_hint\": - null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": - null\n }\n ]\n },\n \"assets\": {\n \"weights\": {\n \"href\": - \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n - \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A - Resnet-18 classification model trained on normalized Sentinel-2 imagery with - Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; - application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n - \ ],\n \"$comment\": \"Following 'eo:bands' is required to fulfil - schema validation of 'eo' extension.\",\n \"eo:bands\": [\n {\n - \ \"name\": \"B01\",\n \"common_name\": \"coastal\",\n \"description\": - \"Coastal aerosol (band 1)\",\n \"center_wavelength\": 0.443,\n \"full_width_half_max\": - 0.027\n },\n {\n \"name\": \"B02\",\n \"common_name\": - \"blue\",\n \"description\": \"Blue (band 2)\",\n \"center_wavelength\": - 0.49,\n \"full_width_half_max\": 0.098\n },\n {\n \"name\": - \"B03\",\n \"common_name\": \"green\",\n \"description\": - \"Green (band 3)\",\n \"center_wavelength\": 0.56,\n \"full_width_half_max\": - 0.045\n },\n {\n \"name\": \"B04\",\n \"common_name\": - \"red\",\n \"description\": \"Red (band 4)\",\n \"center_wavelength\": - 0.665,\n \"full_width_half_max\": 0.038\n },\n {\n - \ \"name\": \"B05\",\n \"common_name\": \"rededge\",\n \"description\": - \"Red edge 1 (band 5)\",\n \"center_wavelength\": 0.704,\n \"full_width_half_max\": - 0.019\n },\n {\n \"name\": \"B06\",\n \"common_name\": - \"rededge\",\n \"description\": \"Red edge 2 (band 6)\",\n \"center_wavelength\": - 0.74,\n \"full_width_half_max\": 0.018\n },\n {\n \"name\": - \"B07\",\n \"common_name\": \"rededge\",\n \"description\": - \"Red edge 3 (band 7)\",\n \"center_wavelength\": 0.783,\n \"full_width_half_max\": - 0.028\n },\n {\n \"name\": \"B08\",\n \"common_name\": - \"nir\",\n \"description\": \"NIR 1 (band 8)\",\n \"center_wavelength\": - 0.842,\n \"full_width_half_max\": 0.145\n },\n {\n - \ \"name\": \"B8A\",\n \"common_name\": \"nir08\",\n \"description\": - \"NIR 2 (band 8A)\",\n \"center_wavelength\": 0.865,\n \"full_width_half_max\": - 0.033\n },\n {\n \"name\": \"B09\",\n \"common_name\": - \"nir09\",\n \"description\": \"NIR 3 (band 9)\",\n \"center_wavelength\": - 0.945,\n \"full_width_half_max\": 0.026\n },\n {\n - \ \"name\": \"B10\",\n \"common_name\": \"cirrus\",\n \"description\": - \"SWIR - Cirrus (band 10)\",\n \"center_wavelength\": 1.375,\n \"full_width_half_max\": - 0.026\n },\n {\n \"name\": \"B11\",\n \"common_name\": - \"swir16\",\n \"description\": \"SWIR 1 (band 11)\",\n \"center_wavelength\": - 1.61,\n \"full_width_half_max\": 0.143\n },\n {\n \"name\": - \"B12\",\n \"common_name\": \"swir22\",\n \"description\": - \"SWIR 2 (band 12)\",\n \"center_wavelength\": 2.19,\n \"full_width_half_max\": - 0.242\n }\n ]\n },\n \"source_code\": {\n \"href\": - \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n - \ \"title\": \"Model implementation.\",\n \"description\": \"Source - code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": - [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n - \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": - \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n - \ \"rel\": \"self\",\n \"href\": \"./item_eo_bands.json\",\n \"type\": - \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n - \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n - \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n - \ }\n ]\n}\n" - headers: - Accept-Ranges: - - bytes - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - max-age=300 - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '2476' - Content-Security-Policy: - - default-src 'none'; style-src 'unsafe-inline'; sandbox - Content-Type: - - text/plain; charset=utf-8 - Cross-Origin-Resource-Policy: - - cross-origin - Date: - - Tue, 29 Apr 2025 08:30:26 GMT - ETag: - - W/"538e8345f63973b9ad1ad9f7aaaa0dacd3ab3ffeea7255d6da75ff0299bd8896" - Expires: - - Tue, 29 Apr 2025 08:35:26 GMT - Source-Age: - - '118' - Strict-Transport-Security: - - max-age=31536000 - Vary: - - Authorization,Accept-Encoding,Origin - Via: - - 1.1 varnish - X-Cache: - - HIT - X-Cache-Hits: - - '0' - X-Content-Type-Options: - - nosniff - X-Fastly-Request-ID: - - 24eec2b921a253b550ede66e2a1d9adc7bd2de44 - X-Frame-Options: - - deny - X-GitHub-Request-Id: - - 5E94:6EEED:C4E611:DE2F9E:68108DAB - X-Served-By: - - cache-fra-etou8220158-FRA - X-Timer: - - S1745915426.042527,VS0,VE2 - X-XSS-Protection: - - 1; mode=block - status: - code: 200 - message: OK -version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.2.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.2.0].yaml deleted file mode 100644 index b9de4ecf5..000000000 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.2.0].yaml +++ /dev/null @@ -1,209 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - python-requests/2.32.3 - method: GET - uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.2.0/examples/item_eo_bands.json - response: - body: - string: "{\n \"$comment\": \"Demonstrate the use of MLM and EO for bands description, - with EO bands directly in the Model Asset.\",\n \"stac_version\": \"1.0.0\",\n - \ \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.2.0/schema.json\",\n - \ \"https://stac-extensions.github.io/eo/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n - \ \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n - \ ],\n \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n - \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": - \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n - \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n - \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n - \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n - \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n - \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": - [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n - \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced - from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n - \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n - \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 - Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n - \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n - \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n - \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": - \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": - false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": - 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 - Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n - \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n - \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n - \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n - \ \"shape\": [\n -1,\n 13,\n 64,\n - \ 64\n ],\n \"dim_order\": [\n \"batch\",\n - \ \"channel\",\n \"height\",\n \"width\"\n - \ ],\n \"data_type\": \"float32\"\n },\n \"norm_by_channel\": - true,\n \"norm_type\": \"z-score\",\n \"resize_type\": null,\n - \ \"statistics\": [\n {\n \"mean\": 1354.40546513,\n - \ \"stddev\": 245.71762908\n },\n {\n \"mean\": - 1118.24399958,\n \"stddev\": 333.00778264\n },\n {\n - \ \"mean\": 1042.92983953,\n \"stddev\": 395.09249139\n - \ },\n {\n \"mean\": 947.62620298,\n \"stddev\": - 593.75055589\n },\n {\n \"mean\": 1199.47283961,\n - \ \"stddev\": 566.4170017\n },\n {\n \"mean\": - 1999.79090914,\n \"stddev\": 861.18399006\n },\n {\n - \ \"mean\": 2369.22292565,\n \"stddev\": 1086.63139075\n - \ },\n {\n \"mean\": 2296.82608323,\n \"stddev\": - 1117.98170791\n },\n {\n \"mean\": 732.08340178,\n - \ \"stddev\": 404.91978886\n },\n {\n \"mean\": - 12.11327804,\n \"stddev\": 4.77584468\n },\n {\n - \ \"mean\": 1819.01027855,\n \"stddev\": 1002.58768311\n - \ },\n {\n \"mean\": 1118.92391149,\n \"stddev\": - 761.30323499\n },\n {\n \"mean\": 2594.14080798,\n - \ \"stddev\": 1231.58581042\n }\n ],\n \"pre_processing_function\": - {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n - \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": - \"classification\",\n \"tasks\": [\n \"classification\"\n - \ ],\n \"result\": {\n \"shape\": [\n -1,\n - \ 10\n ],\n \"dim_order\": [\n \"batch\",\n - \ \"class\"\n ],\n \"data_type\": \"float32\"\n - \ },\n \"classification_classes\": [\n {\n \"value\": - 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n - \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": - false\n },\n {\n \"value\": 1,\n \"name\": - \"Forest\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 2,\n \"name\": \"Herbaceous - Vegetation\",\n \"description\": null,\n \"title\": - null,\n \"color_hint\": null,\n \"nodata\": false\n - \ },\n {\n \"value\": 3,\n \"name\": - \"Highway\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 4,\n \"name\": \"Industrial - Buildings\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n - \ \"description\": null,\n \"title\": null,\n \"color_hint\": - null,\n \"nodata\": false\n },\n {\n \"value\": - 6,\n \"name\": \"Permanent Crop\",\n \"description\": - null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": - false\n },\n {\n \"value\": 7,\n \"name\": - \"Residential Buildings\",\n \"description\": null,\n \"title\": - null,\n \"color_hint\": null,\n \"nodata\": false\n - \ },\n {\n \"value\": 8,\n \"name\": - \"River\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n - \ \"description\": null,\n \"title\": null,\n \"color_hint\": - null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": - null\n }\n ]\n },\n \"assets\": {\n \"weights\": {\n \"href\": - \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n - \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A - Resnet-18 classification model trained on normalized Sentinel-2 imagery with - Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; - application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n - \ ],\n \"$comment\": \"Following 'eo:bands' is required to fulfil - schema validation of 'eo' extension.\",\n \"eo:bands\": [\n {\n - \ \"name\": \"B01\",\n \"common_name\": \"coastal\",\n \"description\": - \"Coastal aerosol (band 1)\",\n \"center_wavelength\": 0.443,\n \"full_width_half_max\": - 0.027\n },\n {\n \"name\": \"B02\",\n \"common_name\": - \"blue\",\n \"description\": \"Blue (band 2)\",\n \"center_wavelength\": - 0.49,\n \"full_width_half_max\": 0.098\n },\n {\n \"name\": - \"B03\",\n \"common_name\": \"green\",\n \"description\": - \"Green (band 3)\",\n \"center_wavelength\": 0.56,\n \"full_width_half_max\": - 0.045\n },\n {\n \"name\": \"B04\",\n \"common_name\": - \"red\",\n \"description\": \"Red (band 4)\",\n \"center_wavelength\": - 0.665,\n \"full_width_half_max\": 0.038\n },\n {\n - \ \"name\": \"B05\",\n \"common_name\": \"rededge\",\n \"description\": - \"Red edge 1 (band 5)\",\n \"center_wavelength\": 0.704,\n \"full_width_half_max\": - 0.019\n },\n {\n \"name\": \"B06\",\n \"common_name\": - \"rededge\",\n \"description\": \"Red edge 2 (band 6)\",\n \"center_wavelength\": - 0.74,\n \"full_width_half_max\": 0.018\n },\n {\n \"name\": - \"B07\",\n \"common_name\": \"rededge\",\n \"description\": - \"Red edge 3 (band 7)\",\n \"center_wavelength\": 0.783,\n \"full_width_half_max\": - 0.028\n },\n {\n \"name\": \"B08\",\n \"common_name\": - \"nir\",\n \"description\": \"NIR 1 (band 8)\",\n \"center_wavelength\": - 0.842,\n \"full_width_half_max\": 0.145\n },\n {\n - \ \"name\": \"B8A\",\n \"common_name\": \"nir08\",\n \"description\": - \"NIR 2 (band 8A)\",\n \"center_wavelength\": 0.865,\n \"full_width_half_max\": - 0.033\n },\n {\n \"name\": \"B09\",\n \"common_name\": - \"nir09\",\n \"description\": \"NIR 3 (band 9)\",\n \"center_wavelength\": - 0.945,\n \"full_width_half_max\": 0.026\n },\n {\n - \ \"name\": \"B10\",\n \"common_name\": \"cirrus\",\n \"description\": - \"SWIR - Cirrus (band 10)\",\n \"center_wavelength\": 1.375,\n \"full_width_half_max\": - 0.026\n },\n {\n \"name\": \"B11\",\n \"common_name\": - \"swir16\",\n \"description\": \"SWIR 1 (band 11)\",\n \"center_wavelength\": - 1.61,\n \"full_width_half_max\": 0.143\n },\n {\n \"name\": - \"B12\",\n \"common_name\": \"swir22\",\n \"description\": - \"SWIR 2 (band 12)\",\n \"center_wavelength\": 2.19,\n \"full_width_half_max\": - 0.242\n }\n ]\n },\n \"source_code\": {\n \"href\": - \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n - \ \"title\": \"Model implementation.\",\n \"description\": \"Source - code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": - [\n \"mlm:model\",\n \"code\",\n \"metadata\"\n ]\n - \ }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n \"href\": - \"./collection.json\",\n \"type\": \"application/json\"\n },\n {\n - \ \"rel\": \"self\",\n \"href\": \"./item_eo_bands.json\",\n \"type\": - \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n - \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n - \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n - \ }\n ]\n}\n" - headers: - Accept-Ranges: - - bytes - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - max-age=300 - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '2478' - Content-Security-Policy: - - default-src 'none'; style-src 'unsafe-inline'; sandbox - Content-Type: - - text/plain; charset=utf-8 - Cross-Origin-Resource-Policy: - - cross-origin - Date: - - Tue, 29 Apr 2025 08:30:26 GMT - ETag: - - W/"90ccd29b9b5252a7bbcc61aa942d67f941d72bd60900fe3ecbc41c4afc124914" - Expires: - - Tue, 29 Apr 2025 08:35:26 GMT - Source-Age: - - '118' - Strict-Transport-Security: - - max-age=31536000 - Vary: - - Authorization,Accept-Encoding,Origin - Via: - - 1.1 varnish - X-Cache: - - HIT - X-Cache-Hits: - - '0' - X-Content-Type-Options: - - nosniff - X-Fastly-Request-ID: - - 87443c348f0fef4024b71e8a678690f98fce5dc3 - X-Frame-Options: - - deny - X-GitHub-Request-Id: - - 8A6E:1A88C7:CA3CAF:E38650:68108DAC - X-Served-By: - - cache-fra-etou8220022-FRA - X-Timer: - - S1745915426.110658,VS0,VE1 - X-XSS-Protection: - - 1; mode=block - status: - code: 200 - message: OK -version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.3.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.3.0].yaml deleted file mode 100644 index b96072e8c..000000000 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_eo_bands.json-1.3.0].yaml +++ /dev/null @@ -1,209 +0,0 @@ -interactions: -- request: - body: null - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - User-Agent: - - python-requests/2.32.3 - method: GET - uri: https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/v1.3.0/examples/item_eo_bands.json - response: - body: - string: "{\n \"$comment\": \"Demonstrate the use of MLM and EO for bands description, - with EO bands directly in the Model Asset.\",\n \"stac_version\": \"1.0.0\",\n - \ \"stac_extensions\": [\n \"https://crim-ca.github.io/mlm-extension/v1.3.0/schema.json\",\n - \ \"https://stac-extensions.github.io/eo/v1.1.0/schema.json\",\n \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\",\n - \ \"https://stac-extensions.github.io/file/v1.0.0/schema.json\",\n \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n - \ ],\n \"type\": \"Feature\",\n \"id\": \"resnet-18_sentinel-2_all_moco_classification\",\n - \ \"collection\": \"ml-model-examples\",\n \"geometry\": {\n \"type\": - \"Polygon\",\n \"coordinates\": [\n [\n [\n -7.882190080512502,\n - \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n - \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n - \ 58.21798141355221\n ],\n [\n 27.911651652899923,\n - \ 37.13739173208318\n ],\n [\n -7.882190080512502,\n - \ 37.13739173208318\n ]\n ]\n ]\n },\n \"bbox\": - [\n -7.882190080512502,\n 37.13739173208318,\n 27.911651652899923,\n - \ 58.21798141355221\n ],\n \"properties\": {\n \"description\": \"Sourced - from torchgeo python library, identifier is ResNet18_Weights.SENTINEL2_ALL_MOCO\",\n - \ \"datetime\": null,\n \"start_datetime\": \"1900-01-01T00:00:00Z\",\n - \ \"end_datetime\": \"9999-12-31T23:59:59Z\",\n \"mlm:name\": \"Resnet-18 - Sentinel-2 ALL MOCO\",\n \"mlm:tasks\": [\n \"classification\"\n ],\n - \ \"mlm:architecture\": \"ResNet\",\n \"mlm:framework\": \"pytorch\",\n - \ \"mlm:framework_version\": \"2.1.2+cu121\",\n \"file:size\": 43000000,\n - \ \"mlm:memory_size\": 1,\n \"mlm:total_parameters\": 11700000,\n \"mlm:pretrained_source\": - \"EuroSat Sentinel-2\",\n \"mlm:accelerator\": \"cuda\",\n \"mlm:accelerator_constrained\": - false,\n \"mlm:accelerator_summary\": \"Unknown\",\n \"mlm:batch_size_suggestion\": - 256,\n \"mlm:input\": [\n {\n \"name\": \"13 Band Sentinel-2 - Batch\",\n \"bands\": [\n \"B01\",\n \"B02\",\n \"B03\",\n - \ \"B04\",\n \"B05\",\n \"B06\",\n \"B07\",\n - \ \"B08\",\n \"B8A\",\n \"B09\",\n \"B10\",\n - \ \"B11\",\n \"B12\"\n ],\n \"input\": {\n - \ \"shape\": [\n -1,\n 13,\n 64,\n - \ 64\n ],\n \"dim_order\": [\n \"batch\",\n - \ \"channel\",\n \"height\",\n \"width\"\n - \ ],\n \"data_type\": \"float32\"\n },\n \"norm_by_channel\": - true,\n \"norm_type\": \"z-score\",\n \"resize_type\": null,\n - \ \"statistics\": [\n {\n \"mean\": 1354.40546513,\n - \ \"stddev\": 245.71762908\n },\n {\n \"mean\": - 1118.24399958,\n \"stddev\": 333.00778264\n },\n {\n - \ \"mean\": 1042.92983953,\n \"stddev\": 395.09249139\n - \ },\n {\n \"mean\": 947.62620298,\n \"stddev\": - 593.75055589\n },\n {\n \"mean\": 1199.47283961,\n - \ \"stddev\": 566.4170017\n },\n {\n \"mean\": - 1999.79090914,\n \"stddev\": 861.18399006\n },\n {\n - \ \"mean\": 2369.22292565,\n \"stddev\": 1086.63139075\n - \ },\n {\n \"mean\": 2296.82608323,\n \"stddev\": - 1117.98170791\n },\n {\n \"mean\": 732.08340178,\n - \ \"stddev\": 404.91978886\n },\n {\n \"mean\": - 12.11327804,\n \"stddev\": 4.77584468\n },\n {\n - \ \"mean\": 1819.01027855,\n \"stddev\": 1002.58768311\n - \ },\n {\n \"mean\": 1118.92391149,\n \"stddev\": - 761.30323499\n },\n {\n \"mean\": 2594.14080798,\n - \ \"stddev\": 1231.58581042\n }\n ],\n \"pre_processing_function\": - {\n \"format\": \"python\",\n \"expression\": \"torchgeo.datamodules.eurosat.EuroSATDataModule.collate_fn\"\n - \ }\n }\n ],\n \"mlm:output\": [\n {\n \"name\": - \"classification\",\n \"tasks\": [\n \"classification\"\n - \ ],\n \"result\": {\n \"shape\": [\n -1,\n - \ 10\n ],\n \"dim_order\": [\n \"batch\",\n - \ \"class\"\n ],\n \"data_type\": \"float32\"\n - \ },\n \"classification_classes\": [\n {\n \"value\": - 0,\n \"name\": \"Annual Crop\",\n \"description\": null,\n - \ \"title\": null,\n \"color_hint\": null,\n \"nodata\": - false\n },\n {\n \"value\": 1,\n \"name\": - \"Forest\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 2,\n \"name\": \"Herbaceous - Vegetation\",\n \"description\": null,\n \"title\": - null,\n \"color_hint\": null,\n \"nodata\": false\n - \ },\n {\n \"value\": 3,\n \"name\": - \"Highway\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 4,\n \"name\": \"Industrial - Buildings\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 5,\n \"name\": \"Pasture\",\n - \ \"description\": null,\n \"title\": null,\n \"color_hint\": - null,\n \"nodata\": false\n },\n {\n \"value\": - 6,\n \"name\": \"Permanent Crop\",\n \"description\": - null,\n \"title\": null,\n \"color_hint\": null,\n \"nodata\": - false\n },\n {\n \"value\": 7,\n \"name\": - \"Residential Buildings\",\n \"description\": null,\n \"title\": - null,\n \"color_hint\": null,\n \"nodata\": false\n - \ },\n {\n \"value\": 8,\n \"name\": - \"River\",\n \"description\": null,\n \"title\": null,\n - \ \"color_hint\": null,\n \"nodata\": false\n },\n - \ {\n \"value\": 9,\n \"name\": \"SeaLake\",\n - \ \"description\": null,\n \"title\": null,\n \"color_hint\": - null,\n \"nodata\": false\n }\n ],\n \"post_processing_function\": - null\n }\n ]\n },\n \"assets\": {\n \"weights\": {\n \"href\": - \"https://huggingface.co/torchgeo/resnet18_sentinel2_all_moco/resolve/main/resnet18_sentinel2_all_moco-59bfdff9.pth\",\n - \ \"title\": \"Pytorch weights checkpoint\",\n \"description\": \"A - Resnet-18 classification model trained on normalized Sentinel-2 imagery with - Eurosat landcover labels with torchgeo\",\n \"type\": \"application/octet-stream; - application=pytorch\",\n \"roles\": [\n \"mlm:model\",\n \"mlm:weights\"\n - \ ],\n \"$comment\": \"Following 'eo:bands' is required to fulfil - schema validation of 'eo' extension.\",\n \"eo:bands\": [\n {\n - \ \"name\": \"B01\",\n \"common_name\": \"coastal\",\n \"description\": - \"Coastal aerosol (band 1)\",\n \"center_wavelength\": 0.443,\n \"full_width_half_max\": - 0.027\n },\n {\n \"name\": \"B02\",\n \"common_name\": - \"blue\",\n \"description\": \"Blue (band 2)\",\n \"center_wavelength\": - 0.49,\n \"full_width_half_max\": 0.098\n },\n {\n \"name\": - \"B03\",\n \"common_name\": \"green\",\n \"description\": - \"Green (band 3)\",\n \"center_wavelength\": 0.56,\n \"full_width_half_max\": - 0.045\n },\n {\n \"name\": \"B04\",\n \"common_name\": - \"red\",\n \"description\": \"Red (band 4)\",\n \"center_wavelength\": - 0.665,\n \"full_width_half_max\": 0.038\n },\n {\n - \ \"name\": \"B05\",\n \"common_name\": \"rededge\",\n \"description\": - \"Red edge 1 (band 5)\",\n \"center_wavelength\": 0.704,\n \"full_width_half_max\": - 0.019\n },\n {\n \"name\": \"B06\",\n \"common_name\": - \"rededge\",\n \"description\": \"Red edge 2 (band 6)\",\n \"center_wavelength\": - 0.74,\n \"full_width_half_max\": 0.018\n },\n {\n \"name\": - \"B07\",\n \"common_name\": \"rededge\",\n \"description\": - \"Red edge 3 (band 7)\",\n \"center_wavelength\": 0.783,\n \"full_width_half_max\": - 0.028\n },\n {\n \"name\": \"B08\",\n \"common_name\": - \"nir\",\n \"description\": \"NIR 1 (band 8)\",\n \"center_wavelength\": - 0.842,\n \"full_width_half_max\": 0.145\n },\n {\n - \ \"name\": \"B8A\",\n \"common_name\": \"nir08\",\n \"description\": - \"NIR 2 (band 8A)\",\n \"center_wavelength\": 0.865,\n \"full_width_half_max\": - 0.033\n },\n {\n \"name\": \"B09\",\n \"common_name\": - \"nir09\",\n \"description\": \"NIR 3 (band 9)\",\n \"center_wavelength\": - 0.945,\n \"full_width_half_max\": 0.026\n },\n {\n - \ \"name\": \"B10\",\n \"common_name\": \"cirrus\",\n \"description\": - \"SWIR - Cirrus (band 10)\",\n \"center_wavelength\": 1.375,\n \"full_width_half_max\": - 0.026\n },\n {\n \"name\": \"B11\",\n \"common_name\": - \"swir16\",\n \"description\": \"SWIR 1 (band 11)\",\n \"center_wavelength\": - 1.61,\n \"full_width_half_max\": 0.143\n },\n {\n \"name\": - \"B12\",\n \"common_name\": \"swir22\",\n \"description\": - \"SWIR 2 (band 12)\",\n \"center_wavelength\": 2.19,\n \"full_width_half_max\": - 0.242\n }\n ]\n },\n \"source_code\": {\n \"href\": - \"https://github.com/microsoft/torchgeo/blob/61efd2e2c4df7ebe3bd03002ebbaeaa3cfe9885a/torchgeo/models/resnet.py#L207\",\n - \ \"title\": \"Model implementation.\",\n \"description\": \"Source - code to run the model.\",\n \"type\": \"text/x-python\",\n \"roles\": - [\n \"mlm:source_code\",\n \"code\",\n \"metadata\"\n - \ ]\n }\n },\n \"links\": [\n {\n \"rel\": \"collection\",\n - \ \"href\": \"./collection.json\",\n \"type\": \"application/json\"\n - \ },\n {\n \"rel\": \"self\",\n \"href\": \"./item_eo_bands.json\",\n - \ \"type\": \"application/geo+json\"\n },\n {\n \"rel\": \"derived_from\",\n - \ \"href\": \"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a\",\n - \ \"type\": \"application/json\",\n \"ml-aoi:split\": \"train\"\n - \ }\n ]\n}\n" - headers: - Accept-Ranges: - - bytes - Access-Control-Allow-Origin: - - '*' - Cache-Control: - - max-age=300 - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Length: - - '2481' - Content-Security-Policy: - - default-src 'none'; style-src 'unsafe-inline'; sandbox - Content-Type: - - text/plain; charset=utf-8 - Cross-Origin-Resource-Policy: - - cross-origin - Date: - - Tue, 29 Apr 2025 08:30:26 GMT - ETag: - - W/"a8e548ba2f956af68d095ed3395f558cd02489363df3b2b44052cc4a5a15653e" - Expires: - - Tue, 29 Apr 2025 08:35:26 GMT - Source-Age: - - '118' - Strict-Transport-Security: - - max-age=31536000 - Vary: - - Authorization,Accept-Encoding,Origin - Via: - - 1.1 varnish - X-Cache: - - HIT - X-Cache-Hits: - - '0' - X-Content-Type-Options: - - nosniff - X-Fastly-Request-ID: - - 9f483102ad1f4e924b4bcd84a00606d99516dc4a - X-Frame-Options: - - deny - X-GitHub-Request-Id: - - FCEF:117E:6F0439:7ED11A:68108DAC - X-Served-By: - - cache-fra-etou8220123-FRA - X-Timer: - - S1745915426.179302,VS0,VE1 - X-XSS-Protection: - - 1; mode=block - status: - code: 200 - message: OK -version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.0.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.0.0].yaml index 4339f6716..5deed6eab 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.0.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.0.0].yaml @@ -119,13 +119,13 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:26 GMT + - Wed, 30 Apr 2025 08:48:45 GMT ETag: - W/"b8e68364828f14a588c33c8a9e49382d8e69c2bbe5052e997e7a039f612178aa" Expires: - - Tue, 29 Apr 2025 08:35:26 GMT + - Wed, 30 Apr 2025 08:53:45 GMT Source-Age: - - '117' + - '0' Strict-Transport-Security: - max-age=31536000 Vary: @@ -133,24 +133,1062 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - 34f627f96ddccc8c605c63326973840c9edb02a9 + - 60aa8b326adae43581693904cc35d310511edb79 X-Frame-Options: - deny X-GitHub-Request-Id: - - 7B49:230D29:B4D608:CD825C:681087AA + - 4B88:184A0:8D6654:97D80C:6811E3ED X-Served-By: - - cache-fra-etou8220118-FRA + - cache-fra-etou8220024-FRA X-Timer: - - S1745915426.247149,VS0,VE1 + - S1746002926.544023,VS0,VE166 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '2' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:45 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - e1ab6a7104f41a450de16e24abf6f0a5cd19a07b + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220106-FRA + X-Timer: + - S1746002926.835542,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '358' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:45 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 8954eba3dc7f240928f66419c526b8d7e8b8746f + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220178-FRA + X-Timer: + - S1746002926.936132,VS0,VE1 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '353' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:46 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 8ee10dec29ee3c117655023143b03f82c4fbcfc1 + X-GitHub-Request-Id: + - CCB6:11BF:2646467:26A98A1:6811AA24 + X-Served-By: + - cache-fra-etou8220040-FRA + X-Timer: + - S1746002926.064746,VS0,VE2 + expires: + - Wed, 30 Apr 2025 04:52:12 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/file/v2.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/file/v2.1.0/schema.json#\",\n \"title\": + \"File Info Extension\",\n \"description\": \"STAC File Info Extension for + STAC Items, STAC Catalogs, and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"type\": \"object\",\n \"required\": [\n + \ \"type\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"links\": {\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Catalogs.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Catalog\"\n },\n \"links\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ]\n },\n {\n \"$comment\": + \"This is the schema for STAC Collections.\",\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"links\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/file/v2.1.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"file:byte_order\": + {\n \"type\": \"string\",\n \"enum\": [\n \"big-endian\",\n + \ \"little-endian\"\n ],\n \"title\": \"File Byte + Order\"\n },\n \"file:checksum\": {\n \"type\": \"string\",\n + \ \"pattern\": \"^[a-f0-9]+$\",\n \"title\": \"File Checksum + (Multihash)\"\n },\n \"file:header_size\": {\n \"type\": + \"integer\",\n \"minimum\": 0,\n \"title\": \"File Header + Size\"\n },\n \"file:size\": {\n \"type\": \"integer\",\n + \ \"minimum\": 0,\n \"title\": \"File Size\"\n },\n + \ \"file:values\": {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n \"required\": + [\n \"values\",\n \"summary\"\n ],\n + \ \"properties\": {\n \"values\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n + \ \"description\": \"Any data type is allowed\"\n }\n + \ },\n \"summary\": {\n \"type\": + \"string\",\n \"minLength\": 1\n }\n }\n + \ }\n },\n \"file:local_path\": {\n \"type\": + \"string\",\n \"pattern\": \"^[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+(/[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+)*/?$\",\n + \ \"title\": \"Relative File Path\"\n }\n },\n \"patternProperties\": + {\n \"^(?!file:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '0' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '4536' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:46 GMT + ETag: + - '"61b4cf00-11b8"' + Last-Modified: + - Sat, 11 Dec 2021 16:17:04 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - MISS + X-Cache-Hits: + - '0' + X-Fastly-Request-ID: + - 0ab859df17885b144bb25675b1710468e436264f + X-GitHub-Request-Id: + - 868B:11B5:DD2BB0:DF64F4:6811E3EE + X-Served-By: + - cache-fra-etou8220062-FRA + X-Timer: + - S1746002926.175468,VS0,VE109 + expires: + - Wed, 30 Apr 2025 08:58:46 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json#\",\n \"title\": + \"ML AOI Extension\",\n \"description\": \"ML AOI Extension for STAC definitions.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"summaries\": + {\n \"type\": \"object\",\n \"properties\": {\n + \ \"ml-aoi:split\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"$ref\": \"#/definitions/fields/properties/ml-aoi:split\"\n + \ }\n }\n }\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"ml-aoi:split\": {\n \"type\": + \"string\",\n \"enum\": [\"train\", \"test\", \"validate\"]\n },\n + \ \"ml-aoi:role\": {\n \"type\": \"string\",\n \"enum\": + [\"label\", \"feature\"]\n },\n \"ml-aoi:reference-grid\": {\n + \ \"type\": \"boolean\"\n },\n \"ml-aoi:resampling-method\": + {\n \"$comment\": \"Supported GDAL resampling method (https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r)\",\n + \ \"type\": \"string\",\n \"enum\": [\n \"near\",\n + \ \"bilinear\",\n \"cubic\",\n \"cubcspline\",\n + \ \"lanczos\",\n \"average\",\n \"rms\",\n + \ \"mode\",\n \"max\",\n \"min\",\n + \ \"med\",\n \"q1\",\n \"q3\",\n \"sum\"\n + \ ]\n }\n },\n \"patternProperties\": {\n \"^(?!ml-aoi:)\": + {}\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '337' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '3400' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:46 GMT + ETag: + - '"6605925b-d48"' + Last-Modified: + - Thu, 28 Mar 2024 15:52:59 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - ea574301be2f279231d07243f6c9787ea2afdedd + X-GitHub-Request-Id: + - E06D:14F99C:2DEDC77:2E61FB6:6811CF0F + X-Served-By: + - cache-fra-etou8220022-FRA + X-Timer: + - S1746002926.458421,VS0,VE2 + expires: + - Wed, 30 Apr 2025 07:29:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.1.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.1.0].yaml index 1ca0e4513..f67393032 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.1.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.1.0].yaml @@ -125,13 +125,13 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:26 GMT + - Wed, 30 Apr 2025 08:48:46 GMT ETag: - W/"ec315329c9c65996484f1dab9aa184c19906db9ce90cb2300b05235d2c2faddb" Expires: - - Tue, 29 Apr 2025 08:35:26 GMT + - Wed, 30 Apr 2025 08:53:46 GMT Source-Age: - - '117' + - '0' Strict-Transport-Security: - max-age=31536000 Vary: @@ -139,24 +139,1062 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - d1512db233f19b44296c533246a0494b557881f9 + - 60852f9e73c043dc33915063b3f3495986ca1952 X-Frame-Options: - deny X-GitHub-Request-Id: - - 2968:1A88C7:CA3EAE:E38854:68108DAC + - DB77:FDDB1:89859E:93F5CA:6811E3ED X-Served-By: - - cache-fra-etou8220163-FRA + - cache-fra-etou8220111-FRA X-Timer: - - S1745915426.311474,VS0,VE1 + - S1746002927.558899,VS0,VE189 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '3' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:46 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 612e2b2c6d6962315cf558c9dd8d132051fbbd8e + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220143-FRA + X-Timer: + - S1746002927.864726,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '359' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:46 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 0d1cc605497f003af298f9686c95a693894bb27e + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220045-FRA + X-Timer: + - S1746002927.979422,VS0,VE1 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '354' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:47 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 17b4fdbccdfb010cfbfebe30cc4b6eaf8bfad393 + X-GitHub-Request-Id: + - CCB6:11BF:2646467:26A98A1:6811AA24 + X-Served-By: + - cache-fra-etou8220046-FRA + X-Timer: + - S1746002927.091214,VS0,VE2 + expires: + - Wed, 30 Apr 2025 04:52:12 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/file/v2.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/file/v2.1.0/schema.json#\",\n \"title\": + \"File Info Extension\",\n \"description\": \"STAC File Info Extension for + STAC Items, STAC Catalogs, and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"type\": \"object\",\n \"required\": [\n + \ \"type\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"links\": {\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Catalogs.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Catalog\"\n },\n \"links\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ]\n },\n {\n \"$comment\": + \"This is the schema for STAC Collections.\",\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"links\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/file/v2.1.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"file:byte_order\": + {\n \"type\": \"string\",\n \"enum\": [\n \"big-endian\",\n + \ \"little-endian\"\n ],\n \"title\": \"File Byte + Order\"\n },\n \"file:checksum\": {\n \"type\": \"string\",\n + \ \"pattern\": \"^[a-f0-9]+$\",\n \"title\": \"File Checksum + (Multihash)\"\n },\n \"file:header_size\": {\n \"type\": + \"integer\",\n \"minimum\": 0,\n \"title\": \"File Header + Size\"\n },\n \"file:size\": {\n \"type\": \"integer\",\n + \ \"minimum\": 0,\n \"title\": \"File Size\"\n },\n + \ \"file:values\": {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n \"required\": + [\n \"values\",\n \"summary\"\n ],\n + \ \"properties\": {\n \"values\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n + \ \"description\": \"Any data type is allowed\"\n }\n + \ },\n \"summary\": {\n \"type\": + \"string\",\n \"minLength\": 1\n }\n }\n + \ }\n },\n \"file:local_path\": {\n \"type\": + \"string\",\n \"pattern\": \"^[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+(/[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+)*/?$\",\n + \ \"title\": \"Relative File Path\"\n }\n },\n \"patternProperties\": + {\n \"^(?!file:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '1' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '4536' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:47 GMT + ETag: + - '"61b4cf00-11b8"' + Last-Modified: + - Sat, 11 Dec 2021 16:17:04 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - b4f4a197a7d5f9b79be3559f46485c0e3bb2a4f2 + X-GitHub-Request-Id: + - 868B:11B5:DD2BB0:DF64F4:6811E3EE + X-Served-By: + - cache-fra-etou8220075-FRA + X-Timer: + - S1746002927.202749,VS0,VE1 + expires: + - Wed, 30 Apr 2025 08:58:46 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json#\",\n \"title\": + \"ML AOI Extension\",\n \"description\": \"ML AOI Extension for STAC definitions.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"summaries\": + {\n \"type\": \"object\",\n \"properties\": {\n + \ \"ml-aoi:split\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"$ref\": \"#/definitions/fields/properties/ml-aoi:split\"\n + \ }\n }\n }\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"ml-aoi:split\": {\n \"type\": + \"string\",\n \"enum\": [\"train\", \"test\", \"validate\"]\n },\n + \ \"ml-aoi:role\": {\n \"type\": \"string\",\n \"enum\": + [\"label\", \"feature\"]\n },\n \"ml-aoi:reference-grid\": {\n + \ \"type\": \"boolean\"\n },\n \"ml-aoi:resampling-method\": + {\n \"$comment\": \"Supported GDAL resampling method (https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r)\",\n + \ \"type\": \"string\",\n \"enum\": [\n \"near\",\n + \ \"bilinear\",\n \"cubic\",\n \"cubcspline\",\n + \ \"lanczos\",\n \"average\",\n \"rms\",\n + \ \"mode\",\n \"max\",\n \"min\",\n + \ \"med\",\n \"q1\",\n \"q3\",\n \"sum\"\n + \ ]\n }\n },\n \"patternProperties\": {\n \"^(?!ml-aoi:)\": + {}\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '338' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '3400' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:47 GMT + ETag: + - '"6605925b-d48"' + Last-Modified: + - Thu, 28 Mar 2024 15:52:59 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 0756264fd0e2e5c2e043b281e9806e161b07d218 + X-GitHub-Request-Id: + - E06D:14F99C:2DEDC77:2E61FB6:6811CF0F + X-Served-By: + - cache-fra-etou8220173-FRA + X-Timer: + - S1746002927.292189,VS0,VE1 + expires: + - Wed, 30 Apr 2025 07:29:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.2.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.2.0].yaml index c9f57f8bb..c8012b511 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.2.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.2.0].yaml @@ -125,13 +125,13 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:26 GMT + - Wed, 30 Apr 2025 08:48:47 GMT ETag: - W/"2615bf01905ff85470ba440bcbe17f54614988b43e703b01bcc24b09a5cba5df" Expires: - - Tue, 29 Apr 2025 08:35:26 GMT + - Wed, 30 Apr 2025 08:53:47 GMT Source-Age: - - '117' + - '0' Strict-Transport-Security: - max-age=31536000 Vary: @@ -139,24 +139,1062 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - 618e8a2e789630ca9088426bec20bd24148f0408 + - 96b38bddced809799ad7c28e40c74626a0516540 X-Frame-Options: - deny X-GitHub-Request-Id: - - 8060:1BD2FE:C7A2CC:E0EC97:68108DA9 + - A31C:1BD2FE:2AD2784:2E98B62:6811E3EE X-Served-By: - - cache-fra-etou8220084-FRA + - cache-fra-etou8220060-FRA X-Timer: - - S1745915426.372021,VS0,VE1 + - S1746002927.379240,VS0,VE167 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '4' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:47 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 793b5e819c50caeb7902e42d41e402d7bcf32330 + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220156-FRA + X-Timer: + - S1746002928.680619,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '360' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:47 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 68ef10efb4cb5bba88b5a9cf4cc437d8a2fe0616 + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220163-FRA + X-Timer: + - S1746002928.778665,VS0,VE2 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '355' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:47 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 1ec804b265fe231750b3793c321964ba4d80c60e + X-GitHub-Request-Id: + - CCB6:11BF:2646467:26A98A1:6811AA24 + X-Served-By: + - cache-fra-etou8220117-FRA + X-Timer: + - S1746002928.883978,VS0,VE2 + expires: + - Wed, 30 Apr 2025 04:52:12 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/file/v2.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/file/v2.1.0/schema.json#\",\n \"title\": + \"File Info Extension\",\n \"description\": \"STAC File Info Extension for + STAC Items, STAC Catalogs, and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"type\": \"object\",\n \"required\": [\n + \ \"type\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"links\": {\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Catalogs.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Catalog\"\n },\n \"links\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ]\n },\n {\n \"$comment\": + \"This is the schema for STAC Collections.\",\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"links\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/file/v2.1.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"file:byte_order\": + {\n \"type\": \"string\",\n \"enum\": [\n \"big-endian\",\n + \ \"little-endian\"\n ],\n \"title\": \"File Byte + Order\"\n },\n \"file:checksum\": {\n \"type\": \"string\",\n + \ \"pattern\": \"^[a-f0-9]+$\",\n \"title\": \"File Checksum + (Multihash)\"\n },\n \"file:header_size\": {\n \"type\": + \"integer\",\n \"minimum\": 0,\n \"title\": \"File Header + Size\"\n },\n \"file:size\": {\n \"type\": \"integer\",\n + \ \"minimum\": 0,\n \"title\": \"File Size\"\n },\n + \ \"file:values\": {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n \"required\": + [\n \"values\",\n \"summary\"\n ],\n + \ \"properties\": {\n \"values\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n + \ \"description\": \"Any data type is allowed\"\n }\n + \ },\n \"summary\": {\n \"type\": + \"string\",\n \"minLength\": 1\n }\n }\n + \ }\n },\n \"file:local_path\": {\n \"type\": + \"string\",\n \"pattern\": \"^[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+(/[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+)*/?$\",\n + \ \"title\": \"Relative File Path\"\n }\n },\n \"patternProperties\": + {\n \"^(?!file:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '2' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '4536' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:47 GMT + ETag: + - '"61b4cf00-11b8"' + Last-Modified: + - Sat, 11 Dec 2021 16:17:04 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 665a01e30fd27bed3a134d2784f684a9bea7f423 + X-GitHub-Request-Id: + - 868B:11B5:DD2BB0:DF64F4:6811E3EE + X-Served-By: + - cache-fra-etou8220086-FRA + X-Timer: + - S1746002928.985252,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:46 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json#\",\n \"title\": + \"ML AOI Extension\",\n \"description\": \"ML AOI Extension for STAC definitions.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"summaries\": + {\n \"type\": \"object\",\n \"properties\": {\n + \ \"ml-aoi:split\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"$ref\": \"#/definitions/fields/properties/ml-aoi:split\"\n + \ }\n }\n }\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"ml-aoi:split\": {\n \"type\": + \"string\",\n \"enum\": [\"train\", \"test\", \"validate\"]\n },\n + \ \"ml-aoi:role\": {\n \"type\": \"string\",\n \"enum\": + [\"label\", \"feature\"]\n },\n \"ml-aoi:reference-grid\": {\n + \ \"type\": \"boolean\"\n },\n \"ml-aoi:resampling-method\": + {\n \"$comment\": \"Supported GDAL resampling method (https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r)\",\n + \ \"type\": \"string\",\n \"enum\": [\n \"near\",\n + \ \"bilinear\",\n \"cubic\",\n \"cubcspline\",\n + \ \"lanczos\",\n \"average\",\n \"rms\",\n + \ \"mode\",\n \"max\",\n \"min\",\n + \ \"med\",\n \"q1\",\n \"q3\",\n \"sum\"\n + \ ]\n }\n },\n \"patternProperties\": {\n \"^(?!ml-aoi:)\": + {}\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '339' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '3400' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:48 GMT + ETag: + - '"6605925b-d48"' + Last-Modified: + - Thu, 28 Mar 2024 15:52:59 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 028298b99f1f646e52f32118f290378188cd86a6 + X-GitHub-Request-Id: + - E06D:14F99C:2DEDC77:2E61FB6:6811CF0F + X-Served-By: + - cache-fra-etou8220064-FRA + X-Timer: + - S1746002928.078278,VS0,VE1 + expires: + - Wed, 30 Apr 2025 07:29:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.3.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.3.0].yaml index 3522b97cb..88596d164 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.3.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_multi_io.json-1.3.0].yaml @@ -129,13 +129,13 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:26 GMT + - Wed, 30 Apr 2025 08:48:48 GMT ETag: - W/"8a7f7c7f98bd63496692bef25107288160f2763f8618048b0a4516885afcdd1d" Expires: - - Tue, 29 Apr 2025 08:35:26 GMT + - Wed, 30 Apr 2025 08:53:48 GMT Source-Age: - - '116' + - '0' Strict-Transport-Security: - max-age=31536000 Vary: @@ -143,24 +143,1062 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - 4938841ea8995faf87ddb8a113c0a9870e55984a + - c7fb1566a7b001b7b7c9a719b61918039f2610bb X-Frame-Options: - deny X-GitHub-Request-Id: - - 1E08:5B0FB:C3A5C2:DCEFDB:68108DAD + - FFE5:11B6:128191D:140375C:6811E3F0 X-Served-By: - - cache-fra-etou8220064-FRA + - cache-fra-etou8220095-FRA X-Timer: - - S1745915426.436633,VS0,VE7 + - S1746002928.154086,VS0,VE202 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '5' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:48 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - f806de900570c5302cf282e37cfa39752bdc3ba0 + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220127-FRA + X-Timer: + - S1746002929.503033,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '361' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:48 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - e93d25a3dfb739c905b0c40843dd991f984459ae + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220054-FRA + X-Timer: + - S1746002929.609425,VS0,VE1 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '356' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:48 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 567ba9fbfb6ce06933aa025adaa32c70ac853d03 + X-GitHub-Request-Id: + - CCB6:11BF:2646467:26A98A1:6811AA24 + X-Served-By: + - cache-fra-etou8220152-FRA + X-Timer: + - S1746002929.708216,VS0,VE2 + expires: + - Wed, 30 Apr 2025 04:52:12 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/file/v2.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/file/v2.1.0/schema.json#\",\n \"title\": + \"File Info Extension\",\n \"description\": \"STAC File Info Extension for + STAC Items, STAC Catalogs, and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"type\": \"object\",\n \"required\": [\n + \ \"type\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"links\": {\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Catalogs.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Catalog\"\n },\n \"links\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ]\n },\n {\n \"$comment\": + \"This is the schema for STAC Collections.\",\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"links\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/file/v2.1.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"file:byte_order\": + {\n \"type\": \"string\",\n \"enum\": [\n \"big-endian\",\n + \ \"little-endian\"\n ],\n \"title\": \"File Byte + Order\"\n },\n \"file:checksum\": {\n \"type\": \"string\",\n + \ \"pattern\": \"^[a-f0-9]+$\",\n \"title\": \"File Checksum + (Multihash)\"\n },\n \"file:header_size\": {\n \"type\": + \"integer\",\n \"minimum\": 0,\n \"title\": \"File Header + Size\"\n },\n \"file:size\": {\n \"type\": \"integer\",\n + \ \"minimum\": 0,\n \"title\": \"File Size\"\n },\n + \ \"file:values\": {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n \"required\": + [\n \"values\",\n \"summary\"\n ],\n + \ \"properties\": {\n \"values\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n + \ \"description\": \"Any data type is allowed\"\n }\n + \ },\n \"summary\": {\n \"type\": + \"string\",\n \"minLength\": 1\n }\n }\n + \ }\n },\n \"file:local_path\": {\n \"type\": + \"string\",\n \"pattern\": \"^[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+(/[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+)*/?$\",\n + \ \"title\": \"Relative File Path\"\n }\n },\n \"patternProperties\": + {\n \"^(?!file:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '3' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '4536' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:48 GMT + ETag: + - '"61b4cf00-11b8"' + Last-Modified: + - Sat, 11 Dec 2021 16:17:04 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 0feb186679670da798be6438865740d1fea5c6c9 + X-GitHub-Request-Id: + - 868B:11B5:DD2BB0:DF64F4:6811E3EE + X-Served-By: + - cache-fra-etou8220152-FRA + X-Timer: + - S1746002929.807178,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:46 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json#\",\n \"title\": + \"ML AOI Extension\",\n \"description\": \"ML AOI Extension for STAC definitions.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"summaries\": + {\n \"type\": \"object\",\n \"properties\": {\n + \ \"ml-aoi:split\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"$ref\": \"#/definitions/fields/properties/ml-aoi:split\"\n + \ }\n }\n }\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"ml-aoi:split\": {\n \"type\": + \"string\",\n \"enum\": [\"train\", \"test\", \"validate\"]\n },\n + \ \"ml-aoi:role\": {\n \"type\": \"string\",\n \"enum\": + [\"label\", \"feature\"]\n },\n \"ml-aoi:reference-grid\": {\n + \ \"type\": \"boolean\"\n },\n \"ml-aoi:resampling-method\": + {\n \"$comment\": \"Supported GDAL resampling method (https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r)\",\n + \ \"type\": \"string\",\n \"enum\": [\n \"near\",\n + \ \"bilinear\",\n \"cubic\",\n \"cubcspline\",\n + \ \"lanczos\",\n \"average\",\n \"rms\",\n + \ \"mode\",\n \"max\",\n \"min\",\n + \ \"med\",\n \"q1\",\n \"q3\",\n \"sum\"\n + \ ]\n }\n },\n \"patternProperties\": {\n \"^(?!ml-aoi:)\": + {}\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '339' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '3400' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:48 GMT + ETag: + - '"6605925b-d48"' + Last-Modified: + - Thu, 28 Mar 2024 15:52:59 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - d1098e090c1293ff0ba5541890bea8b1f0aff54b + X-GitHub-Request-Id: + - E06D:14F99C:2DEDC77:2E61FB6:6811CF0F + X-Served-By: + - cache-fra-etou8220042-FRA + X-Timer: + - S1746002929.898510,VS0,VE1 + expires: + - Wed, 30 Apr 2025 07:29:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.0.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.0.0].yaml index 0711a31b0..ecbfcfa3b 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.0.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.0.0].yaml @@ -157,11 +157,11 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:26 GMT + - Wed, 30 Apr 2025 08:48:49 GMT ETag: - W/"27c40ee3df9fd2dbdd00cea8e1d17b857c904ad289a6c2c0a9fe0cf407d24bf0" Expires: - - Tue, 29 Apr 2025 08:35:26 GMT + - Wed, 30 Apr 2025 08:53:49 GMT Source-Age: - '0' Strict-Transport-Security: @@ -171,24 +171,1062 @@ interactions: Via: - 1.1 varnish X-Cache: - - HIT + - MISS X-Cache-Hits: - '0' X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - 7a36ae34b722807e93dfe3f48c400e27363eb6c9 + - 42924440e215af557ead5eb020696d90b55955e4 X-Frame-Options: - deny X-GitHub-Request-Id: - - A91B:10BA70:79A76D:8B30B1:681087AC + - 9532:116B:1004257:11531F8:6811E3F0 X-Served-By: - - cache-fra-etou8220051-FRA + - cache-fra-etou8220132-FRA X-Timer: - - S1745915427.502040,VS0,VE227 + - S1746002929.984751,VS0,VE162 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '6' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:49 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 708cb79a28fa037030bc1e5de3f35490d37104da + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220045-FRA + X-Timer: + - S1746002929.246087,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '362' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:49 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '2' + X-Fastly-Request-ID: + - 764a946d642365a7a59b2e3993b21439a1688631 + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220163-FRA + X-Timer: + - S1746002929.351571,VS0,VE0 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '356' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:49 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 4138c397ae89b08d84976e1a791abd866a9bbcd9 + X-GitHub-Request-Id: + - CCB6:11BF:2646467:26A98A1:6811AA24 + X-Served-By: + - cache-fra-etou8220149-FRA + X-Timer: + - S1746002929.450482,VS0,VE3 + expires: + - Wed, 30 Apr 2025 04:52:12 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/file/v2.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/file/v2.1.0/schema.json#\",\n \"title\": + \"File Info Extension\",\n \"description\": \"STAC File Info Extension for + STAC Items, STAC Catalogs, and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"type\": \"object\",\n \"required\": [\n + \ \"type\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"links\": {\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Catalogs.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Catalog\"\n },\n \"links\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ]\n },\n {\n \"$comment\": + \"This is the schema for STAC Collections.\",\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"links\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/file/v2.1.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"file:byte_order\": + {\n \"type\": \"string\",\n \"enum\": [\n \"big-endian\",\n + \ \"little-endian\"\n ],\n \"title\": \"File Byte + Order\"\n },\n \"file:checksum\": {\n \"type\": \"string\",\n + \ \"pattern\": \"^[a-f0-9]+$\",\n \"title\": \"File Checksum + (Multihash)\"\n },\n \"file:header_size\": {\n \"type\": + \"integer\",\n \"minimum\": 0,\n \"title\": \"File Header + Size\"\n },\n \"file:size\": {\n \"type\": \"integer\",\n + \ \"minimum\": 0,\n \"title\": \"File Size\"\n },\n + \ \"file:values\": {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n \"required\": + [\n \"values\",\n \"summary\"\n ],\n + \ \"properties\": {\n \"values\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n + \ \"description\": \"Any data type is allowed\"\n }\n + \ },\n \"summary\": {\n \"type\": + \"string\",\n \"minLength\": 1\n }\n }\n + \ }\n },\n \"file:local_path\": {\n \"type\": + \"string\",\n \"pattern\": \"^[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+(/[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+)*/?$\",\n + \ \"title\": \"Relative File Path\"\n }\n },\n \"patternProperties\": + {\n \"^(?!file:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '3' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '4536' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:49 GMT + ETag: + - '"61b4cf00-11b8"' + Last-Modified: + - Sat, 11 Dec 2021 16:17:04 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - afaeb47490c7efd66feea509e5725cc12c46f39b + X-GitHub-Request-Id: + - 868B:11B5:DD2BB0:DF64F4:6811E3EE + X-Served-By: + - cache-fra-etou8220111-FRA + X-Timer: + - S1746002930.558707,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:46 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json#\",\n \"title\": + \"ML AOI Extension\",\n \"description\": \"ML AOI Extension for STAC definitions.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"summaries\": + {\n \"type\": \"object\",\n \"properties\": {\n + \ \"ml-aoi:split\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"$ref\": \"#/definitions/fields/properties/ml-aoi:split\"\n + \ }\n }\n }\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"ml-aoi:split\": {\n \"type\": + \"string\",\n \"enum\": [\"train\", \"test\", \"validate\"]\n },\n + \ \"ml-aoi:role\": {\n \"type\": \"string\",\n \"enum\": + [\"label\", \"feature\"]\n },\n \"ml-aoi:reference-grid\": {\n + \ \"type\": \"boolean\"\n },\n \"ml-aoi:resampling-method\": + {\n \"$comment\": \"Supported GDAL resampling method (https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r)\",\n + \ \"type\": \"string\",\n \"enum\": [\n \"near\",\n + \ \"bilinear\",\n \"cubic\",\n \"cubcspline\",\n + \ \"lanczos\",\n \"average\",\n \"rms\",\n + \ \"mode\",\n \"max\",\n \"min\",\n + \ \"med\",\n \"q1\",\n \"q3\",\n \"sum\"\n + \ ]\n }\n },\n \"patternProperties\": {\n \"^(?!ml-aoi:)\": + {}\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '340' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '3400' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:49 GMT + ETag: + - '"6605925b-d48"' + Last-Modified: + - Thu, 28 Mar 2024 15:52:59 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 1c079335dbdc15d572963f7af233e656c95abe7e + X-GitHub-Request-Id: + - E06D:14F99C:2DEDC77:2E61FB6:6811CF0F + X-Served-By: + - cache-fra-etou8220080-FRA + X-Timer: + - S1746002930.653546,VS0,VE1 + expires: + - Wed, 30 Apr 2025 07:29:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.1.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.1.0].yaml index 551738ac0..b7cdb4409 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.1.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.1.0].yaml @@ -157,11 +157,11 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:27 GMT + - Wed, 30 Apr 2025 08:48:49 GMT ETag: - W/"5c22d84a039a443127d9a87f0dbfdee885ebebdcfd2e1ee13a7ade60744f8d7f" Expires: - - Tue, 29 Apr 2025 08:35:27 GMT + - Wed, 30 Apr 2025 08:53:49 GMT Source-Age: - '0' Strict-Transport-Security: @@ -177,18 +177,1056 @@ interactions: X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - b542ececad89b84b1b8a4a56f77a9f239ae2b83b + - 7d02b19c5255e71303e5a9a17d3ca4cbf586f3d1 X-Frame-Options: - deny X-GitHub-Request-Id: - - 47BB:112C:B623DB:CE12ED:68108E20 + - 8E28:119D:9A69F9:A5B877:6811E3EA X-Served-By: - - cache-fra-etou8220107-FRA + - cache-fra-etou8220093-FRA X-Timer: - - S1745915427.782391,VS0,VE241 + - S1746002930.731532,VS0,VE228 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '7' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:50 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 64619ac3a14801dae3ba320141c3292efbd07f0f + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220146-FRA + X-Timer: + - S1746002930.135895,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '362' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:50 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 56da344255d88d52ca78446397cbbfb293a7c6a1 + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220174-FRA + X-Timer: + - S1746002930.255407,VS0,VE3 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '357' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:50 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 90a0f8fd55b659570222b66862330ef3a01285da + X-GitHub-Request-Id: + - CCB6:11BF:2646467:26A98A1:6811AA24 + X-Served-By: + - cache-fra-etou8220131-FRA + X-Timer: + - S1746002930.354278,VS0,VE2 + expires: + - Wed, 30 Apr 2025 04:52:12 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/file/v2.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/file/v2.1.0/schema.json#\",\n \"title\": + \"File Info Extension\",\n \"description\": \"STAC File Info Extension for + STAC Items, STAC Catalogs, and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"type\": \"object\",\n \"required\": [\n + \ \"type\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"links\": {\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Catalogs.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Catalog\"\n },\n \"links\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ]\n },\n {\n \"$comment\": + \"This is the schema for STAC Collections.\",\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"links\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/file/v2.1.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"file:byte_order\": + {\n \"type\": \"string\",\n \"enum\": [\n \"big-endian\",\n + \ \"little-endian\"\n ],\n \"title\": \"File Byte + Order\"\n },\n \"file:checksum\": {\n \"type\": \"string\",\n + \ \"pattern\": \"^[a-f0-9]+$\",\n \"title\": \"File Checksum + (Multihash)\"\n },\n \"file:header_size\": {\n \"type\": + \"integer\",\n \"minimum\": 0,\n \"title\": \"File Header + Size\"\n },\n \"file:size\": {\n \"type\": \"integer\",\n + \ \"minimum\": 0,\n \"title\": \"File Size\"\n },\n + \ \"file:values\": {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n \"required\": + [\n \"values\",\n \"summary\"\n ],\n + \ \"properties\": {\n \"values\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n + \ \"description\": \"Any data type is allowed\"\n }\n + \ },\n \"summary\": {\n \"type\": + \"string\",\n \"minLength\": 1\n }\n }\n + \ }\n },\n \"file:local_path\": {\n \"type\": + \"string\",\n \"pattern\": \"^[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+(/[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+)*/?$\",\n + \ \"title\": \"Relative File Path\"\n }\n },\n \"patternProperties\": + {\n \"^(?!file:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '4' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '4536' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:50 GMT + ETag: + - '"61b4cf00-11b8"' + Last-Modified: + - Sat, 11 Dec 2021 16:17:04 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - fba5b86249978d66056d486954749b1ea8239951 + X-GitHub-Request-Id: + - 868B:11B5:DD2BB0:DF64F4:6811E3EE + X-Served-By: + - cache-fra-etou8220024-FRA + X-Timer: + - S1746002930.455185,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:46 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json#\",\n \"title\": + \"ML AOI Extension\",\n \"description\": \"ML AOI Extension for STAC definitions.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"summaries\": + {\n \"type\": \"object\",\n \"properties\": {\n + \ \"ml-aoi:split\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"$ref\": \"#/definitions/fields/properties/ml-aoi:split\"\n + \ }\n }\n }\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"ml-aoi:split\": {\n \"type\": + \"string\",\n \"enum\": [\"train\", \"test\", \"validate\"]\n },\n + \ \"ml-aoi:role\": {\n \"type\": \"string\",\n \"enum\": + [\"label\", \"feature\"]\n },\n \"ml-aoi:reference-grid\": {\n + \ \"type\": \"boolean\"\n },\n \"ml-aoi:resampling-method\": + {\n \"$comment\": \"Supported GDAL resampling method (https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r)\",\n + \ \"type\": \"string\",\n \"enum\": [\n \"near\",\n + \ \"bilinear\",\n \"cubic\",\n \"cubcspline\",\n + \ \"lanczos\",\n \"average\",\n \"rms\",\n + \ \"mode\",\n \"max\",\n \"min\",\n + \ \"med\",\n \"q1\",\n \"q3\",\n \"sum\"\n + \ ]\n }\n },\n \"patternProperties\": {\n \"^(?!ml-aoi:)\": + {}\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '341' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '3400' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:50 GMT + ETag: + - '"6605925b-d48"' + Last-Modified: + - Thu, 28 Mar 2024 15:52:59 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - d5af0da6e63063b949d9f49e364f6f7fc6265f84 + X-GitHub-Request-Id: + - E06D:14F99C:2DEDC77:2E61FB6:6811CF0F + X-Served-By: + - cache-fra-etou8220076-FRA + X-Timer: + - S1746002931.549027,VS0,VE2 + expires: + - Wed, 30 Apr 2025 07:29:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.2.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.2.0].yaml index da2c6d7dd..f5ca376e6 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.2.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.2.0].yaml @@ -157,11 +157,11 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:29 GMT + - Wed, 30 Apr 2025 08:48:50 GMT ETag: - W/"610fc710cdb0bf17cbf12e94e38d37a3c0111ca0df0c43ce55494f377c599a06" Expires: - - Tue, 29 Apr 2025 08:35:29 GMT + - Wed, 30 Apr 2025 08:53:50 GMT Source-Age: - '0' Strict-Transport-Security: @@ -177,18 +177,1056 @@ interactions: X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - 5a178a4473df1ba0e3f07ad649af698a1176e9d6 + - faf2b1cf573dfb9718ebf90228830b598b126a4f X-Frame-Options: - deny X-GitHub-Request-Id: - - EF74:230D29:C11A4E:DA7002:68108E23 + - 9218:112C:29614DE:2D11292:6811E3F2 X-Served-By: - - cache-fra-etou8220096-FRA + - cache-fra-etou8220143-FRA X-Timer: - - S1745915427.183779,VS0,VE1942 + - S1746002931.636231,VS0,VE250 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '7' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:51 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 116508e56e051bcfe7643603eaacfa375680f997 + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220034-FRA + X-Timer: + - S1746002931.058296,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '363' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:51 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - d8ac3f8ec147e9347656651254db60b62600c79f + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220145-FRA + X-Timer: + - S1746002931.164839,VS0,VE2 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '358' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:51 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 6846c831ed987c00ff57b367aa6fb3b155feeba3 + X-GitHub-Request-Id: + - CCB6:11BF:2646467:26A98A1:6811AA24 + X-Served-By: + - cache-fra-etou8220107-FRA + X-Timer: + - S1746002931.270275,VS0,VE1 + expires: + - Wed, 30 Apr 2025 04:52:12 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/file/v2.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/file/v2.1.0/schema.json#\",\n \"title\": + \"File Info Extension\",\n \"description\": \"STAC File Info Extension for + STAC Items, STAC Catalogs, and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"type\": \"object\",\n \"required\": [\n + \ \"type\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"links\": {\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Catalogs.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Catalog\"\n },\n \"links\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ]\n },\n {\n \"$comment\": + \"This is the schema for STAC Collections.\",\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"links\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/file/v2.1.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"file:byte_order\": + {\n \"type\": \"string\",\n \"enum\": [\n \"big-endian\",\n + \ \"little-endian\"\n ],\n \"title\": \"File Byte + Order\"\n },\n \"file:checksum\": {\n \"type\": \"string\",\n + \ \"pattern\": \"^[a-f0-9]+$\",\n \"title\": \"File Checksum + (Multihash)\"\n },\n \"file:header_size\": {\n \"type\": + \"integer\",\n \"minimum\": 0,\n \"title\": \"File Header + Size\"\n },\n \"file:size\": {\n \"type\": \"integer\",\n + \ \"minimum\": 0,\n \"title\": \"File Size\"\n },\n + \ \"file:values\": {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n \"required\": + [\n \"values\",\n \"summary\"\n ],\n + \ \"properties\": {\n \"values\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n + \ \"description\": \"Any data type is allowed\"\n }\n + \ },\n \"summary\": {\n \"type\": + \"string\",\n \"minLength\": 1\n }\n }\n + \ }\n },\n \"file:local_path\": {\n \"type\": + \"string\",\n \"pattern\": \"^[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+(/[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+)*/?$\",\n + \ \"title\": \"Relative File Path\"\n }\n },\n \"patternProperties\": + {\n \"^(?!file:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '5' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '4536' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:51 GMT + ETag: + - '"61b4cf00-11b8"' + Last-Modified: + - Sat, 11 Dec 2021 16:17:04 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 621c46e23b749c062dd42088f4b1753f39c89c75 + X-GitHub-Request-Id: + - 868B:11B5:DD2BB0:DF64F4:6811E3EE + X-Served-By: + - cache-fra-etou8220135-FRA + X-Timer: + - S1746002931.364711,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:46 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json#\",\n \"title\": + \"ML AOI Extension\",\n \"description\": \"ML AOI Extension for STAC definitions.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"summaries\": + {\n \"type\": \"object\",\n \"properties\": {\n + \ \"ml-aoi:split\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"$ref\": \"#/definitions/fields/properties/ml-aoi:split\"\n + \ }\n }\n }\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"ml-aoi:split\": {\n \"type\": + \"string\",\n \"enum\": [\"train\", \"test\", \"validate\"]\n },\n + \ \"ml-aoi:role\": {\n \"type\": \"string\",\n \"enum\": + [\"label\", \"feature\"]\n },\n \"ml-aoi:reference-grid\": {\n + \ \"type\": \"boolean\"\n },\n \"ml-aoi:resampling-method\": + {\n \"$comment\": \"Supported GDAL resampling method (https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r)\",\n + \ \"type\": \"string\",\n \"enum\": [\n \"near\",\n + \ \"bilinear\",\n \"cubic\",\n \"cubcspline\",\n + \ \"lanczos\",\n \"average\",\n \"rms\",\n + \ \"mode\",\n \"max\",\n \"min\",\n + \ \"med\",\n \"q1\",\n \"q3\",\n \"sum\"\n + \ ]\n }\n },\n \"patternProperties\": {\n \"^(?!ml-aoi:)\": + {}\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '342' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '3400' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:51 GMT + ETag: + - '"6605925b-d48"' + Last-Modified: + - Thu, 28 Mar 2024 15:52:59 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 8aa1e689200e323ce6042a89abe6c327347ed31e + X-GitHub-Request-Id: + - E06D:14F99C:2DEDC77:2E61FB6:6811CF0F + X-Served-By: + - cache-fra-etou8220037-FRA + X-Timer: + - S1746002931.458632,VS0,VE1 + expires: + - Wed, 30 Apr 2025 07:29:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.3.0].yaml b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.3.0].yaml index 87b445e48..a3c0321d0 100644 --- a/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.3.0].yaml +++ b/tests/extensions/cassettes/test_mlm/test_migrate[https---raw.githubusercontent.com-stac-extensions-mlm-refs-tags-v{version}-examples-item_raster_bands.json-1.3.0].yaml @@ -164,11 +164,11 @@ interactions: Cross-Origin-Resource-Policy: - cross-origin Date: - - Tue, 29 Apr 2025 08:30:29 GMT + - Wed, 30 Apr 2025 08:48:51 GMT ETag: - W/"a27ee1f30298ca82fea5f599426403c50df7b1da816b19a0b602af5649b1531b" Expires: - - Tue, 29 Apr 2025 08:35:29 GMT + - Wed, 30 Apr 2025 08:53:51 GMT Source-Age: - '0' Strict-Transport-Security: @@ -184,18 +184,1056 @@ interactions: X-Content-Type-Options: - nosniff X-Fastly-Request-ID: - - 540e6adbd74fc013400f20084d209c94f3eb67ea + - 3990d560c254fad46e7869359cc67e9ac0180f01 X-Frame-Options: - deny X-GitHub-Request-Id: - - 45A5:5B0FB:C4898F:DDDFF6:68108E25 + - 4FE1:242658:8C60AA:96D16F:6811E3F3 X-Served-By: - - cache-fra-etou8220178-FRA + - cache-fra-etou8220110-FRA X-Timer: - - S1745915429.237875,VS0,VE295 + - S1746002932.545537,VS0,VE179 X-XSS-Protection: - 1; mode=block status: code: 200 message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/mlm/v1.4.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\",\n \"title\": + \"Machine Learning Model STAC Extension Schema\",\n \"description\": \"This + object represents the metadata for a Machine Learning Model (MLM) used in + STAC documents.\",\n \"$comment\": \"Use 'allOf+if/then' for each 'type' + to allow implementations to report more specific messages about the exact + case in error (if any). Using only a 'oneOf/allOf' with the 'type' caused + any incompatible 'type' to be reported first with a minimal and poorly described + error by 'pystac'.\",\n \"allOf\": [\n {\n \"description\": \"This + is the schema for STAC extension MLM in Items.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"description\": + \"Schema to validate the MLM fields permitted under Item properties or Assets + properties.\",\n \"type\": \"object\",\n \"required\": + [\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"properties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Item properties.\",\n + \ \"$ref\": \"#/$defs/mlmItemFields\"\n },\n \"assets\": + {\n \"additionalProperties\": {\n \"$comment\": + \"Schema to validate the MLM fields permitted under Asset properties.\",\n + \ \"$ref\": \"#/$defs/mlmAssetFields\"\n }\n + \ }\n }\n },\n {\n \"$ref\": + \"#/$defs/stac_extensions_mlm\"\n },\n {\n \"$comment\": + \"Schema to validate cross-references of bands between MLM inputs and any + 'bands'-compliant section describing them using another STAC definition.\",\n + \ \"$ref\": \"#/$defs/AnyBandsRef\"\n },\n {\n + \ \"$comment\": \"Schema to validate that at least one Asset defines + a model role.\",\n \"$ref\": \"#/$defs/AssetModelRoleMinimumOneDefinition\"\n + \ },\n {\n \"$comment\": \"Schema to validate + that the Asset model properties are mutually exclusive to the model role.\",\n + \ \"$ref\": \"#/$defs/AssetModelRequiredProperties\"\n }\n + \ ]\n }\n },\n {\n \"description\": \"This is the schema + for STAC extension MLM in Collections.\",\n \"if\": {\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n }\n }\n },\n + \ \"then\": {\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"properties\": {\n \"summaries\": {\n + \ \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/$defs/mlmCollectionFields\"\n }\n + \ },\n \"assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/$defs/mlmAssetFields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/$defs/mlmAssetFields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_mlm\"\n + \ }\n ]\n }\n }\n ],\n \"$defs\": {\n \"stac_extensions_mlm\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/mlm/v1.4.0/schema.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/eo/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_item\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition, + which describes the STAC-Item field named 'properties' containing 'eo:bands' + as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"properties\": {\n \"required\": + [\n \"eo:bands\"\n ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": 1,\n \"items\": + {\n \"type\": \"object\"\n }\n }\n + \ }\n }\n }\n },\n \"stac_extensions_eo_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Asset + containing 'eo:bands' as described in [https://github.com/stac-extensions/eo#item-properties-or-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"eo:bands\"\n + \ ],\n \"properties\": {\n \"eo:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\"\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_extensions_raster\": {\n \"type\": + \"object\",\n \"required\": [\n \"stac_extensions\"\n ],\n + \ \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"string\",\n + \ \"pattern\": \"https://stac-extensions\\\\.github\\\\.io/raster/v1(\\\\.[0-9]+){2}/schema\\\\.json\"\n + \ }\n }\n }\n },\n \"stac_extensions_raster_bands_asset\": + {\n \"required\": [\n \"assets\"\n ],\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + at least one Asset field containing 'raster:bands' as described in [https://github.com/stac-extensions/raster/tree/v1.1.0#item-asset-fields].\",\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"required\": [\n \"raster:bands\"\n + \ ],\n \"properties\": {\n \"raster:bands\": + {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"$comment\": \"Raster + extension does not explicitly indicate a 'name', but one is needed for MLM.\",\n + \ \"type\": \"object\",\n \"required\": + [\n \"name\"\n ],\n \"properties\": + {\n \"name\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n }\n }\n + \ }\n }\n }\n }\n }\n + \ }\n }\n },\n \"stac_version_1.1\": {\n \"$comment\": + \"Requirement for STAC 1.1 or above.\",\n \"type\": \"object\",\n \"required\": + [\n \"stac_version\"\n ],\n \"properties\": {\n \"stac_version\": + {\n \"pattern\": \"1\\\\.[1-9][0-9]*\\\\.[0-9]+(-.*)?\"\n }\n + \ }\n },\n \"fields\": {\n \"description\": \"All possible + MLM fields regardless of the level they apply (Collection, Item, Asset, Link).\",\n + \ \"type\": \"object\",\n \"properties\": {\n \"mlm:name\": + {\n \"$ref\": \"#/$defs/mlm:name\"\n },\n \"mlm:architecture\": + {\n \"$ref\": \"#/$defs/mlm:architecture\"\n },\n \"mlm:tasks\": + {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n \"mlm:framework\": + {\n \"$ref\": \"#/$defs/mlm:framework\"\n },\n \"mlm:framework_version\": + {\n \"$ref\": \"#/$defs/mlm:framework_version\"\n },\n \"mlm:memory_size\": + {\n \"$ref\": \"#/$defs/mlm:memory_size\"\n },\n \"mlm:total_parameters\": + {\n \"$ref\": \"#/$defs/mlm:total_parameters\"\n },\n \"mlm:pretrained\": + {\n \"$ref\": \"#/$defs/mlm:pretrained\"\n },\n \"mlm:pretrained_source\": + {\n \"$ref\": \"#/$defs/mlm:pretrained_source\"\n },\n \"mlm:batch_size_suggestion\": + {\n \"$ref\": \"#/$defs/mlm:batch_size_suggestion\"\n },\n + \ \"mlm:accelerator\": {\n \"$ref\": \"#/$defs/mlm:accelerator\"\n + \ },\n \"mlm:accelerator_constrained\": {\n \"$ref\": + \"#/$defs/mlm:accelerator_constrained\"\n },\n \"mlm:accelerator_summary\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_summary\"\n },\n \"mlm:accelerator_count\": + {\n \"$ref\": \"#/$defs/mlm:accelerator_count\"\n },\n \"mlm:input\": + {\n \"$ref\": \"#/$defs/mlm:input\"\n },\n \"mlm:output\": + {\n \"$ref\": \"#/$defs/mlm:output\"\n },\n \"mlm:hyperparameters\": + {\n \"$ref\": \"#/$defs/mlm:hyperparameters\"\n },\n \"mlm:artifact_type\": + {\n \"$ref\": \"#/$defs/mlm:artifact_type\"\n },\n \"mlm:compile_method\": + {\n \"$ref\": \"#/$defs/mlm:compile_method\"\n }\n },\n + \ \"$comment\": \"Allow properties not defined by MLM prefix to work with + other extensions and attributes, but disallow undefined MLM fields.\",\n \"patternProperties\": + {\n \"^(?!mlm:)\": {}\n },\n \"additionalProperties\": false\n + \ },\n \"mlmCollectionFields\": {\n \"description\": \"Schema to + validate the MLM fields permitted under Collection summaries.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Collection summaries.\",\n \"type\": \"object\",\n \"required\": + []\n },\n {\n \"description\": \"Fields that are disallowed + under the Collection summaries.\",\n \"not\": {\n \"required\": + [\n \"mlm:input\",\n \"mlm:output\",\n \"mlm:artifact_type\",\n + \ \"mlm:compile_method\"\n ]\n }\n },\n + \ {\n \"description\": \"Field with known definitions that + must be validated.\",\n \"$ref\": \"#/$defs/fields\"\n }\n + \ ]\n },\n \"mlmItemFields\": {\n \"description\": \"Schema + to validate the MLM fields permitted under Item properties.\",\n \"allOf\": + [\n {\n \"description\": \"Fields that are mandatory under + the Item properties.\",\n \"required\": [\n \"mlm:name\",\n + \ \"mlm:architecture\",\n \"mlm:tasks\",\n \"mlm:input\",\n + \ \"mlm:output\"\n ]\n },\n {\n \"description\": + \"Fields that are disallowed under the Item properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\"required\": + [\"mlm:artifact_type\"]},\n {\"required\": [\"mlm:compile_method\"]}\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlmAssetFields\": {\n + \ \"description\": \"Schema to validate the MLM fields permitted under + Assets properties.\",\n \"allOf\": [\n {\n \"description\": + \"Fields that are disallowed under the Asset properties.\",\n \"$comment\": + \"Particularity of the 'not/required' approach: they must be tested one by + one. Otherwise, it validates that they are all (simultaneously) not present.\",\n + \ \"not\": {\n \"anyOf\": [\n {\n \"required\": + [\n \"mlm:name\"\n ]\n },\n {\n + \ \"required\": [\n \"mlm:input\"\n ]\n + \ },\n {\n \"required\": [\n \"mlm:output\"\n + \ ]\n },\n {\n \"required\": + [\n \"mlm:hyperparameters\"\n ]\n }\n + \ ]\n }\n },\n {\n \"description\": + \"Field with known definitions that must be validated.\",\n \"$ref\": + \"#/$defs/fields\"\n }\n ]\n },\n \"mlm:name\": {\n \"type\": + \"string\",\n \"pattern\": \"^[a-zA-Z][a-zA-Z0-9_.\\\\-\\\\s]+[a-zA-Z0-9]$\"\n + \ },\n \"mlm:architecture\": {\n \"type\": \"string\",\n \"title\": + \"Model Architecture\",\n \"description\": \"A descriptive name of the + model architecture, typically a common name from the literature.\",\n \"examples\": + [\n \"ResNet\",\n \"VGG\",\n \"GAN\",\n \"Vision + Transformer\"\n ]\n },\n \"mlm:framework\": {\n \"title\": + \"Name of the machine learning framework used.\",\n \"anyOf\": [\n {\n + \ \"$comment\": \"Add more entries here as needed, and repeat them + in the README.\",\n \"description\": \"Notable predefined framework + names.\",\n \"type\": \"string\",\n \"enum\": [\n \"PyTorch\",\n + \ \"TensorFlow\",\n \"scikit-learn\",\n \"Hugging + Face\",\n \"Keras\",\n \"ONNX\",\n \"rgee\",\n + \ \"spatialRF\",\n \"JAX\",\n \"MXNet\",\n + \ \"Caffe\",\n \"PyMC\",\n \"Weka\"\n ]\n + \ },\n {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"pattern\": \"^(?=[^\\\\s._\\\\-]).*[^\\\\s._\\\\-]$\",\n \"description\": + \"Any other framework name to allow extension. Enum names should be preferred + when possible to allow better portability.\"\n }\n ]\n },\n + \ \"mlm:framework_version\": {\n \"title\": \"Framework version\",\n + \ \"type\": \"string\",\n \"pattern\": \"^(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)\\\\.(0|[1-9]\\\\d*)(?:-((?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\\\.(?:0|[1-9]\\\\d*|\\\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\\\+([0-9a-zA-Z-]+(?:\\\\.[0-9a-zA-Z-]+)*))?$\"\n + \ },\n \"mlm:artifact_type\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"torch.save\",\n \"torch.jit.save\",\n + \ \"torch.export.save\",\n \"tf.keras.Model.save\",\n \"tf.keras.Model.save_weights\",\n + \ \"tf.saved_model.export(format='tf_saved_model')\"\n ]\n },\n + \ \"mlm:compile_method\": {\n \"type\": \"string\",\n \"minLength\": + 1,\n \"examples\": [\n \"aot\",\n \"jit\"\n ]\n },\n + \ \"mlm:tasks\": {\n \"type\": \"array\",\n \"uniqueItems\": true,\n + \ \"items\": {\n \"type\": \"string\",\n \"enum\": [\n \"regression\",\n + \ \"classification\",\n \"scene-classification\",\n \"detection\",\n + \ \"object-detection\",\n \"segmentation\",\n \"semantic-segmentation\",\n + \ \"instance-segmentation\",\n \"panoptic-segmentation\",\n + \ \"similarity-search\",\n \"generative\",\n \"image-captioning\",\n + \ \"super-resolution\"\n ]\n }\n },\n \"mlm:memory_size\": + {\n \"description\": \"Memory size (in bytes) required to load the model + with the specified accelerator.\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:total_parameters\": {\n \"description\": \"Total + number of model parameters (weights).\",\n \"type\": \"integer\",\n \"minimum\": + 0\n },\n \"mlm:pretrained\": {\n \"type\": \"boolean\",\n \"$comment\": + \"If trained from scratch, the source should be explicitly 'null'. However, + omitting the source if pretrained is allowed.\",\n \"if\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"properties\": {\n + \ \"mlm:pretrained\": {\n \"const\": false\n }\n + \ }\n }\n }\n },\n \"then\": {\n \"$comment\": + \"This is the JSON-object 'properties' definition, which describes the STAC-Item + field named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"$comment\": \"This is the JSON-object 'properties' definition + for the STAC MLM pretraining reference.\",\n \"required\": [\n + \ \"mlm:pretrained_source\"\n ],\n \"properties\": + {\n \"mlm:pretrained_source\": {\n \"const\": + null\n }\n }\n }\n }\n }\n },\n + \ \"mlm:pretrained_source\": {\n \"description\": \"Pre-training dataset + reference or training from scratch definition.\",\n \"oneOf\": [\n {\n + \ \"type\": \"string\",\n \"description\": \"The name or + URI of the dataset used for pretraining the model.\",\n \"examples\": + [\n \"ImageNet\",\n \"EuroSAT\"\n ]\n },\n + \ {\n \"type\": \"null\",\n \"description\": \"Explicit + mention that the model is trained from scratch.\"\n }\n ]\n },\n + \ \"mlm:batch_size_suggestion\": {\n \"description\": \"Recommended + batch size to employ the model with the accelerator.\",\n \"type\": \"integer\",\n + \ \"minimum\": 0\n },\n \"mlm:accelerator\": {\n \"oneOf\": + [\n {\n \"type\": \"string\",\n \"enum\": [\n \"amd64\",\n + \ \"cuda\",\n \"xla\",\n \"amd-rocm\",\n \"intel-ipex-cpu\",\n + \ \"intel-ipex-gpu\",\n \"macos-arm\"\n ]\n + \ },\n {\n \"type\": \"null\"\n }\n ],\n + \ \"default\": null\n },\n \"mlm:accelerator_constrained\": {\n + \ \"type\": \"boolean\",\n \"default\": false\n },\n \"mlm:accelerator_summary\": + {\n \"type\": \"string\"\n },\n \"mlm:accelerator_count\": {\n + \ \"type\": \"integer\",\n \"minimum\": 1\n },\n \"mlm:input\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/$defs/ModelInput\"\n + \ }\n },\n \"ModelInput\": {\n \"title\": \"Model Input Object\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\",\n \"bands\",\n + \ \"input\"\n ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"bands\": {\n \"$ref\": \"#/$defs/ModelBands\"\n },\n + \ \"input\": {\n \"$ref\": \"#/$defs/InputStructure\"\n },\n + \ \"description\": {\n \"type\": \"string\",\n \"minLength\": + 1\n },\n \"value_scaling\": {\n \"$ref\": \"#/$defs/ValueScaling\"\n + \ },\n \"resize_type\": {\n \"$ref\": \"#/$defs/ResizeType\"\n + \ },\n \"pre_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n },\n \"mlm:output\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"title\": \"Model Output Object\",\n \"type\": + \"object\",\n \"required\": [\n \"name\",\n \"tasks\",\n + \ \"result\"\n ],\n \"properties\": {\n \"name\": + {\n \"type\": \"string\",\n \"minLength\": 1\n },\n + \ \"tasks\": {\n \"$ref\": \"#/$defs/mlm:tasks\"\n },\n + \ \"result\": {\n \"$ref\": \"#/$defs/ResultStructure\"\n + \ },\n \"description\": {\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n \"classification:classes\": + {\n \"$ref\": \"#/$defs/ClassificationClasses\"\n },\n + \ \"post_processing_function\": {\n \"$ref\": \"#/$defs/ProcessingExpression\"\n + \ }\n }\n }\n },\n \"mlm:hyperparameters\": {\n + \ \"type\": \"object\",\n \"minProperties\": 1,\n \"patternProperties\": + {\n \"^[0-9a-zA-Z_.-]+$\": true\n },\n \"additionalProperties\": + false\n },\n \"InputStructure\": {\n \"title\": \"Input Structure + Object\",\n \"type\": \"object\",\n \"required\": [\n \"shape\",\n + \ \"dim_order\",\n \"data_type\"\n ],\n \"properties\": + {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"ResultStructure\": {\n \"title\": \"Result + Structure Object\",\n \"type\": \"object\",\n \"required\": [\n + \ \"shape\",\n \"dim_order\",\n \"data_type\"\n ],\n + \ \"properties\": {\n \"shape\": {\n \"$ref\": \"#/$defs/DimensionShape\"\n + \ },\n \"dim_order\": {\n \"$ref\": \"#/$defs/DimensionOrder\"\n + \ },\n \"data_type\": {\n \"$ref\": \"#/$defs/DataType\"\n + \ }\n }\n },\n \"DimensionShape\": {\n \"type\": \"array\",\n + \ \"minItems\": 1,\n \"items\": {\n \"type\": \"integer\",\n + \ \"minimum\": -1\n }\n },\n \"DimensionOrder\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"uniqueItems\": true,\n \"items\": + {\n \"type\": \"string\",\n \"minLength\": 1,\n \"pattern\": + \"^[a-z-_]+$\",\n \"examples\": [\n \"batch\",\n \"channel\",\n + \ \"time\",\n \"height\",\n \"width\",\n \"depth\",\n + \ \"token\",\n \"class\",\n \"score\",\n \"confidence\"\n + \ ]\n }\n },\n \"ValueScaling\": {\n \"oneOf\": [\n + \ {\n \"type\": \"null\"\n },\n {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"$ref\": + \"#/$defs/ValueScalingObject\"\n }\n }\n ]\n },\n + \ \"ValueScalingObject\": {\n \"oneOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\",\n + \ \"maximum\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"min-max\"\n },\n \"minimum\": + {\n \"type\": \"number\"\n },\n \"maximum\": + {\n \"type\": \"number\"\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"required\": [\n \"type\",\n + \ \"mean\",\n \"stddev\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"z-score\"\n },\n + \ \"mean\": {\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"type\": \"number\"\n }\n + \ }\n },\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"minimum\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"minimum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-min\"\n },\n \"minimum\": {\n \"type\": + \"number\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"maximum\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"clip-max\"\n },\n \"maximum\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"offset\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"value\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"scale\"\n },\n \"value\": {\n \"type\": + \"number\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ValueScalingProcessingExpression\"\n }\n ]\n },\n + \ \"ValueScalingProcessingExpression\": {\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"processing\"\n }\n }\n },\n {\n \"$ref\": + \"#/$defs/ProcessingExpression\"\n }\n ]\n },\n \"ResizeType\": + {\n \"oneOf\": [\n {\n \"type\": \"string\",\n \"enum\": + [\n \"crop\",\n \"pad\",\n \"interpolation-nearest\",\n + \ \"interpolation-linear\",\n \"interpolation-cubic\",\n + \ \"interpolation-area\",\n \"interpolation-lanczos4\",\n + \ \"interpolation-max\",\n \"wrap-fill-outliers\",\n + \ \"wrap-inverse-map\"\n ]\n },\n {\n \"type\": + \"null\"\n }\n ]\n },\n \"ClassificationClasses\": {\n \"$comment\": + \"Must allow empty array for outputs that provide other predictions than classes.\",\n + \ \"oneOf\": [\n {\n \"$ref\": \"https://stac-extensions.github.io/classification/v1.1.0/schema.json#/definitions/fields/properties/classification:classes\"\n + \ },\n {\n \"type\": \"array\",\n \"maxItems\": + 0\n }\n ]\n },\n \"ProcessingExpression\": {\n \"oneOf\": + [\n {\n \"$ref\": \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#/definitions/fields/properties/processing:expression\"\n + \ },\n {\n \"type\": \"null\"\n }\n ]\n + \ },\n \"DataType\": {\n \"$ref\": \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#/definitions/bands/items/properties/data_type\"\n + \ },\n \"HasArtifactType\": {\n \"$comment\": \"Used to check the + artifact type property that is required by a Model Asset annotated by 'mlm:model' + role.\",\n \"type\": \"object\",\n \"required\": [\n \"mlm:artifact_type\"\n + \ ],\n \"properties\": {\n \"mlm:artifact_type\": {\n \"$ref\": + \"#/$defs/mlm:artifact_type\"\n }\n }\n },\n \"AssetModelRole\": + {\n \"$comment\": \"Used to check the presence of 'mlm:model' role required + by a Model Asset.\",\n \"type\": \"object\",\n \"required\": [\n + \ \"roles\"\n ],\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"contains\": {\n \"const\": + \"mlm:model\"\n },\n \"minItems\": 1\n }\n }\n + \ },\n \"AssetModelRequiredProperties\": {\n \"$comment\": \"Asset + containing the model definition must indicate both the 'mlm:model' role and + an artifact type.\",\n \"required\": [\n \"assets\"\n ],\n + \ \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"if\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ },\n \"then\": {\n \"$ref\": \"#/$defs/HasArtifactType\"\n + \ },\n \"else\": {\n \"not\": {\n \"$ref\": + \"#/$defs/HasArtifactType\"\n }\n }\n }\n + \ }\n }\n },\n \"AssetModelRoleMinimumOneDefinition\": {\n + \ \"$comment\": \"At least one Asset must provide the model definition + indicated by the 'mlm:model' role.\",\n \"required\": [\n \"assets\"\n + \ ],\n \"anyOf\": [\n {\n \"properties\": {\n \"assets\": + {\n \"additionalProperties\": {\n \"$ref\": \"#/$defs/AssetModelRole\"\n + \ }\n }\n }\n },\n {\n \"not\": + {\n \"properties\": {\n \"assets\": {\n \"additionalProperties\": + {\n \"properties\": {\n \"roles\": {\n + \ \"type\": \"array\",\n \"items\": + {\n \"type\": \"string\",\n \"not\": + {\n \"const\": \"mlm:model\"\n }\n + \ }\n }\n }\n }\n + \ }\n }\n }\n }\n ]\n },\n + \ \"ModelBands\": {\n \"description\": \"List of bands (if any) that + compose the input. Band order represents the index position of the bands.\",\n + \ \"$comment\": \"No 'minItems' here to support model inputs not using + any band (other data source).\",\n \"type\": \"array\",\n \"items\": + {\n \"oneOf\": [\n {\n \"description\": \"Implied + named-band with the name directly provided.\",\n \"type\": \"string\",\n + \ \"minLength\": 1\n },\n {\n \"description\": + \"Explicit named-band with optional derived expression to obtain it.\",\n + \ \"type\": \"object\",\n \"required\": [\n \"name\"\n + \ ],\n \"properties\": {\n \"name\": {\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"format\": {\n \"description\": + \"Format to interpret the specified expression used to obtain the band.\",\n + \ \"type\": \"string\",\n \"minLength\": 1\n + \ },\n \"expression\": {\n \"description\": + \"Any representation relevant for the specified 'format'.\"\n }\n + \ },\n \"dependencies\": {\n \"format\": + [\n \"expression\"\n ],\n \"expression\": + [\n \"format\"\n ]\n },\n \"additionalProperties\": + false\n }\n ]\n }\n },\n \"AnyBandsRef\": {\n \"$comment\": + \"This definition ensures that, if at least 1 named MLM input 'bands' is provided, + at least 1 of the supported references from EO, Raster or STAC Core 1.1 are + provided as well. Otherwise, 'bands' must be explicitly empty.\",\n \"if\": + {\n \"type\": \"object\",\n \"$comment\": \"This is the JSON-object + 'properties' definition, which describes the STAC-Item field named 'properties'.\",\n + \ \"properties\": {\n \"properties\": {\n \"type\": + \"object\",\n \"required\": [\n \"mlm:input\"\n ],\n + \ \"$comment\": \"This is the JSON-object 'properties' definition + for the MLM input with bands listing referring to at least one band name.\",\n + \ \"properties\": {\n \"mlm:input\": {\n \"type\": + \"array\",\n \"$comment\": \"Below 'minItems' ensures that + band check does not fail for explicitly empty 'mlm:inputs'.\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n + \ \"required\": [\n \"bands\"\n ],\n + \ \"$comment\": \"This is the 'Model Input Object' properties.\",\n + \ \"properties\": {\n \"bands\": {\n \"type\": + \"array\",\n \"minItems\": 1\n }\n + \ }\n }\n }\n }\n }\n + \ }\n },\n \"then\": {\n \"$comment\": \"Need at least + one 'bands' definition, but multiple are allowed.\",\n \"anyOf\": [\n + \ {\n \"$comment\": \"Bands described by raster extension.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_extensions_raster\"\n + \ },\n {\n \"$ref\": \"#/$defs/stac_extensions_raster_bands_asset\"\n + \ }\n ]\n },\n {\n \"$comment\": + \"Bands described by eo extension.\",\n \"allOf\": [\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo\"\n },\n + \ {\n \"anyOf\": [\n {\n \"$ref\": + \"#/$defs/stac_extensions_eo_bands_item\"\n },\n {\n + \ \"$ref\": \"#/$defs/stac_extensions_eo_bands_asset\"\n + \ }\n ]\n }\n ]\n },\n + \ {\n \"$comment\": \"Bands described by STAC Core 1.1.\",\n + \ \"allOf\": [\n {\n \"$ref\": \"#/$defs/stac_version_1.1\"\n + \ },\n {\n \"$comment\": \"This is + the JSON-object 'properties' definition, which describes the STAC-Item field + named 'properties'.\",\n \"properties\": {\n \"properties\": + {\n \"required\": [\n \"bands\"\n + \ ],\n \"$comment\": \"This is the JSON-object + 'properties' definition for the STAC Core 'bands' field defined by [https://github.com/radiantearth/stac-spec/blob/bands/item-spec/common-metadata.md#bands].\",\n + \ \"properties\": {\n \"bands\": {\n + \ \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": + \"object\"\n }\n }\n }\n + \ }\n }\n }\n ]\n }\n + \ ]\n },\n \"else\": {\n \"$comment\": \"Case where + no 'bands' (empty list) are referenced in the MLM input. Because models can + use a mixture of inputs with/without bands, we cannot enforce eo/raster/stac + bands references to be omitted. If bands are provided in the 'mlm:model', + it will simply be an odd case if none are used in any 'mlm:input' bands'.\"\n + \ }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '8' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '33494' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:51 GMT + ETag: + - '"67a12887-82d6"' + Last-Modified: + - Mon, 03 Feb 2025 20:35:19 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - ea3acb22572b1c15d3e138abfdd6eadd70d2934e + X-GitHub-Request-Id: + - A3CF:119E:933E42:94ABEA:6811E3EB + X-Served-By: + - cache-fra-etou8220096-FRA + X-Timer: + - S1746002932.850023,VS0,VE2 + expires: + - Wed, 30 Apr 2025 08:58:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/raster/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json#\",\n \"title\": + \"raster Extension\",\n \"description\": \"STAC Raster Extension for STAC + Items.\",\n \"oneOf\": [\n {\n \"$comment\": \"This is the schema + for STAC extension raster in Items.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\",\n \"assets\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Feature\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n }\n }\n + \ },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Collections.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/assetfields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/assetfields\"\n }\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/raster/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"assetfields\": {\n \"type\": \"object\",\n + \ \"properties\": {\n \"raster:bands\": {\n \"$ref\": + \"#/definitions/bands\"\n }\n },\n \"patternProperties\": + {\n \"^(?!raster:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n },\n \"bands\": {\n \"title\": \"Bands\",\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n \"title\": + \"Band\",\n \"type\": \"object\",\n \"minProperties\": 1,\n + \ \"additionalProperties\": true,\n \"properties\": {\n \"data_type\": + {\n \"title\": \"Data type of the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"int8\",\n \"int16\",\n + \ \"int32\",\n \"int64\",\n \"uint8\",\n + \ \"uint16\",\n \"uint32\",\n \"uint64\",\n + \ \"float16\",\n \"float32\",\n \"float64\",\n + \ \"cint16\",\n \"cint32\",\n \"cfloat32\",\n + \ \"cfloat64\",\n \"other\"\n ]\n },\n + \ \"unit\": {\n \"title\": \"Unit denomination of the pixel + value\",\n \"type\": \"string\"\n },\n \"bits_per_sample\": + {\n \"title\": \"The actual number of bits used for this band\",\n + \ \"type\": \"integer\"\n },\n \"sampling\": {\n + \ \"title\": \"Pixel sampling in the band\",\n \"type\": + \"string\",\n \"enum\": [\n \"area\",\n \"point\"\n + \ ]\n },\n \"nodata\": {\n \"title\": + \"No data pixel value\",\n \"oneOf\": [\n {\n \"type\": + \"number\"\n },\n {\n \"type\": \"string\",\n + \ \"enum\": [\n \"nan\",\n \"inf\",\n + \ \"-inf\"\n ]\n }\n ]\n + \ },\n \"scale\": {\n \"title\": \"multiplicator + factor of the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"offset\": {\n \"title\": + \"number to be added to the pixel value to transform into the value\",\n \"type\": + \"number\"\n },\n \"spatial_resolution\": {\n \"title\": + \"Average spatial resolution (in meters) of the pixels in the band\",\n \"type\": + \"number\"\n },\n \"statistics\": {\n \"title\": + \"Statistics\",\n \"type\": \"object\",\n \"minProperties\": + 1,\n \"additionalProperties\": false,\n \"properties\": + {\n \"mean\": {\n \"title\": \"Mean value of all + the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"minimum\": {\n \"title\": \"Minimum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"maximum\": {\n \"title\": \"Maximum value of + all the pixels in the band\",\n \"type\": \"number\"\n },\n + \ \"stddev\": {\n \"title\": \"Standard deviation + value of all the pixels in the band\",\n \"type\": \"number\"\n + \ },\n \"valid_percent\": {\n \"title\": + \"Percentage of valid (not nodata) pixel\",\n \"type\": \"number\"\n + \ }\n }\n },\n \"histogram\": {\n + \ \"title\": \"Histogram\",\n \"type\": \"object\",\n + \ \"additionalItems\": false,\n \"required\": [\n \"count\",\n + \ \"min\",\n \"max\",\n \"buckets\"\n + \ ],\n \"additionalProperties\": false,\n \"properties\": + {\n \"count\": {\n \"title\": \"number of buckets\",\n + \ \"type\": \"number\"\n },\n \"min\": + {\n \"title\": \"Minimum value of the buckets\",\n \"type\": + \"number\"\n },\n \"max\": {\n \"title\": + \"Maximum value of the buckets\",\n \"type\": \"number\"\n + \ },\n \"buckets\": {\n \"title\": + \"distribution buckets\",\n \"type\": \"array\",\n \"minItems\": + 3,\n \"items\": {\n \"title\": \"number of + pixels in the bucket\",\n \"type\": \"integer\"\n }\n + \ }\n }\n }\n }\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '364' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '6318' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:51 GMT + ETag: + - '"66df5c80-18ae"' + Last-Modified: + - Mon, 09 Sep 2024 20:37:20 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 2b02ba0c8898ac2e1e4ff998eb889de5117ca36d + X-GitHub-Request-Id: + - 64B5:6EDF6:28F2EAC:295801B:681184F8 + X-Served-By: + - cache-fra-etou8220160-FRA + X-Timer: + - S1746002932.950606,VS0,VE2 + expires: + - Wed, 30 Apr 2025 02:13:36 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/processing/v1.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json#\",\n \"title\": + \"Processing Extension\",\n \"description\": \"STAC Processing Extension + for STAC Items and STAC Collections.\",\n \"anyOf\": [\n {\n \"$comment\": + \"This is the schema for STAC Items.\",\n \"type\": \"object\",\n \"required\": + [\n \"type\",\n \"properties\",\n \"assets\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": \"Feature\"\n + \ },\n \"properties\": {\n \"allOf\": [\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n },\n + \ {\n \"$ref\": \"#/definitions/fields\"\n }\n + \ ]\n },\n \"assets\": {\n \"$comment\": \"This + validates the fields in Item Assets, but does not require them.\",\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n }\n },\n \"allOf\": + [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n }\n + \ ]\n },\n {\n \"$comment\": \"This is the schema for STAC + Collections.\",\n \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"providers\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"item_assets\": {\n \"type\": \"object\",\n + \ \"additionalProperties\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n },\n \"summaries\": {\n \"$comment\": + \"The values of summaries are not validated yet!\"\n },\n \"assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": {\n + \ \"$ref\": \"#/definitions/fields\"\n }\n }\n },\n + \ \"allOf\": [\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ],\n \"anyOf\": [\n {\n \"$comment\": + \"Requires at least one provider to contain processing fields.\",\n \"type\": + \"object\",\n \"required\": [\n \"providers\"\n ],\n + \ \"properties\": {\n \"providers\": {\n \"type\": + \"array\",\n \"contains\": {\n \"type\": \"object\",\n + \ \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n + \ ],\n \"properties\": {\n \"roles\": + {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n + \ \"processor\"\n ]\n }\n + \ }\n }\n },\n {\n + \ \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ ]\n }\n }\n }\n },\n + \ {\n \"$comment\": \"Requires at least one asset to contain + processing fields.\",\n \"type\": \"object\",\n \"required\": + [\n \"assets\"\n ],\n \"properties\": {\n \"assets\": + {\n \"type\": \"object\",\n \"not\": {\n \"additionalProperties\": + {\n \"not\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n }\n }\n + \ },\n {\n \"$comment\": \"Requires at least one item + asset definition to contain processing fields.\",\n \"type\": \"object\",\n + \ \"required\": [\n \"item_assets\"\n ],\n \"properties\": + {\n \"item_assets\": {\n \"type\": \"object\",\n \"not\": + {\n \"additionalProperties\": {\n \"not\": + {\n \"$ref\": \"#/definitions/require_any_field\"\n }\n + \ }\n }\n }\n }\n },\n + \ {\n \"type\": \"object\",\n \"$comment\": \"Requires + at least one summary to be a processing field.\",\n \"required\": + [\n \"summaries\"\n ],\n \"properties\": {\n + \ \"summaries\": {\n \"$ref\": \"#/definitions/require_any_field\"\n + \ }\n }\n }\n ]\n }\n ],\n \"definitions\": + {\n \"stac_extensions\": {\n \"type\": \"object\",\n \"required\": + [\n \"stac_extensions\"\n ],\n \"properties\": {\n \"stac_extensions\": + {\n \"type\": \"array\",\n \"contains\": {\n \"const\": + \"https://stac-extensions.github.io/processing/v1.1.0/schema.json\"\n }\n + \ }\n }\n },\n \"require_provider_role\": {\n \"type\": + \"object\",\n \"required\": [\n \"roles\"\n ],\n \"properties\": + {\n \"roles\": {\n \"type\": \"array\",\n \"contains\": + {\n \"enum\": [\n \"producer\",\n \"processor\"\n + \ ]\n }\n }\n }\n },\n \"require_any_field\": + {\n \"anyOf\": [\n {\"type\": \"object\", \"required\": [\"processing:expression\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:lineage\"]},\n + \ {\"type\": \"object\", \"required\": [\"processing:level\"]},\n {\"type\": + \"object\", \"required\": [\"processing:facility\"]},\n {\"type\": + \"object\", \"required\": [\"processing:software\"]}\n ]\n },\n \"fields\": + {\n \"type\": \"object\",\n \"properties\": {\n \"processing:expression\": + {\n \"title\": \"Processing Expression\",\n \"type\": \"object\",\n + \ \"required\": [\n \"format\",\n \"expression\"\n + \ ],\n \"properties\": {\n \"format\": {\n \"type\": + \"string\"\n },\n \"expression\": {\n \"description\": + \"Any data type, depending on the format chosen.\"\n }\n }\n + \ },\n \"processing:lineage\": {\n \"title\": \"Processing + Lineage Information\",\n \"type\": \"string\",\n \"examples\": + [\n \"Post Processing GRD\"\n ]\n },\n \"processing:level\": + {\n \"title\": \"Processing Level\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"RAW\",\n \"L1\",\n \"L1A\",\n + \ \"L1B\",\n \"L1C\",\n \"L2\",\n \"L2A\",\n + \ \"L3\",\n \"L4\"\n ]\n },\n \"processing:facility\": + {\n \"title\": \"Processing Facility\",\n \"type\": \"string\",\n + \ \"examples\": [\n \"Copernicus S1 Core Ground Segment + - DPA\"\n ]\n },\n \"processing:software\": {\n \"title\": + \"Processing Software Name / version\",\n \"type\": \"object\",\n + \ \"patternProperties\": {\n \".{1,}\": {\n \"type\": + \"string\"\n }\n },\n \"examples\": [\n {\n + \ \"Sentinel-1 IPF\": \"002.71\"\n }\n ]\n + \ }\n },\n \"patternProperties\": {\n \"^(?!processing:)\": + {}\n },\n \"additionalProperties\": false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '359' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '7146' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:52 GMT + ETag: + - '"663cfd3e-1bea"' + Last-Modified: + - Thu, 09 May 2024 16:43:42 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - e95607f0f6ec3b7e2cc08a772e10d0b1b55a5c09 + X-GitHub-Request-Id: + - CCB6:11BF:2646467:26A98A1:6811AA24 + X-Served-By: + - cache-fra-etou8220168-FRA + X-Timer: + - S1746002932.035253,VS0,VE2 + expires: + - Wed, 30 Apr 2025 04:52:12 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/file/v2.1.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/file/v2.1.0/schema.json#\",\n \"title\": + \"File Info Extension\",\n \"description\": \"STAC File Info Extension for + STAC Items, STAC Catalogs, and STAC Collections.\",\n \"oneOf\": [\n {\n + \ \"$comment\": \"This is the schema for STAC Items.\",\n \"allOf\": + [\n {\n \"type\": \"object\",\n \"required\": [\n + \ \"type\",\n \"assets\"\n ],\n \"properties\": + {\n \"type\": {\n \"const\": \"Feature\"\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"links\": {\n \"type\": \"array\",\n \"items\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Catalogs.\",\n \"allOf\": [\n {\n \"type\": + \"object\",\n \"required\": [\n \"type\"\n ],\n + \ \"properties\": {\n \"type\": {\n \"const\": + \"Catalog\"\n },\n \"links\": {\n \"type\": + \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/fields\"\n + \ }\n }\n }\n },\n {\n \"$ref\": + \"#/definitions/stac_extensions\"\n }\n ]\n },\n {\n \"$comment\": + \"This is the schema for STAC Collections.\",\n \"allOf\": [\n {\n + \ \"type\": \"object\",\n \"required\": [\n \"type\"\n + \ ],\n \"properties\": {\n \"type\": {\n \"const\": + \"Collection\"\n },\n \"assets\": {\n \"type\": + \"object\",\n \"additionalProperties\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"links\": + {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": + \"#/definitions/fields\"\n }\n },\n \"item_assets\": + {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/file/v2.1.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"$comment\": + \"Add your new fields here. Don't require them here, do that above in the + item schema.\",\n \"type\": \"object\",\n \"properties\": {\n \"file:byte_order\": + {\n \"type\": \"string\",\n \"enum\": [\n \"big-endian\",\n + \ \"little-endian\"\n ],\n \"title\": \"File Byte + Order\"\n },\n \"file:checksum\": {\n \"type\": \"string\",\n + \ \"pattern\": \"^[a-f0-9]+$\",\n \"title\": \"File Checksum + (Multihash)\"\n },\n \"file:header_size\": {\n \"type\": + \"integer\",\n \"minimum\": 0,\n \"title\": \"File Header + Size\"\n },\n \"file:size\": {\n \"type\": \"integer\",\n + \ \"minimum\": 0,\n \"title\": \"File Size\"\n },\n + \ \"file:values\": {\n \"type\": \"array\",\n \"minItems\": + 1,\n \"items\": {\n \"type\": \"object\",\n \"required\": + [\n \"values\",\n \"summary\"\n ],\n + \ \"properties\": {\n \"values\": {\n \"type\": + \"array\",\n \"minItems\": 1,\n \"items\": {\n + \ \"description\": \"Any data type is allowed\"\n }\n + \ },\n \"summary\": {\n \"type\": + \"string\",\n \"minLength\": 1\n }\n }\n + \ }\n },\n \"file:local_path\": {\n \"type\": + \"string\",\n \"pattern\": \"^[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+(/[^\\\\r\\\\n\\\\t\\\\\\\\:'\\\"/]+)*/?$\",\n + \ \"title\": \"Relative File Path\"\n }\n },\n \"patternProperties\": + {\n \"^(?!file:)\": {\n \"$comment\": \"Above, change `template` + to the prefix of this extension\"\n }\n },\n \"additionalProperties\": + false\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '6' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '4536' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:52 GMT + ETag: + - '"61b4cf00-11b8"' + Last-Modified: + - Sat, 11 Dec 2021 16:17:04 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - 2fd6aeb2d6ee2a363a2f7c0678ba774b9ebdcfff + X-GitHub-Request-Id: + - 868B:11B5:DD2BB0:DF64F4:6811E3EE + X-Served-By: + - cache-fra-etou8220145-FRA + X-Timer: + - S1746002932.180129,VS0,VE1 + expires: + - Wed, 30 Apr 2025 08:58:46 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK +- request: + body: null + headers: + Connection: + - close + Host: + - stac-extensions.github.io + User-Agent: + - Python-urllib/3.10 + method: GET + uri: https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json + response: + body: + string: "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n \"$id\": + \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json#\",\n \"title\": + \"ML AOI Extension\",\n \"description\": \"ML AOI Extension for STAC definitions.\",\n + \ \"oneOf\": [\n {\n \"$comment\": \"This is the schema for STAC Collections.\",\n + \ \"allOf\": [\n {\n \"type\": \"object\",\n \"required\": + [\n \"type\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Collection\"\n },\n \"summaries\": + {\n \"type\": \"object\",\n \"properties\": {\n + \ \"ml-aoi:split\": {\n \"type\": \"array\",\n + \ \"items\": {\n \"$ref\": \"#/definitions/fields/properties/ml-aoi:split\"\n + \ }\n }\n }\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n },\n + \ \"item_assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n },\n {\n \"$comment\": \"This is the schema + for STAC Items.\",\n \"allOf\": [\n {\n \"type\": \"object\",\n + \ \"required\": [\n \"type\",\n \"properties\",\n + \ \"assets\"\n ],\n \"properties\": {\n \"type\": + {\n \"const\": \"Feature\"\n },\n \"properties\": + {\n \"allOf\": [\n {\n \"$ref\": + \"#/definitions/fields\"\n }\n ]\n },\n + \ \"assets\": {\n \"type\": \"object\",\n \"additionalProperties\": + {\n \"$ref\": \"#/definitions/fields\"\n }\n }\n + \ }\n },\n {\n \"$ref\": \"#/definitions/stac_extensions\"\n + \ }\n ]\n }\n ],\n \"definitions\": {\n \"stac_extensions\": + {\n \"type\": \"object\",\n \"required\": [\n \"stac_extensions\"\n + \ ],\n \"properties\": {\n \"stac_extensions\": {\n \"type\": + \"array\",\n \"contains\": {\n \"const\": \"https://stac-extensions.github.io/ml-aoi/v0.2.0/schema.json\"\n + \ }\n }\n }\n },\n \"fields\": {\n \"type\": + \"object\",\n \"properties\": {\n \"ml-aoi:split\": {\n \"type\": + \"string\",\n \"enum\": [\"train\", \"test\", \"validate\"]\n },\n + \ \"ml-aoi:role\": {\n \"type\": \"string\",\n \"enum\": + [\"label\", \"feature\"]\n },\n \"ml-aoi:reference-grid\": {\n + \ \"type\": \"boolean\"\n },\n \"ml-aoi:resampling-method\": + {\n \"$comment\": \"Supported GDAL resampling method (https://gdal.org/programs/gdalwarp.html#cmdoption-gdalwarp-r)\",\n + \ \"type\": \"string\",\n \"enum\": [\n \"near\",\n + \ \"bilinear\",\n \"cubic\",\n \"cubcspline\",\n + \ \"lanczos\",\n \"average\",\n \"rms\",\n + \ \"mode\",\n \"max\",\n \"min\",\n + \ \"med\",\n \"q1\",\n \"q3\",\n \"sum\"\n + \ ]\n }\n },\n \"patternProperties\": {\n \"^(?!ml-aoi:)\": + {}\n }\n }\n }\n}\n" + headers: + Accept-Ranges: + - bytes + Access-Control-Allow-Origin: + - '*' + Age: + - '343' + Cache-Control: + - max-age=600 + Connection: + - close + Content-Length: + - '3400' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 30 Apr 2025 08:48:52 GMT + ETag: + - '"6605925b-d48"' + Last-Modified: + - Thu, 28 Mar 2024 15:52:59 GMT + Server: + - GitHub.com + Strict-Transport-Security: + - max-age=31556952 + Vary: + - Accept-Encoding + Via: + - 1.1 varnish + X-Cache: + - HIT + X-Cache-Hits: + - '1' + X-Fastly-Request-ID: + - accce148756425d70a4443818f038623af3e340c + X-GitHub-Request-Id: + - E06D:14F99C:2DEDC77:2E61FB6:6811CF0F + X-Served-By: + - cache-fra-etou8220047-FRA + X-Timer: + - S1746002932.279262,VS0,VE1 + expires: + - Wed, 30 Apr 2025 07:29:43 GMT + permissions-policy: + - interest-cohort=() + x-proxy-cache: + - MISS + status: + code: 200 + message: OK version: 1 diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index ebbe09cb5..9717c658c 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1818,8 +1818,6 @@ def test_migration_1_3_to_1_4_collection() -> None: "https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/" "v{version}/examples/item_basic.json", "https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/" - "v{version}/examples/item_eo_bands.json", - "https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/" "v{version}/examples/item_multi_io.json", "https://raw.githubusercontent.com/stac-extensions/mlm/refs/tags/" "v{version}/examples/item_raster_bands.json", @@ -1848,3 +1846,5 @@ def test_migrate(url_template: str, version: str) -> None: assert MLMExtension.get_schema_uri() in item.stac_extensions assert old_uri not in item.stac_extensions assert new_uri not in item.stac_extensions + + item.validate() From 40c7925eb26bf2d48ad9ef64393d9ae9bf437dd1 Mon Sep 17 00:00:00 2001 From: = Date: Wed, 30 Apr 2025 11:00:19 +0200 Subject: [PATCH 63/68] update exception message --- tests/extensions/test_mlm.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 9717c658c..50a9a8200 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1839,7 +1839,9 @@ def test_migrate(url_template: str, version: str) -> None: data["stac_extensions"][i] = new_uri except ValueError: if new_uri not in data["stac_extensions"]: - raise Exception("Stac object does not list stac:mlm as extension") + raise Exception( + f"Stac object does not list stac:mlm v{version} as extension" + ) item = pystac.Item.from_dict(data) From f58cc6a350ffb0c25b671815b9be32d2a0ff8ebd Mon Sep 17 00:00:00 2001 From: = Date: Fri, 2 May 2025 12:54:45 +0200 Subject: [PATCH 64/68] added raster migration --- pystac/extensions/mlm.py | 77 ++++++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index 40b5d8f0d..acd9b6c66 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2152,36 +2152,67 @@ def migrate(props_obj: dict[str, Any]) -> None: if "mlm:input" not in props_obj: return - bands_objs_present = any("bands" in inp for inp in props_obj["mlm:input"]) + # check if mlm:input.bands is present and contains items + bands_objs_present = [ + "bands" in inp and len(inp["bands"]) > 0 + for inp in props_obj["mlm:input"] + ] - if not bands_objs_present: + if not any(bands_objs_present): return - if "raster:bands" not in props_obj: + if "eo:bands" in props_obj or "bands" in props_obj: return - raster_bands = props_obj["raster:bands"] - # make sure all raster_bands have a name prop with length>0 - names_properties_valid = all( - "name" in band and len(band["name"]) > 0 for band in raster_bands - ) - if not names_properties_valid: - raise STACError( - "Error migrating stac:mlm version: In mlm>=1.3, each band in " - 'raster:bands is required to have a property "name"' + if ( + "raster:bands" in props_obj + and "eo:bands" not in props_obj + and "bands" not in props_obj + ): + raster_bands = props_obj["raster:bands"] + + bands_valid = all( + "name" in band and len(band["name"]) > 0 for band in raster_bands ) - # no need to perform the actions below if props_obj is an asset - # this is checked by the presence of "roles" prop - if "roles" in props_obj: - return + if not bands_valid: + raise STACError( + "Error migrating stac:mlm version: In mlm>=1.3, each band in " + 'raster:bands is required to have a property "name" with ' + "length > 0" + ) - # copy the raster:bands to assets - for inner_asset_name in obj["assets"]: - inner_asset = obj["assets"][inner_asset_name] - if "mlm:model" not in inner_asset["roles"]: - continue - inner_asset["raster:bands"] = raster_bands + # no need to perform the actions below if props_obj is not an asset + # this is checked by the presence of "roles" prop + if "roles" in props_obj: + return + + # move raster:bands to assets that contain "mlm:model" role + for inner_asset_name in obj["assets"]: + inner_asset = obj["assets"][inner_asset_name] + if "mlm:model" not in inner_asset["roles"]: + continue + inner_asset["raster:bands"] = raster_bands + props_obj.pop("raster:bands") + + # create new bands object from mlm:input.bands if none exist + if ( + "raster:bands" not in props_obj + and "eo:bands" not in props_obj + and "bands" not in props_obj + ): + i = bands_objs_present.index(True) + bands = [ + {"name": band if type(band) is str else band["name"]} + for band in props_obj["mlm:input"][i]["bands"] + ] + + # copy the raster:bands to assets + for inner_asset_name in obj["assets"]: + inner_asset = obj["assets"][inner_asset_name] + if "mlm:model" not in inner_asset["roles"]: + continue + inner_asset["raster:bands"] = bands if obj["type"] == "Feature" and "mlm:input" in obj["properties"]: migrate(obj["properties"]) @@ -2281,7 +2312,7 @@ def migrate(props_obj: dict[str, Any]) -> None: # add new REQUIRED proretie mlm:artifact_type to asset if "mlm:model" in obj["assets"][asset]["roles"]: - obj["assets"][asset]["mlm:artifact_type"] = "" + obj["assets"][asset]["mlm:artifact_type"] = "asdf" def migrate( self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription From 8f63dfb4ec9921d437bee2cf5d7fcaf5f2cfb98f Mon Sep 17 00:00:00 2001 From: = Date: Fri, 2 May 2025 14:27:16 +0200 Subject: [PATCH 65/68] updated default artifact_type when migrating --- pystac/extensions/mlm.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pystac/extensions/mlm.py b/pystac/extensions/mlm.py index acd9b6c66..781b3b0f9 100644 --- a/pystac/extensions/mlm.py +++ b/pystac/extensions/mlm.py @@ -2312,7 +2312,10 @@ def migrate(props_obj: dict[str, Any]) -> None: # add new REQUIRED proretie mlm:artifact_type to asset if "mlm:model" in obj["assets"][asset]["roles"]: - obj["assets"][asset]["mlm:artifact_type"] = "asdf" + obj["assets"][asset]["mlm:artifact_type"] = ( + "Placeholder string to satisfy requirements when migrating " + "from mlm v1.3 to v1.4" + ) def migrate( self, obj: dict[str, Any], version: STACVersionID, info: STACJSONDescription From 50dd1749a74b764f09b82e0f01cc0190e0192531 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 2 May 2025 14:57:09 +0200 Subject: [PATCH 66/68] added tests --- tests/extensions/test_mlm.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/extensions/test_mlm.py b/tests/extensions/test_mlm.py index 50a9a8200..c091b13ac 100644 --- a/tests/extensions/test_mlm.py +++ b/tests/extensions/test_mlm.py @@ -1253,6 +1253,7 @@ def test_migration_1_1_to_1_2(asset_type: str) -> None: ], True, ), + (["B02", "B03"], None, True), ( ["B02", "B03"], [ @@ -1317,6 +1318,7 @@ def test_migration_1_2_to_1_3_item( ], True, ), + (["B02", "B03"], None, True), ( ["B02", "B03"], [ @@ -1381,6 +1383,7 @@ def test_migration_1_2_to_1_3_collection( ], True, ), + (["B02", "B03"], None, True), ( ["B02", "B03"], [ From d7ae1ecea542b3ac982479c109e3fb9d11a4ea57 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 2 May 2025 15:22:08 +0200 Subject: [PATCH 67/68] added mlm changes --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 07de048e6..40fe9bc00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,15 @@ media type value for these types and new media types COPC and VND_PMTILES ([#1554](https://github.com/stac-utils/pystac/pull/1554)) +### Fixed + +- `extensions.mlm` various fixes + - Fixed ResizeType typos `interpolation-nearest` and `interpolation-linear` + - Fixed displaying the correct property in error message for `ResultStructure.data_type` + - Fixed `ValueScaling.maximum` setter to pop when None is given + - Fixed `ModelInput.value_scaling` to be `list[ValueScaling]` instead of `ValueScaling` + - Fixed missing version migrations + ## [v1.13.0] - 2025-04-15 ### Added From df2ef7f99c328233f5f1d305e845ed3b76bfe416 Mon Sep 17 00:00:00 2001 From: = Date: Fri, 2 May 2025 15:24:47 +0200 Subject: [PATCH 68/68] added PR --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40fe9bc00..deb380d66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Fixed -- `extensions.mlm` various fixes +- `extensions.mlm` various fixes [#1556](https://github.com/stac-utils/pystac/pull/1556) - Fixed ResizeType typos `interpolation-nearest` and `interpolation-linear` - Fixed displaying the correct property in error message for `ResultStructure.data_type` - Fixed `ValueScaling.maximum` setter to pop when None is given