From 80e6965516cdf160349554dc2d3586ba548f85b8 Mon Sep 17 00:00:00 2001 From: Thomas Li Fredriksen Date: Thu, 28 Oct 2021 09:17:39 +0200 Subject: [PATCH 1/6] Extension/table - created extension --- pystac/extensions/table.py | 303 +++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 pystac/extensions/table.py diff --git a/pystac/extensions/table.py b/pystac/extensions/table.py new file mode 100644 index 000000000..8c48f0ea1 --- /dev/null +++ b/pystac/extensions/table.py @@ -0,0 +1,303 @@ +"""Implements the Table extension + +https://github.com/stac-extensions/table +""" +from copy import deepcopy +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.0.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 + `__ 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 deepcopy(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), "table:table", 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 + `__ 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 deepcopy(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 `. 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 `. + + 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
`. + + 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 "".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
`. + + 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 "".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
`. + + 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 "".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() From 501107001bb50565764cf213b674fb5ee42b9483 Mon Sep 17 00:00:00 2001 From: Thomas Li Fredriksen Date: Thu, 28 Oct 2021 11:07:43 +0200 Subject: [PATCH 2/6] Extension/table - Added extension hook. Added unittests --- pystac/__init__.py | 2 + pystac/extensions/table.py | 2 +- tests/data-files/table/README.md | 5 ++ tests/data-files/table/collection-2.json | 34 +++++++++ tests/data-files/table/collection.json | 76 ++++++++++++++++++++ tests/data-files/table/item.json | 74 +++++++++++++++++++ tests/data-files/table/table-collection.json | 70 ++++++++++++++++++ tests/extensions/test_table.py | 61 ++++++++++++++++ 8 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 tests/data-files/table/README.md create mode 100644 tests/data-files/table/collection-2.json create mode 100644 tests/data-files/table/collection.json create mode 100644 tests/data-files/table/item.json create mode 100644 tests/data-files/table/table-collection.json create mode 100644 tests/extensions/test_table.py diff --git a/pystac/__init__.py b/pystac/__init__.py index d3546854c..b20c047ce 100644 --- a/pystac/__init__.py +++ b/pystac/__init__.py @@ -87,6 +87,7 @@ import pystac.extensions.sar import pystac.extensions.sat import pystac.extensions.scientific +import pystac.extensions.table import pystac.extensions.timestamps import pystac.extensions.version import pystac.extensions.view @@ -103,6 +104,7 @@ pystac.extensions.sar.SAR_EXTENSION_HOOKS, pystac.extensions.sat.SAT_EXTENSION_HOOKS, pystac.extensions.scientific.SCIENTIFIC_EXTENSION_HOOKS, + pystac.extensions.table.TABLE_EXTENSION_HOOKS, pystac.extensions.timestamps.TIMESTAMPS_EXTENSION_HOOKS, pystac.extensions.version.VERSION_EXTENSION_HOOKS, pystac.extensions.view.VIEW_EXTENSION_HOOKS, diff --git a/pystac/extensions/table.py b/pystac/extensions/table.py index 8c48f0ea1..30ae4d176 100644 --- a/pystac/extensions/table.py +++ b/pystac/extensions/table.py @@ -15,7 +15,7 @@ T = TypeVar("T", pystac.Collection, pystac.Item, pystac.Asset) -SCHEMA_URI = "https://stac-extensions.github.io/table/v1.0.0/schema.json" +SCHEMA_URI = "https://stac-extensions.github.io/table/v1.2.0/schema.json" PREFIX: str = "table:" COLUMNS_PROP = PREFIX + "columns" diff --git a/tests/data-files/table/README.md b/tests/data-files/table/README.md new file mode 100644 index 000000000..974256a97 --- /dev/null +++ b/tests/data-files/table/README.md @@ -0,0 +1,5 @@ +# Source information + +Example files yanged from the Table extension STAC specification: + +https://github.com/stac-extensions/table/tree/f962838a51c0ce78c9aa180b7973e538e6ef900e/examples diff --git a/tests/data-files/table/collection-2.json b/tests/data-files/table/collection-2.json new file mode 100644 index 000000000..64e2b7e71 --- /dev/null +++ b/tests/data-files/table/collection-2.json @@ -0,0 +1,34 @@ +{ + "type": "Collection", + "id": "id", + "stac_version": "1.0.0", + "description": "desc", + "links": [], + "stac_extensions": [ + "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json", + "https://stac-extensions.github.io/table/v1.2.0/schema.json" + ], + "extent": { + "spatial": { + "bbox": [ + [ + 1, + 2, + 3, + 4 + ] + ] + }, + "temporal": { + "interval": [ + [ + null, + null + ] + ] + } + }, + "license": "proprietary", + "item_assets": {}, + "table:columns": [] +} \ No newline at end of file diff --git a/tests/data-files/table/collection.json b/tests/data-files/table/collection.json new file mode 100644 index 000000000..91551b68f --- /dev/null +++ b/tests/data-files/table/collection.json @@ -0,0 +1,76 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json", + "https://stac-extensions.github.io/table/v1.2.0/schema.json" + ], + "type": "Collection", + "id": "collection", + "title": "A title", + "description": "A description", + "license": "Apache-2.0", + "extent": { + "spatial": { + "bbox": [ + [ + 172.9, + 1.3, + 173, + 1.4 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2015-06-23T00:00:00Z", + null + ] + ] + } + }, + "assets": { + "example": { + "href": "https://example.com/examples/file.xyz" + } + }, + "item_assets": { + "data": { + "roles": [ + "data" + ], + "description": "description" + } + }, + "summaries": { + "datetime": { + "minimum": "2015-06-23T00:00:00Z", + "maximum": "2019-07-10T13:44:56Z" + } + }, + "table:columns": [ + { + "name": "geometry", + "description": "The observation location.", + "type": "int64" + }, + { + "name": "id", + "description": "The numerical identifier" + }, + { + "name": "value" + } + ], + "table:primary_geometry": "geometry", + "links": [ + { + "href": "https://example.com/examples/collection.json", + "rel": "self" + }, + { + "href": "https://example.com/examples/item.json", + "rel": "item" + } + ] +} diff --git a/tests/data-files/table/item.json b/tests/data-files/table/item.json new file mode 100644 index 000000000..1ce67588c --- /dev/null +++ b/tests/data-files/table/item.json @@ -0,0 +1,74 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/table/v1.2.0/schema.json" + ], + "type": "Feature", + "id": "item", + "bbox": [ + 172.9, + 1.3, + 173, + 1.4 + ], + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 172.9, + 1.3 + ], + [ + 173, + 1.3 + ], + [ + 173, + 1.4 + ], + [ + 172.9, + 1.4 + ], + [ + 172.9, + 1.3 + ] + ] + ] + }, + "properties": { + "datetime": "2020-12-11T22:38:32Z", + "table:columns": [ + { + "name": "geometry", + "description": "The observation location.", + "type": "byte_array" + }, + { + "name": "id", + "description": "The numerical identifier", + "type": "int64" + }, + { + "name": "value", + "description": "The observed value", + "type": "float64" + } + ], + "table:primary_geometry": "geometry", + "table:row_count": 100 + }, + "links": [ + { + "href": "https://example.com/examples/item.json", + "rel": "self" + } + ], + "assets": { + "data": { + "href": "https://example.com/examples/file.xyz" + } + } +} \ No newline at end of file diff --git a/tests/data-files/table/table-collection.json b/tests/data-files/table/table-collection.json new file mode 100644 index 000000000..d024bafa4 --- /dev/null +++ b/tests/data-files/table/table-collection.json @@ -0,0 +1,70 @@ +{ + "stac_version": "1.0.0", + "stac_extensions": [ + "https://stac-extensions.github.io/item-assets/v1.0.0/schema.json", + "https://stac-extensions.github.io/table/v1.2.0/schema.json" + ], + "type": "Collection", + "id": "collection", + "title": "A title", + "description": "A description", + "license": "Apache-2.0", + "extent": { + "spatial": { + "bbox": [ + [ + 172.9, + 1.3, + 173, + 1.4 + ] + ] + }, + "temporal": { + "interval": [ + [ + "2015-06-23T00:00:00Z", + null + ] + ] + } + }, + "assets": { + "example": { + "href": "https://example.com/examples/file.xyz" + } + }, + "item_assets": { + "data": { + "roles": [ + "data" + ], + "description": "description" + } + }, + "summaries": { + "datetime": { + "minimum": "2015-06-23T00:00:00Z", + "maximum": "2019-07-10T13:44:56Z" + } + }, + "table:tables": [ + { + "name": "first table", + "description": "This is my first table" + }, + { + "name": "Second table" + } + ], + "links": [ + { + "href": "https://example.com/examples/collection.json", + "rel": "self" + }, + { + "href": "https://example.com/examples/item.json", + "rel": "item" + } + ] +} diff --git a/tests/extensions/test_table.py b/tests/extensions/test_table.py new file mode 100644 index 000000000..56c8696ab --- /dev/null +++ b/tests/extensions/test_table.py @@ -0,0 +1,61 @@ +import unittest +import pystac +from pystac import ExtensionTypeError +from pystac.extensions.table import TableExtension + +from tests.utils import TestCases + + +class TableTest(unittest.TestCase): + def setUp(self) -> None: + self.example_uri = TestCases.get_path("data-files/table/item.json") + + def test_validate(self) -> None: + item = pystac.Item.from_file(self.example_uri) + item.validate() + + def test_extension_not_implemented(self) -> None: + # Should raise exception if item does not include extension URI + item = pystac.Item.from_file(self.example_uri) + item.stac_extensions.remove(TableExtension.get_schema_uri()) + + with self.assertRaises(pystac.ExtensionNotImplemented): + _ = TableExtension.ext(item) + + # Should raise exception if owning item does not include extension URI + asset = item.assets["data"] + + with self.assertRaises(pystac.ExtensionNotImplemented): + _ = TableExtension.ext(asset) + + # Should succeed if Asset has no owner + ownerless_asset = pystac.Asset.from_dict(asset.to_dict()) + _ = TableExtension.ext(ownerless_asset) + + def test_item_ext_add_to(self) -> None: + item = pystac.Item.from_file(self.example_uri) + item.stac_extensions.remove(TableExtension.get_schema_uri()) + + _ = TableExtension.ext(item, add_if_missing=True) + + self.assertIn(TableExtension.get_schema_uri(), item.stac_extensions) + + def test_asset_ext_add_to(self) -> None: + item = pystac.Item.from_file(self.example_uri) + item.stac_extensions.remove(TableExtension.get_schema_uri()) + + self.assertNotIn(TableExtension.get_schema_uri(), item.stac_extensions) + asset = item.assets["data"] + + _ = TableExtension.ext(asset, add_if_missing=True) + self.assertIn(TableExtension.get_schema_uri(), item.stac_extensions) + + def test_should_raise_exception_when_passing_invalid_extension_object( + self, + ) -> None: + self.assertRaisesRegex( + ExtensionTypeError, + r"^Table extension does not apply to type 'object'$", + TableExtension.ext, + object(), + ) From bdbe34dd802257c006c35091e581aab94262dba7 Mon Sep 17 00:00:00 2001 From: Thomas Li Fredriksen Date: Fri, 29 Oct 2021 08:24:18 +0200 Subject: [PATCH 3/6] Extension/table - Removed deepcopy usage --- pystac/extensions/table.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pystac/extensions/table.py b/pystac/extensions/table.py index 30ae4d176..cc9e21110 100644 --- a/pystac/extensions/table.py +++ b/pystac/extensions/table.py @@ -2,7 +2,6 @@ https://github.com/stac-extensions/table """ -from copy import deepcopy from typing import Any, Dict, Generic, List, Optional, TypeVar, Union, cast import pystac @@ -81,7 +80,7 @@ def col_type(self, v: Optional[str]) -> None: def to_dict(self) -> Dict[str, Any]: """Returns a JSON-like dictionary representing this ``Column``.""" - return deepcopy(self.properties) + return self.properties class Table: @@ -118,7 +117,7 @@ def description(self, v: Optional[str]) -> None: def to_dict(self) -> Dict[str, Any]: """Returns a JSON-like dictionary representing this ``Table``.""" - return deepcopy(self.properties) + return self.properties class TableExtension( From 3986c49e9d0db25cc870629558b2a6dc8ec1502f Mon Sep 17 00:00:00 2001 From: Thomas Li Fredriksen Date: Fri, 29 Oct 2021 08:28:02 +0200 Subject: [PATCH 4/6] Extension/table - Implemented changes proposed in PR #646 --- pystac/extensions/table.py | 4 +--- tests/data-files/table/README.md | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pystac/extensions/table.py b/pystac/extensions/table.py index cc9e21110..44b38ba2b 100644 --- a/pystac/extensions/table.py +++ b/pystac/extensions/table.py @@ -94,9 +94,7 @@ def __init__(self, properties: Dict[str, Any]): @property def name(self) -> str: """The table name""" - return get_required( - self.properties.get(TBL_NAME_PROP), "table:table", TBL_NAME_PROP - ) + return get_required(self.properties.get(TBL_NAME_PROP), self, TBL_NAME_PROP) @name.setter def name(self, v: str) -> None: diff --git a/tests/data-files/table/README.md b/tests/data-files/table/README.md index 974256a97..d7ff8442c 100644 --- a/tests/data-files/table/README.md +++ b/tests/data-files/table/README.md @@ -1,5 +1,5 @@ # Source information -Example files yanged from the Table extension STAC specification: +Example files yanked from the Table extension STAC specification: https://github.com/stac-extensions/table/tree/f962838a51c0ce78c9aa180b7973e538e6ef900e/examples From 57803df101bc20791bb085e8951a6ef74f8f53ea Mon Sep 17 00:00:00 2001 From: Thomas Li Fredriksen Date: Fri, 29 Oct 2021 08:34:34 +0200 Subject: [PATCH 5/6] Extension/table - Updated docs for PR #646 --- docs/api.rst | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index 639c3350d..fb982fa69 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -809,6 +809,39 @@ ItemScientificExtension :members: :show-inheritance: +Table Extension +--------------- + +These classes are representations of the :stac-ext:`Table Extension Spec `. + +TableExtension +~~~~~~~~~~~~~~ + +.. autoclass:: pystac.extensions.table.TableExtension + :members: + :show-inheritance: + +CollectionTableExtension +~~~~~~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: pystac.extensions.table.CollectionTableExtension + :members: + :show-inheritance: + +ItemTableExtension +~~~~~~~~~~~~~~~~~~ + +.. autoclass:: pystac.extensions.table.ItemTableExtension + :members: + :show-inheritance: + +AssetTableExtension +~~~~~~~~~~~~~~~~~~~ + +.. autoclass:: pystac.extensions.table.AssetTableExtension + :members: + :show-inheritance: + Timestamps Extension -------------------- From cdb3199a69d8e12a9e0a42a3ff44a3c5846330b3 Mon Sep 17 00:00:00 2001 From: Thomas Li Fredriksen Date: Fri, 29 Oct 2021 08:36:26 +0200 Subject: [PATCH 6/6] Extension/table - Updated changelog for PR #646 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4103089aa..9f96a34ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added +- Added Table-extension ([#646](https://github.com/stac-utils/pystac/pull/646)) + ### Removed - Exclude `tests` from package distribution. This should make the package lighter ([#604](https://github.com/stac-utils/pystac/pull/604))