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

Item assets #1476

Merged
merged 14 commits into from
Jan 6, 2025
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@

## [Unreleased]


### Added

- Top-level `item_assets` dict on `Collection`s ([#1476](https://github.com/stac-utils/pystac/pull/1476))

### Changed

- Write STAC v1.1.0 ([#1427](https://github.com/stac-utils/pystac/pull/1427))
- Use [uv](https://github.com/astral-sh/uv) for development dependencies and docs ([#1439](https://github.com/stac-utils/pystac/pull/1439))
- Correctly detect absolute file path ref on windows, reflecting change in python 3.13 ([#1475](https://github.com/stac-utils/pystac/pull/14750)) (only effects python 3.13)
- Deprecated `ItemAssetExtension` ([#1476](https://github.com/stac-utils/pystac/pull/1476))

## [v1.11.0] - 2024-09-26

Expand Down
7 changes: 7 additions & 0 deletions docs/api/item_assets.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pystac.item_assets
==================

.. automodule:: pystac.item_assets
:members:
:undoc-members:
:noindex:
9 changes: 9 additions & 0 deletions docs/api/pystac.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pystac
Summaries
Item
Asset
ItemAssetDefinition
CommonMetadata
ItemCollection
Link
Expand Down Expand Up @@ -116,6 +117,14 @@ Asset
:members:
:undoc-members:

ItemAssetDefinition
-------------------

.. autoclass:: pystac.ItemAssetDefinition
:members:
:undoc-members:


CommonMetadata
--------------

Expand Down
2 changes: 2 additions & 0 deletions pystac/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"RangeSummary",
"Item",
"Asset",
"ItemAssetDefinition",
"ItemCollection",
"Provider",
"ProviderRole",
Expand Down Expand Up @@ -81,6 +82,7 @@
from pystac.summaries import RangeSummary, Summaries
from pystac.asset import Asset
from pystac.item import Item
from pystac.item_assets import ItemAssetDefinition
from pystac.item_collection import ItemCollection
from pystac.provider import ProviderRole, Provider
from pystac.utils import HREF
Expand Down
58 changes: 58 additions & 0 deletions pystac/collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from pystac.asset import Asset, Assets
from pystac.catalog import Catalog
from pystac.errors import DeprecatedWarning, ExtensionNotImplemented, STACTypeError
from pystac.item_assets import ItemAssetDefinition, _ItemAssets
from pystac.layout import HrefLayoutStrategy
from pystac.link import Link
from pystac.provider import Provider
Expand Down Expand Up @@ -553,6 +554,7 @@
self.keywords = keywords
self.providers = providers
self.summaries = summaries or Summaries.empty()
self._item_assets: _ItemAssets | None = None

self.assets = {}
if assets is not None:
Expand Down Expand Up @@ -731,6 +733,62 @@
return super().get_item(id, recursive=recursive)
raise e

@property
def item_assets(self) -> dict[str, ItemAssetDefinition]:
"""Accessor for `item_assets
<https://github.com/radiantearth/stac-spec/blob/v1.1.0/collection-spec/collection-spec.md#item_assets>`__
on this collection.

Example::

.. code-block:: python

>>> print(collection.item_assets)
{'thumbnail': <pystac.item_assets.ItemAssetDefinition at 0x72aea0420750>,
'metadata': <pystac.item_assets.ItemAssetDefinition at 0x72aea017dc90>,
'B5': <pystac.item_assets.ItemAssetDefinition at 0x72aea017efd0>,
'B6': <pystac.item_assets.ItemAssetDefinition at 0x72aea016d5d0>,
'B7': <pystac.item_assets.ItemAssetDefinition at 0x72aea016e050>,
'B8': <pystac.item_assets.ItemAssetDefinition at 0x72aea016da90>}
>>> collection.item_assets["thumbnail"].title
'Thumbnail'

Set attributes on :class:`~pystac.ItemAssetDefinition` objects

.. code-block:: python

>>> collection.item_assets["thumbnail"].title = "New Title"

Add to the ``item_assets`` dict:

.. code-block:: python

>>> collection.item_assets["B4"] = {
'type': 'image/tiff; application=geotiff; profile=cloud-optimized',
'eo:bands': [{'name': 'B4', 'common_name': 'red'}]
}
>>> collection.item_assets["B4"].owner == collection
True
"""
if self._item_assets is None:
self._item_assets = _ItemAssets(self)
return self._item_assets

@item_assets.setter
def item_assets(
self, item_assets: dict[str, ItemAssetDefinition | dict[str, Any]] | None
) -> None:
# clear out the cached value
self._item_assets = None

if item_assets is None:
self.extra_fields.pop("item_assets")

Check warning on line 785 in pystac/collection.py

View check run for this annotation

Codecov / codecov/patch

pystac/collection.py#L785

Added line #L785 was not covered by tests
else:
self.extra_fields["item_assets"] = {
k: v if isinstance(v, dict) else v.to_dict()
for k, v in item_assets.items()
}

def update_extent_from_items(self) -> None:
"""
Update datetime and bbox based on all items to a single bbox and time window.
Expand Down
8 changes: 2 additions & 6 deletions pystac/extensions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from abc import ABC, abstractmethod
from collections.abc import Iterable
from typing import (
TYPE_CHECKING,
Any,
Generic,
TypeVar,
Expand All @@ -14,9 +13,6 @@

import pystac

if TYPE_CHECKING:
from pystac.extensions.item_assets import AssetDefinition

VERSION_REGEX = re.compile("/v[0-9].[0-9].*/")


Expand Down Expand Up @@ -158,7 +154,7 @@ def has_extension(cls, obj: S) -> bool:
@classmethod
def validate_owner_has_extension(
cls,
asset: pystac.Asset | AssetDefinition,
asset: pystac.Asset | pystac.ItemAssetDefinition,
add_if_missing: bool = False,
) -> None:
"""
Expand Down Expand Up @@ -190,7 +186,7 @@ def validate_owner_has_extension(
@classmethod
def ensure_owner_has_extension(
cls,
asset_or_link: pystac.Asset | AssetDefinition | pystac.Link,
asset_or_link: pystac.Asset | pystac.ItemAssetDefinition | pystac.Link,
add_if_missing: bool = False,
) -> None:
"""Given an :class:`~pystac.Asset`, checks if the asset's owner has this
Expand Down
19 changes: 10 additions & 9 deletions pystac/extensions/classification.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
)

import pystac
from pystac.extensions import item_assets
from pystac.extensions.base import (
ExtensionManagementMixin,
PropertiesExtension,
Expand All @@ -27,7 +26,7 @@
from pystac.serialization.identify import STACJSONDescription, STACVersionID
from pystac.utils import get_required, map_opt

T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition, RasterBand)
T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition, RasterBand)

SCHEMA_URI_PATTERN: str = (
"https://stac-extensions.github.io/classification/v{version}/schema.json"
Expand Down Expand Up @@ -492,7 +491,7 @@ class ClassificationExtension(
"""An abstract class that can be used to extend the properties of
:class:`~pystac.Item`, :class:`~pystac.Asset`,
:class:`~pystac.extension.raster.RasterBand`, or
:class:`~pystac.extension.item_assets.AssetDefinition` with properties from the
:class:`~pystac.ItemAssetDefinition` with properties from the
:stac-ext:`Classification Extension <classification>`. This class is generic
over the type of STAC object being extended.

Expand Down Expand Up @@ -602,7 +601,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> ClassificationExtension[T]

This extension can be applied to instances of :class:`~pystac.Item`,
:class:`~pystac.Asset`,
:class:`~pystac.extensions.item_assets.AssetDefinition`, or
:class:`~pystac.ItemAssetDefinition`, or
:class:`~pystac.extension.raster.RasterBand`.

Raises:
Expand All @@ -614,7 +613,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> ClassificationExtension[T]
elif isinstance(obj, pystac.Asset):
cls.ensure_owner_has_extension(obj, add_if_missing)
return cast(ClassificationExtension[T], AssetClassificationExtension(obj))
elif isinstance(obj, item_assets.AssetDefinition):
elif isinstance(obj, pystac.ItemAssetDefinition):
cls.ensure_owner_has_extension(obj, add_if_missing)
return cast(
ClassificationExtension[T], ItemAssetsClassificationExtension(obj)
Expand Down Expand Up @@ -665,17 +664,19 @@ def __repr__(self) -> str:


class ItemAssetsClassificationExtension(
ClassificationExtension[item_assets.AssetDefinition]
ClassificationExtension[pystac.ItemAssetDefinition]
):
properties: dict[str, Any]
asset_defn: item_assets.AssetDefinition
asset_defn: pystac.ItemAssetDefinition

def __init__(self, item_asset: item_assets.AssetDefinition):
def __init__(self, item_asset: pystac.ItemAssetDefinition):
self.asset_defn = item_asset
self.properties = item_asset.properties

def __repr__(self) -> str:
return f"<ItemAssetsClassificationExtension AssetDefinition={self.asset_defn}"
return (
f"<ItemAssetsClassificationExtension ItemAssetDefinition={self.asset_defn}"
)


class RasterBandClassificationExtension(ClassificationExtension[RasterBand]):
Expand Down
11 changes: 5 additions & 6 deletions pystac/extensions/datacube.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@
from typing import Any, Generic, Literal, TypeVar, Union, cast

import pystac
from pystac.extensions import item_assets
from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
from pystac.extensions.hooks import ExtensionHooks
from pystac.utils import StringEnum, get_required, map_opt

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

SCHEMA_URI = "https://stac-extensions.github.io/datacube/v2.2.0/schema.json"
Expand Down Expand Up @@ -619,7 +618,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> DatacubeExtension[T]:
elif isinstance(obj, pystac.Asset):
cls.ensure_owner_has_extension(obj, add_if_missing)
return cast(DatacubeExtension[T], AssetDatacubeExtension(obj))
elif isinstance(obj, item_assets.AssetDefinition):
elif isinstance(obj, pystac.ItemAssetDefinition):
cls.ensure_owner_has_extension(obj, add_if_missing)
return cast(DatacubeExtension[T], ItemAssetsDatacubeExtension(obj))
else:
Expand Down Expand Up @@ -691,11 +690,11 @@ def __repr__(self) -> str:
return f"<AssetDatacubeExtension Item id={self.asset_href}>"


class ItemAssetsDatacubeExtension(DatacubeExtension[item_assets.AssetDefinition]):
class ItemAssetsDatacubeExtension(DatacubeExtension[pystac.ItemAssetDefinition]):
properties: dict[str, Any]
asset_defn: item_assets.AssetDefinition
asset_defn: pystac.ItemAssetDefinition

def __init__(self, item_asset: item_assets.AssetDefinition):
def __init__(self, item_asset: pystac.ItemAssetDefinition):
self.asset_defn = item_asset
self.properties = item_asset.properties

Expand Down
12 changes: 6 additions & 6 deletions pystac/extensions/eo.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
)

import pystac
from pystac.extensions import item_assets, projection, view
from pystac.extensions import projection, view
from pystac.extensions.base import (
ExtensionManagementMixin,
PropertiesExtension,
Expand All @@ -25,7 +25,7 @@
from pystac.summaries import RangeSummary
from pystac.utils import get_required, map_opt

T = TypeVar("T", pystac.Item, pystac.Asset, item_assets.AssetDefinition)
T = TypeVar("T", pystac.Item, pystac.Asset, pystac.ItemAssetDefinition)

SCHEMA_URI: str = "https://stac-extensions.github.io/eo/v1.1.0/schema.json"
SCHEMA_URIS: list[str] = [
Expand Down Expand Up @@ -409,7 +409,7 @@ def ext(cls, obj: T, add_if_missing: bool = False) -> EOExtension[T]:
elif isinstance(obj, pystac.Asset):
cls.ensure_owner_has_extension(obj, add_if_missing)
return cast(EOExtension[T], AssetEOExtension(obj))
elif isinstance(obj, item_assets.AssetDefinition):
elif isinstance(obj, pystac.ItemAssetDefinition):
cls.ensure_owner_has_extension(obj, add_if_missing)
return cast(EOExtension[T], ItemAssetsEOExtension(obj))
else:
Expand Down Expand Up @@ -536,9 +536,9 @@ def __repr__(self) -> str:
return f"<AssetEOExtension Asset href={self.asset_href}>"


class ItemAssetsEOExtension(EOExtension[item_assets.AssetDefinition]):
class ItemAssetsEOExtension(EOExtension[pystac.ItemAssetDefinition]):
properties: dict[str, Any]
asset_defn: item_assets.AssetDefinition
asset_defn: pystac.ItemAssetDefinition

def _get_bands(self) -> list[Band] | None:
if BANDS_PROP not in self.properties:
Expand All @@ -550,7 +550,7 @@ def _get_bands(self) -> list[Band] | None:
)
)

def __init__(self, item_asset: item_assets.AssetDefinition):
def __init__(self, item_asset: pystac.ItemAssetDefinition):
self.asset_defn = item_asset
self.properties = item_asset.properties

Expand Down
22 changes: 15 additions & 7 deletions pystac/extensions/ext.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
from dataclasses import dataclass
from typing import Any, Generic, Literal, TypeVar, cast

from pystac import Asset, Catalog, Collection, Item, Link, STACError
from pystac import (
Asset,
Catalog,
Collection,
Item,
ItemAssetDefinition,
Link,
STACError,
)
from pystac.extensions.classification import ClassificationExtension
from pystac.extensions.datacube import DatacubeExtension
from pystac.extensions.eo import EOExtension
from pystac.extensions.file import FileExtension
from pystac.extensions.grid import GridExtension
from pystac.extensions.item_assets import AssetDefinition, ItemAssetsExtension
from pystac.extensions.item_assets import ItemAssetsExtension
from pystac.extensions.mgrs import MgrsExtension
from pystac.extensions.pointcloud import PointcloudExtension
from pystac.extensions.projection import ProjectionExtension
Expand All @@ -22,8 +30,8 @@
from pystac.extensions.view import ViewExtension
from pystac.extensions.xarray_assets import XarrayAssetsExtension

T = TypeVar("T", Asset, AssetDefinition, Link)
U = TypeVar("U", Asset, AssetDefinition)
T = TypeVar("T", Asset, ItemAssetDefinition, Link)
U = TypeVar("U", Asset, ItemAssetDefinition)

EXTENSION_NAMES = Literal[
"classification",
Expand Down Expand Up @@ -107,7 +115,7 @@ def cube(self) -> DatacubeExtension[Collection]:
return DatacubeExtension.ext(self.stac_object)

@property
def item_assets(self) -> dict[str, AssetDefinition]:
def item_assets(self) -> dict[str, ItemAssetDefinition]:
return ItemAssetsExtension.ext(self.stac_object).item_assets

@property
Expand Down Expand Up @@ -300,8 +308,8 @@ def xarray(self) -> XarrayAssetsExtension[Asset]:


@dataclass
class ItemAssetExt(_AssetExt[AssetDefinition]):
stac_object: AssetDefinition
class ItemAssetExt(_AssetExt[ItemAssetDefinition]):
stac_object: ItemAssetDefinition


@dataclass
Expand Down
Loading
Loading