Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extension/datacube - added variable property #645

Merged
merged 4 commits into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
([#590](https://github.com/stac-utils/pystac/pull/590))
- Links will get their `title` from their target if no `title` is provided ([#607](https://github.com/stac-utils/pystac/pull/607))
- Relax typing on `LabelClasses` from `List` to `Sequence` ([#627](https://github.com/stac-utils/pystac/pull/627))
- Upgraded datacube-extension to version 2.0.0 ([#645](https://github.com/stac-utils/pystac/pull/645))

### Fixed

Expand Down
8 changes: 8 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,14 @@ AdditionalDimension
:show-inheritance:
:inherited-members:

Variable
thomafred marked this conversation as resolved.
Show resolved Hide resolved
~~~~~~~~

.. autoclass:: pystac.extensions.datacube.Variable
:members:
:show-inheritance:
:inherited-members:

DatacubeExtension
~~~~~~~~~~~~~~~~~

Expand Down
119 changes: 116 additions & 3 deletions pystac/extensions/datacube.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,31 @@

T = TypeVar("T", pystac.Collection, pystac.Item, pystac.Asset)

SCHEMA_URI = "https://stac-extensions.github.io/datacube/v1.0.0/schema.json"
SCHEMA_URI = "https://stac-extensions.github.io/datacube/v2.0.0/schema.json"

PREFIX: str = "cube:"
DIMENSIONS_PROP = PREFIX + "dimensions"
VARIABLES_PROP = PREFIX + "variables"

# Dimension properties
DIM_TYPE_PROP = "type"
DIM_DESC_PROP = "description"
DIM_AXIS_PROP = "axis"
DIM_EXTENT_PROP = "extent"
DIM_VALUES_PROP = "values"
DIM_DIMENSIONS_PROP = "dimensions"
thomafred marked this conversation as resolved.
Show resolved Hide resolved
thomafred marked this conversation as resolved.
Show resolved Hide resolved
DIM_STEP_PROP = "step"
DIM_REF_SYS_PROP = "reference_system"
DIM_UNIT_PROP = "unit"

# Variable properties
VAR_TYPE_PROP = "type"
VAR_DESC_PROP = "description"
VAR_EXTENT_PROP = "extent"
VAR_VALUES_PROP = "values"
VAR_DIMENSIONS_PROP = "dimensions"
VAR_UNIT_PROP = "unit"


class DimensionType(str, Enum):
"""Dimension object types for spatial and temporal Dimension Objects."""
Expand Down Expand Up @@ -398,6 +408,102 @@ def reference_system(self, v: Optional[Union[str, float, Dict[str, Any]]]) -> No
self.properties[DIM_REF_SYS_PROP] = v


class VariableType(str, Enum):
"""Variable object types"""

DATA = "data"
AUXILIARY = "auxiliary"


class Variable:
properties: Dict[str, Any]

def __init__(self, properties: Dict[str, Any]) -> None:
self.properties = properties

@property
def dimensions(self) -> List[str]:
"""The dimensions of the variable. Should refer to keys in the ``cube:dimensions``
object or be an empty list if the variable has no dimensions"""
return get_required(
self.properties.get(VAR_DIMENSIONS_PROP),
"cube:variable",
VAR_DIMENSIONS_PROP,
)

@dimensions.setter
def dimensions(self, v: List[str]) -> None:
self.properties[VAR_DIMENSIONS_PROP] = v

@property
def var_type(self) -> Union[VariableType, str]:
"""Type of the variable, either ``data`` or ``auxiliary``"""
return get_required(
self.properties.get(VAR_TYPE_PROP), "cube:variable", VAR_TYPE_PROP
)

@var_type.setter
def var_type(self, v: Union[VariableType, str]) -> None:
self.properties[VAR_TYPE_PROP] = v

@property
def description(self) -> Optional[str]:
"""Detailed multi-line description to explain the variable. `CommonMark 0.29
<http://commonmark.org/>`__ syntax MAY be used for rich text representation."""
return self.properties.get(VAR_DESC_PROP)

@description.setter
def description(self, v: Optional[str]) -> None:
if v is None:
self.properties.pop(VAR_DESC_PROP, None)
else:
self.properties[VAR_DESC_PROP] = v

@property
def extent(self) -> List[Union[float, str, None]]:
"""If the variable consists of `ordinal values
<https://en.wikipedia.org/wiki/Level_of_measurement#Ordinal_scale>`, the extent
(lower and upper bounds) of the values as two-dimensional array. Use ``None``
for open intervals"""
return get_required(
self.properties.get(VAR_EXTENT_PROP), "cube:variable", VAR_EXTENT_PROP
)

@extent.setter
def extent(self, v: List[Union[float, str, None]]) -> None:
self.properties[VAR_EXTENT_PROP] = v

@property
def values(self) -> Optional[List[Union[float, str]]]:
"""A set of all potential values, especially useful for `nominal values
<https://en.wikipedia.org/wiki/Level_of_measurement#Nominal_level>`."""
return self.properties.get(VAR_VALUES_PROP)

@values.setter
def values(self, v: Optional[List[Union[float, str]]]) -> None:
if v is None:
self.properties.pop(VAR_VALUES_PROP)
else:
self.properties[VAR_VALUES_PROP] = v

@property
def unit(self) -> Optional[str]:
"""The unit of measurement for the data, preferably compliant to `UDUNITS-2
<https://ncics.org/portfolio/other-resources/udunits2/>` units (singular)"""
return self.properties.get(VAR_UNIT_PROP)

@unit.setter
def unit(self, v: Optional[str]) -> None:
if v is None:
self.properties.pop(VAR_UNIT_PROP)
else:
self.properties[VAR_UNIT_PROP] = v

@staticmethod
def from_dict(d: Dict[str, Any]) -> "Variable":
return Variable(d)


class DatacubeExtension(
Generic[T],
PropertiesExtension,
Expand All @@ -423,8 +529,6 @@ def apply(self, dimensions: Dict[str, Dimension]) -> None:
:class:`~pystac.Collection`, :class:`~pystac.Item` or :class:`~pystac.Asset`.

Args:
bands : A list of available bands where each item is a :class:`~Band`
object. If given, requires at least one band.
dimensions : Dictionary mapping dimension name to a :class:`Dimension`
object.
"""
Expand All @@ -442,6 +546,15 @@ def dimensions(self) -> Dict[str, Dimension]:
def dimensions(self, v: Dict[str, Dimension]) -> None:
self._set_property(DIMENSIONS_PROP, {k: dim.to_dict() for k, dim in v.items()})

@property
def variables(self) -> Optional[Dict[str, Variable]]:
"""Dictionary mapping variable name to a :class:`Variable` object."""
result = self._get_property(VARIABLES_PROP, Dict[str, Any])

if result is None:
return None
return {k: Variable.from_dict(v) for k, v in result.items()}

@classmethod
def get_schema_uri(cls) -> str:
return SCHEMA_URI
Expand Down
2 changes: 1 addition & 1 deletion tests/data-files/datacube/item.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"stac_version": "1.0.0-rc.1",
"stac_extensions": [
"https://stac-extensions.github.io/datacube/v1.0.0/schema.json"
"https://stac-extensions.github.io/datacube/v2.0.0/schema.json"
],
"id": "datacube-123",
"type": "Feature",
Expand Down