-
Notifications
You must be signed in to change notification settings - Fork 128
Extension/Table - Added extension #646
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
Merged
duckontheweb
merged 6 commits into
stac-utils:main
from
C4IROcean:feature/extension-table
Nov 8, 2021
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
80e6965
Extension/table - created extension
thomafred 5011070
Extension/table - Added extension hook. Added unittests
thomafred bdbe34d
Extension/table - Removed deepcopy usage
thomafred 3986c49
Extension/table - Implemented changes proposed in PR #646
thomafred 57803df
Extension/table - Updated docs for PR #646
thomafred cdb3199
Extension/table - Updated changelog for PR #646
thomafred File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,300 @@ | ||
| """Implements the Table extension | ||
|
|
||
| https://github.com/stac-extensions/table | ||
| """ | ||
| from typing import Any, Dict, Generic, List, Optional, TypeVar, Union, cast | ||
|
|
||
| import pystac | ||
| from pystac.extensions.base import ( | ||
| ExtensionManagementMixin, | ||
| PropertiesExtension, | ||
| ) | ||
| from pystac.extensions.hooks import ExtensionHooks | ||
| from pystac.utils import get_required | ||
|
|
||
| T = TypeVar("T", pystac.Collection, pystac.Item, pystac.Asset) | ||
|
|
||
| SCHEMA_URI = "https://stac-extensions.github.io/table/v1.2.0/schema.json" | ||
|
|
||
| PREFIX: str = "table:" | ||
| COLUMNS_PROP = PREFIX + "columns" | ||
| PRIMARY_GEOMETRY_PROP = PREFIX + "primary_geometry" | ||
| ROW_COUNT_PROP = PREFIX + "row_count" | ||
| STORAGE_OPTIONS_PROP = PREFIX + "storage_options" | ||
| TABLES_PROP = PREFIX + "tables" | ||
|
|
||
| # Column properties | ||
| COL_NAME_PROP = "name" | ||
| COL_DESCRIPTION_PROP = "description" | ||
| COL_TYPE_PROP = "type" | ||
|
|
||
| # Table properties | ||
| TBL_NAME_PROP = "name" | ||
| TBL_DESCRIPTION_PROP = "description" | ||
|
|
||
|
|
||
| class Column: | ||
| """Object representing a column of a table.""" | ||
|
|
||
| properties: Dict[str, Any] | ||
|
|
||
| def __init__(self, properties: Dict[str, Any]): | ||
| self.properties = properties | ||
|
|
||
| @property | ||
| def name(self) -> str: | ||
| """The column name""" | ||
| return get_required( | ||
| self.properties.get(COL_NAME_PROP), "table:column", COL_NAME_PROP | ||
| ) | ||
|
|
||
| @name.setter | ||
| def name(self, v: str) -> None: | ||
| self.properties[COL_NAME_PROP] = v | ||
|
|
||
| @property | ||
| def description(self) -> Optional[str]: | ||
| """Detailed multi-line description to explain the column. `CommonMark 0.29 | ||
| <http://commonmark.org/>`__ syntax MAY be used for rich text representation.""" | ||
| return self.properties.get(COL_DESCRIPTION_PROP) | ||
|
|
||
| @description.setter | ||
| def description(self, v: Optional[str]) -> None: | ||
| if v is None: | ||
| self.properties.pop(COL_DESCRIPTION_PROP, None) | ||
| else: | ||
| self.properties[COL_DESCRIPTION_PROP] = v | ||
|
|
||
| @property | ||
| def col_type(self) -> Optional[str]: | ||
| """Data type of the column. If using a file format with a type system (like Parquet), | ||
| we recommend you use those types""" | ||
| return self.properties.get(COL_TYPE_PROP) | ||
|
|
||
| @col_type.setter | ||
| def col_type(self, v: Optional[str]) -> None: | ||
| if v is None: | ||
| self.properties.pop(COL_TYPE_PROP, None) | ||
| else: | ||
| self.properties[COL_TYPE_PROP] = v | ||
|
|
||
| def to_dict(self) -> Dict[str, Any]: | ||
| """Returns a JSON-like dictionary representing this ``Column``.""" | ||
| return self.properties | ||
|
|
||
|
|
||
| class Table: | ||
| """Object containing a high-level summary about a table""" | ||
|
|
||
| properties: Dict[str, Any] | ||
|
|
||
| def __init__(self, properties: Dict[str, Any]): | ||
| self.properties = properties | ||
|
|
||
| @property | ||
| def name(self) -> str: | ||
| """The table name""" | ||
| return get_required(self.properties.get(TBL_NAME_PROP), self, TBL_NAME_PROP) | ||
|
|
||
| @name.setter | ||
| def name(self, v: str) -> None: | ||
| self.properties[COL_NAME_PROP] = v | ||
|
|
||
| @property | ||
| def description(self) -> Optional[str]: | ||
| """Detailed multi-line description to explain the table. `CommonMark 0.29 | ||
| <http://commonmark.org/>`__ syntax MAY be used for rich text representation.""" | ||
| return self.properties.get(COL_DESCRIPTION_PROP) | ||
|
|
||
| @description.setter | ||
| def description(self, v: Optional[str]) -> None: | ||
| if v is None: | ||
| self.properties.pop(COL_DESCRIPTION_PROP, None) | ||
| else: | ||
| self.properties[COL_DESCRIPTION_PROP] = v | ||
|
|
||
| def to_dict(self) -> Dict[str, Any]: | ||
| """Returns a JSON-like dictionary representing this ``Table``.""" | ||
| return self.properties | ||
|
|
||
|
|
||
| class TableExtension( | ||
| Generic[T], | ||
| PropertiesExtension, | ||
| ExtensionManagementMixin[Union[pystac.Collection, pystac.Item]], | ||
| ): | ||
| """An abstract class that can be used to extend the properties of a | ||
| :class:`~pystac.Collection`, :class:`~pystac.Item`, or :class:`~pystac.Asset` with | ||
| properties from the :stac-ext:`Datacube Extension <datacube>`. This class is | ||
| generic over the type of STAC Object to be extended (e.g. :class:`~pystac.Item`, | ||
| :class:`~pystac.Asset`). | ||
|
|
||
| To create a concrete instance of :class:`TableExtension`, use the | ||
| :meth:`TableExtension.ext` method. For example: | ||
|
|
||
| .. code-block:: python | ||
|
|
||
| >>> item: pystac.Item = ... | ||
| >>> tbl_ext = TableExtension.ext(item) | ||
|
|
||
| """ | ||
|
|
||
| @classmethod | ||
| def get_schema_uri(cls) -> str: | ||
| return SCHEMA_URI | ||
|
|
||
| @classmethod | ||
| def ext(cls, obj: T, add_if_missing: bool = False) -> "TableExtension[T]": | ||
| """Extend the given STAC Object with properties from the | ||
| :stac-ext:`Table Extension <table>`. | ||
|
|
||
| This extension can be applied to instances of :class:`~pystac.Collection`, | ||
| :class:`~pystac.Item` or :class:`~pystac.Asset`. | ||
|
|
||
| Raises: | ||
| pystac.ExtensionTypeError : If an invalid object type is passed. | ||
| """ | ||
| if isinstance(obj, pystac.Collection): | ||
| cls.validate_has_extension(obj, add_if_missing) | ||
| return cast(TableExtension[T], CollectionTableExtension(obj)) | ||
| if isinstance(obj, pystac.Item): | ||
| cls.validate_has_extension(obj, add_if_missing) | ||
| return cast(TableExtension[T], ItemTableExtension(obj)) | ||
| if isinstance(obj, pystac.Asset): | ||
| cls.validate_owner_has_extension(obj, add_if_missing) | ||
| return cast(TableExtension[T], AssetTableExtension(obj)) | ||
| else: | ||
| raise pystac.ExtensionTypeError( | ||
| f"Table extension does not apply to type '{type(obj).__name__}'" | ||
| ) | ||
|
|
||
| @property | ||
| def columns(self) -> Optional[List[Column]]: | ||
| """A list of :class:`Column` objects describing each column""" | ||
| return self.properties.get(COLUMNS_PROP) | ||
|
|
||
| @columns.setter | ||
| def columns(self, v: List[Column]) -> None: | ||
| if v is None: | ||
| self.properties.pop(COLUMNS_PROP, None) | ||
| else: | ||
| self.properties[COLUMNS_PROP] = v | ||
|
|
||
| @property | ||
| def primary_geometry(self) -> Optional[str]: | ||
| """The primary geometry column name""" | ||
| return self.properties.get(PRIMARY_GEOMETRY_PROP) | ||
|
|
||
| @primary_geometry.setter | ||
| def primary_geometry(self, v: Optional[str]) -> None: | ||
| if v is None: | ||
| self.properties.pop(PRIMARY_GEOMETRY_PROP, None) | ||
| else: | ||
| self.properties[PRIMARY_GEOMETRY_PROP] = v | ||
|
|
||
| @property | ||
| def row_count(self) -> Optional[int]: | ||
| """The number of rows in the dataset""" | ||
| return self.properties.get(ROW_COUNT_PROP) | ||
|
|
||
| @row_count.setter | ||
| def row_count(self, v: Optional[int]) -> None: | ||
| if v is None: | ||
| self.properties.pop(ROW_COUNT_PROP, None) | ||
| else: | ||
| self.properties[ROW_COUNT_PROP] = v | ||
|
|
||
|
|
||
| class CollectionTableExtension(TableExtension[pystac.Collection]): | ||
| """A concrete implementation of :class:`TableExtension` on a | ||
| :class:`~pystac.Collection` that extends the properties of the Item to include | ||
| properties defined in the :stac-ext:`Table Extension <table>`. | ||
|
|
||
| This class should generally not be instantiated directly. Instead, call | ||
| :meth:`TableExtension.ext` on an :class:`~pystac.Collection` to extend it. | ||
| """ | ||
|
|
||
| collection: pystac.Collection | ||
| properties: Dict[str, Any] | ||
|
|
||
| def __init__(self, collection: pystac.Collection): | ||
| self.collection = collection | ||
| self.properties = collection.extra_fields | ||
|
|
||
| @property | ||
| def tables(self) -> Dict[str, Table]: | ||
| """A mapping of table names to table objects""" | ||
| return get_required(self.properties.get(TABLES_PROP), self, TABLES_PROP) | ||
|
|
||
| @tables.setter | ||
| def tables(self, v: Dict[str, Table]) -> None: | ||
| self.properties[TABLES_PROP] = v | ||
|
|
||
| def __repr__(self) -> str: | ||
| return "<CollectionTableExtension Item id={}>".format(self.collection.id) | ||
|
|
||
|
|
||
| class ItemTableExtension(TableExtension[pystac.Item]): | ||
| """A concrete implementation of :class:`TableExtension` on an | ||
| :class:`~pystac.Item` that extends the properties of the Item to include properties | ||
| defined in the :stac-ext:`Table Extension <table>`. | ||
|
|
||
| This class should generally not be instantiated directly. Instead, call | ||
| :meth:`TableExtension.ext` on an :class:`~pystac.Item` to extend it. | ||
| """ | ||
|
|
||
| item: pystac.Item | ||
| properties: Dict[str, Any] | ||
|
|
||
| def __init__(self, item: pystac.Item): | ||
| self.item = item | ||
| self.properties = item.properties | ||
|
|
||
| def __repr__(self) -> str: | ||
| return "<ItemTableExtension Item id={}>".format(self.item.id) | ||
|
|
||
|
|
||
| class AssetTableExtension(TableExtension[pystac.Asset]): | ||
| """A concrete implementation of :class:`TableExtension` on an | ||
| :class:`~pystac.Asset` that extends the Asset fields to include properties defined | ||
| in the :stac-ext:`Table Extension <table>`. | ||
|
|
||
| This class should generally not be instantiated directly. Instead, call | ||
| :meth:`TableExtension.ext` on an :class:`~pystac.Asset` to extend it. | ||
| """ | ||
|
|
||
| asset_href: str | ||
| properties: Dict[str, Any] | ||
| additional_read_properties: Optional[List[Dict[str, Any]]] | ||
|
|
||
| def __init__(self, asset: pystac.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] | ||
| else: | ||
| self.additional_read_properties = None | ||
|
|
||
| @property | ||
| def storage_options(self) -> Optional[Dict[str, Any]]: | ||
| """Additional keywords for opening the dataset""" | ||
| return self.properties.get(STORAGE_OPTIONS_PROP) | ||
|
|
||
| @storage_options.setter | ||
| def storage_options(self, v: Optional[Dict[str, Any]]) -> Any: | ||
| if v is None: | ||
| self.properties.pop(STORAGE_OPTIONS_PROP, None) | ||
| else: | ||
| self.properties[STORAGE_OPTIONS_PROP] = v | ||
|
|
||
| def __repr__(self) -> str: | ||
| return "<AssetTableExtension Item id={}>".format(self.asset_href) | ||
|
|
||
|
|
||
| class TableExtensinoHooks(ExtensionHooks): | ||
| schema_uri: str = SCHEMA_URI | ||
| prev_extension_ids = {"table"} | ||
| stac_object_types = {pystac.STACObjectType.COLLECTION, pystac.STACObjectType.ITEM} | ||
|
|
||
|
|
||
| TABLE_EXTENSION_HOOKS: ExtensionHooks = TableExtensinoHooks() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Source information | ||
|
|
||
| Example files yanked from the Table extension STAC specification: | ||
|
|
||
| https://github.com/stac-extensions/table/tree/f962838a51c0ce78c9aa180b7973e538e6ef900e/examples |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.