diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e4babb6a..27fedbf79 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ ### Fixed +- "How to create STAC catalogs" tutorial ([#775](https://github.com/stac-utils/pystac/pull/775)) + ## [v1.4.0] ### Added diff --git a/docs/concepts.rst b/docs/concepts.rst index 8d2ac863d..bdf82024c 100644 --- a/docs/concepts.rst +++ b/docs/concepts.rst @@ -279,7 +279,7 @@ for reading from AWS's S3 cloud object storage using `boto3 from pystac.stac_io import DefaultStacIO, StacIO class CustomStacIO(DefaultStacIO): - def __init__(): + def __init__(self): self.s3 = boto3.resource("s3") def read_text( @@ -302,8 +302,7 @@ for reading from AWS's S3 cloud object storage using `boto3 if parsed.scheme == "s3": bucket = parsed.netloc key = parsed.path[1:] - s3 = boto3.resource("s3") - s3.Object(bucket, key).put(Body=txt, ContentEncoding="utf-8") + self.s3.Object(bucket, key).put(Body=txt, ContentEncoding="utf-8") else: super().write_text(dest, txt, *args, **kwargs) @@ -322,7 +321,7 @@ to take advantage of connection pooling using a `requests.Session from pystac.stac_io import DefaultStacIO, StacIO class ConnectionPoolingIO(DefaultStacIO): - def __init__(): + def __init__(self): self.session = requests.Session() def read_text( diff --git a/docs/tutorials/how-to-create-stac-catalogs.ipynb b/docs/tutorials/how-to-create-stac-catalogs.ipynb index 81d7433d3..3e8a6f8a3 100644 --- a/docs/tutorials/how-to-create-stac-catalogs.ipynb +++ b/docs/tutorials/how-to-create-stac-catalogs.ipynb @@ -12,7 +12,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This notebook runs through some of the basics of using PySTAC to create a static STAC. It was part of a 30 minute presentation at the [community STAC sprint](https://github.com/radiantearth/community-sprints/tree/master/11052019-arlignton-va) in Arlington, VA in November 2019." + "This notebook runs through some of the basics of using PySTAC to create a static STAC. It was part of a 30 minute presentation at the [community STAC sprint](https://github.com/radiantearth/community-sprints/tree/master/11052019-arlignton-va) in Arlington, VA in November 2019, updated to work with current PySTAC." ] }, { @@ -24,44 +24,38 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Requirement already satisfied: boto3 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (1.10.8)\n", - "Requirement already satisfied: botocore<1.14.0,>=1.13.8 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from boto3) (1.13.8)\n", - "Requirement already satisfied: s3transfer<0.3.0,>=0.2.0 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from boto3) (0.2.1)\n", - "Requirement already satisfied: jmespath<1.0.0,>=0.7.1 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from boto3) (0.9.4)\n", - "Requirement already satisfied: urllib3<1.26,>=1.20; python_version >= \"3.4\" in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from botocore<1.14.0,>=1.13.8->boto3) (1.25.6)\n", - "Requirement already satisfied: docutils<0.16,>=0.10 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from botocore<1.14.0,>=1.13.8->boto3) (0.15.2)\n", - "Requirement already satisfied: python-dateutil<3.0.0,>=2.1; python_version >= \"2.7\" in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from botocore<1.14.0,>=1.13.8->boto3) (2.8.1)\n", - "Requirement already satisfied: six>=1.5 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from python-dateutil<3.0.0,>=2.1; python_version >= \"2.7\"->botocore<1.14.0,>=1.13.8->boto3) (1.12.0)\n", - "\u001b[33mWARNING: You are using pip version 20.1.1; however, version 20.2 is available.\n", - "You should consider upgrading via the '/Users/rob/proj/stac/pystac/venv/bin/python -m pip install --upgrade pip' command.\u001b[0m\n", - "Requirement already satisfied: rasterio in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (1.1.0)\n", - "Requirement already satisfied: numpy in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from rasterio) (1.17.3)\n", - "Requirement already satisfied: snuggs>=1.4.1 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from rasterio) (1.4.7)\n", - "Requirement already satisfied: click<8,>=4.0 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from rasterio) (7.0)\n", - "Requirement already satisfied: click-plugins in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from rasterio) (1.1.1)\n", - "Requirement already satisfied: attrs in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from rasterio) (19.3.0)\n", - "Requirement already satisfied: cligj>=0.5 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from rasterio) (0.5.0)\n", - "Requirement already satisfied: affine in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from rasterio) (2.3.0)\n", - "Requirement already satisfied: pyparsing>=2.1.6 in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (from snuggs>=1.4.1->rasterio) (2.4.2)\n", - "\u001b[33mWARNING: You are using pip version 20.1.1; however, version 20.2 is available.\n", - "You should consider upgrading via the '/Users/rob/proj/stac/pystac/venv/bin/python -m pip install --upgrade pip' command.\u001b[0m\n", - "Requirement already satisfied: shapely in /Users/rob/proj/stac/pystac/venv/lib/python3.6/site-packages (1.6.4.post2)\n", - "\u001b[33mWARNING: You are using pip version 20.1.1; however, version 20.2 is available.\n", - "You should consider upgrading via the '/Users/rob/proj/stac/pystac/venv/bin/python -m pip install --upgrade pip' command.\u001b[0m\n" + "Requirement already satisfied: boto3 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (1.21.28)\n", + "Requirement already satisfied: rasterio in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (1.2.10)\n", + "Requirement already satisfied: shapely in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (1.8.1.post1)\n", + "Requirement already satisfied: pystac in /Users/gadomski/Code/pystac (1.3.0)\n", + "Requirement already satisfied: jmespath<2.0.0,>=0.7.1 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from boto3) (1.0.0)\n", + "Requirement already satisfied: botocore<1.25.0,>=1.24.28 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from boto3) (1.24.28)\n", + "Requirement already satisfied: s3transfer<0.6.0,>=0.5.0 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from boto3) (0.5.2)\n", + "Requirement already satisfied: certifi in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (2021.5.30)\n", + "Requirement already satisfied: numpy in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (1.22.3)\n", + "Requirement already satisfied: click-plugins in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (1.1.1)\n", + "Requirement already satisfied: click>=4.0 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (8.0.1)\n", + "Requirement already satisfied: snuggs>=1.4.1 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (1.4.7)\n", + "Requirement already satisfied: cligj>=0.5 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (0.7.2)\n", + "Requirement already satisfied: affine in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (2.3.1)\n", + "Requirement already satisfied: setuptools in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (56.0.0)\n", + "Requirement already satisfied: attrs in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from rasterio) (21.2.0)\n", + "Requirement already satisfied: python-dateutil>=2.7.0 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from pystac) (2.8.1)\n", + "Requirement already satisfied: urllib3<1.27,>=1.25.4 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from botocore<1.25.0,>=1.24.28->boto3) (1.26.5)\n", + "Requirement already satisfied: six>=1.5 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from python-dateutil>=2.7.0->pystac) (1.16.0)\n", + "Requirement already satisfied: pyparsing>=2.1.6 in /Users/gadomski/.virtualenvs/pystac/lib/python3.9/site-packages (from snuggs>=1.4.1->rasterio) (2.4.7)\n" ] } ], "source": [ - "!pip install boto3\n", - "!pip install rasterio\n", - "!pip install shapely" + "%pip install boto3 rasterio shapely pystac" ] }, { @@ -73,7 +67,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -96,7 +90,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -110,17 +104,17 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "('/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/image.tif',\n", - " )" + "('/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/image.tif',\n", + " )" ] }, - "execution_count": 4, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -141,7 +135,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -155,24 +149,16 @@ " as well as :class:`~pystac.Item` s.\n", "\n", " Args:\n", - " id (str): Identifier for the catalog. Must be unique within the STAC.\n", - " description (str): Detailed multi-line description to fully explain the catalog.\n", - " `CommonMark 0.28 syntax `_ MAY be used for rich text\n", - " representation.\n", - " title (str or None): Optional short descriptive one-line title for the catalog.\n", - " stac_extensions (List[str]): Optional list of extensions the Catalog implements.\n", - " href (str or None): Optional HREF for this catalog, which be set as the catalog's\n", - " self link's HREF.\n", - "\n", - " Attributes:\n", - " id (str): Identifier for the catalog.\n", - " description (str): Detailed multi-line description to fully explain the catalog.\n", - " title (str or None): Optional short descriptive one-line title for the catalog.\n", - " stac_extensions (List[str] or None): Optional list of extensions the Catalog implements.\n", - " extra_fields (dict or None): Extra fields that are part of the top-level JSON properties\n", - " of the Catalog.\n", - " links (List[Link]): A list of :class:`~pystac.Link` objects representing\n", - " all links associated with this Catalog.\n", + " id : Identifier for the catalog. Must be unique within the STAC.\n", + " description : Detailed multi-line description to fully explain the catalog.\n", + " `CommonMark 0.28 syntax `_ MAY be used for rich\n", + " text representation.\n", + " title : Optional short descriptive one-line title for the catalog.\n", + " stac_extensions : Optional list of extensions the Catalog implements.\n", + " href : Optional HREF for this catalog, which be set as the\n", + " catalog's self link's HREF.\n", + " catalog_type : Optional catalog type for this catalog. Must\n", + " be one of the values in :class:`~pystac.CatalogType`.\n", " \n" ] } @@ -190,7 +176,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -206,7 +192,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -232,7 +218,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -244,45 +230,24 @@ " satellite imagery, derived data, DEM's, etc.\n", "\n", " Args:\n", - " id (str): Provider identifier. Must be unique within the STAC.\n", - " geometry (dict): Defines the full footprint of the asset represented by this item,\n", - " formatted according to `RFC 7946, section 3.1 (GeoJSON)\n", - " `_.\n", - " bbox (List[float] or None): Bounding Box of the asset represented by this item using\n", - " either 2D or 3D geometries. The length of the array must be 2*n where n is the\n", - " number of dimensions. Could also be None in the case of a null geometry.\n", - " datetime (datetime or None): Datetime associated with this item. If None,\n", + " id : Provider identifier. Must be unique within the STAC.\n", + " geometry : Defines the full footprint of the asset represented by this\n", + " item, formatted according to\n", + " `RFC 7946, section 3.1 (GeoJSON) `_.\n", + " bbox : Bounding Box of the asset represented by this item\n", + " using either 2D or 3D geometries. The length of the array must be 2*n\n", + " where n is the number of dimensions. Could also be None in the case of a\n", + " null geometry.\n", + " datetime : Datetime associated with this item. If None,\n", " a start_datetime and end_datetime must be supplied in the properties.\n", - " properties (dict): A dictionary of additional metadata for the item.\n", - " stac_extensions (List[str]): Optional list of extensions the Item implements.\n", - " href (str or None): Optional HREF for this item, which be set as the item's\n", + " properties : A dictionary of additional metadata for the item.\n", + " stac_extensions : Optional list of extensions the Item implements.\n", + " href : Optional HREF for this item, which be set as the item's\n", " self link's HREF.\n", - " collection (Collection or str): The Collection or Collection ID that this item\n", + " collection : The Collection or Collection ID that this item\n", " belongs to.\n", - " extra_fields (dict or None): Extra fields that are part of the top-level JSON properties\n", - " of the Item.\n", - "\n", - " Attributes:\n", - " id (str): Provider identifier. Unique within the STAC.\n", - " geometry (dict): Defines the full footprint of the asset represented by this item,\n", - " formatted according to `RFC 7946, section 3.1 (GeoJSON)\n", - " `_.\n", - " bbox (List[float] or None): Bounding Box of the asset represented by this item using\n", - " either 2D or 3D geometries. The length of the array is 2*n where n is the\n", - " number of dimensions. Could also be None in the case of a null geometry.\n", - " datetime (datetime or None): Datetime associated with this item. If None,\n", - " the start_datetime and end_datetime in the common_metadata\n", - " will supply the datetime range of the Item.\n", - " properties (dict): A dictionary of additional metadata for the item.\n", - " stac_extensions (List[str] or None): Optional list of extensions the Item implements.\n", - " collection (Collection or None): Collection that this item is a part of.\n", - " links (List[Link]): A list of :class:`~pystac.Link` objects representing\n", - " all links associated with this STACObject.\n", - " assets (Dict[str, Asset]): Dictionary of asset objects that can be downloaded,\n", - " each with a unique key.\n", - " collection_id (str or None): The Collection ID that this item belongs to, if any.\n", - " extra_fields (dict or None): Extra fields that are part of the top-level JSON properties\n", - " of the Item.\n", + " extra_fields : Extra fields that are part of the top-level JSON\n", + " properties of the Item.\n", " \n" ] } @@ -300,7 +265,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -323,7 +288,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -350,7 +315,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -372,27 +337,16 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "item.get_parent() is None" + "assert item.get_parent() is None" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -401,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -410,7 +364,7 @@ "" ] }, - "execution_count": 14, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -428,7 +382,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -455,42 +409,30 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "An object that contains a link to data associated with the Item that can be\n", - " downloaded or streamed.\n", + "An object that contains a link to data associated with an Item or Collection that\n", + " can be downloaded or streamed.\n", "\n", " Args:\n", - " href (str): Link to the asset object. Relative and absolute links are both allowed.\n", - " title (str): Optional displayed title for clients and users.\n", - " description (str): A description of the Asset providing additional details, such as\n", - " how it was processed or created. CommonMark 0.29 syntax MAY be used for rich\n", - " text representation.\n", - " media_type (str): Optional description of the media type. Registered Media Types\n", - " are preferred. See :class:`~pystac.MediaType` for common media types.\n", - " roles ([str]): Optional, Semantic roles (i.e. thumbnail, overview, data, metadata)\n", - " of the asset.\n", - " properties (dict): Optional, additional properties for this asset. This is used by\n", - " extensions as a way to serialize and deserialize properties on asset\n", - " object JSON.\n", - "\n", - " Attributes:\n", - " href (str): Link to the asset object. Relative and absolute links are both allowed.\n", - " title (str): Optional displayed title for clients and users.\n", - " description (str): A description of the Asset providing additional details, such as\n", - " how it was processed or created. CommonMark 0.29 syntax MAY be used for rich\n", - " text representation.\n", - " media_type (str): Optional description of the media type. Registered Media Types\n", + " href : Link to the asset object. Relative and absolute links are both\n", + " allowed.\n", + " title : Optional displayed title for clients and users.\n", + " description : A description of the Asset providing additional details,\n", + " such as how it was processed or created. CommonMark 0.29 syntax MAY be used\n", + " for rich text representation.\n", + " media_type : Optional description of the media type. Registered Media Types\n", " are preferred. See :class:`~pystac.MediaType` for common media types.\n", - " properties (dict): Optional, additional properties for this asset. This is used by\n", - " extensions as a way to serialize and deserialize properties on asset\n", + " roles : Optional, Semantic roles (i.e. thumbnail, overview,\n", + " data, metadata) of the asset.\n", + " extra_fields : Optional, additional fields for this asset. This is used\n", + " by extensions as a way to serialize and deserialize properties on asset\n", " object JSON.\n", - " owner (Item or None): The Item this asset belongs to.\n", " \n" ] } @@ -501,20 +443,9 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "item.add_asset(\n", " key='image', \n", @@ -534,7 +465,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -543,10 +474,10 @@ "text": [ "{\n", " \"type\": \"Feature\",\n", - " \"stac_version\": \"1.0.0-beta.2\",\n", + " \"stac_version\": \"1.0.0\",\n", " \"id\": \"local-image\",\n", " \"properties\": {\n", - " \"datetime\": \"2020-08-03T03:47:48.786929Z\"\n", + " \"datetime\": \"2022-03-29T12:47:45.754444Z\"\n", " },\n", " \"geometry\": {\n", " \"type\": \"Polygon\",\n", @@ -589,7 +520,7 @@ " ],\n", " \"assets\": {\n", " \"image\": {\n", - " \"href\": \"/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/image.tif\",\n", + " \"href\": \"/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/image.tif\",\n", " \"type\": \"image/tiff; application=geotiff\"\n", " }\n", " },\n", @@ -598,7 +529,8 @@ " 55.73478197572927,\n", " 37.66573047610874,\n", " 55.73882710285011\n", - " ]\n", + " ],\n", + " \"stac_extensions\": []\n", "}\n" ] } @@ -631,7 +563,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -657,20 +589,9 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 22, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "catalog.normalize_hrefs(os.path.join(tmp_dir.name, 'stac'))" ] @@ -684,15 +605,15 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/stac/catalog.json\n", - "/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/stac/local-image/local-image.json\n" + "/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/stac/catalog.json\n", + "/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/stac/local-image/local-image.json\n" ] } ], @@ -712,7 +633,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -721,17 +642,17 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/stac/catalog.json\r\n", - "\r\n", - "/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/stac/local-image:\r\n", - "local-image.json\r\n" + "/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/stac/catalog.json\n", + "\n", + "/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/stac/local-image:\n", + "local-image.json\n" ] } ], @@ -741,7 +662,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -749,33 +670,35 @@ "output_type": "stream", "text": [ "{\n", - " \"id\": \"test-catalog\",\n", - " \"stac_version\": \"1.0.0-beta.2\",\n", - " \"description\": \"Tutorial catalog.\",\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": \"./catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"item\",\n", - " \"href\": \"./local-image/local-image.json\",\n", - " \"type\": \"application/json\"\n", - " }\n", - " ]\n", + " \"type\": \"Catalog\",\n", + " \"id\": \"test-catalog\",\n", + " \"stac_version\": \"1.0.0\",\n", + " \"description\": \"Tutorial catalog.\",\n", + " \"links\": [\n", + " {\n", + " \"rel\": \"root\",\n", + " \"href\": \"./catalog.json\",\n", + " \"type\": \"application/json\"\n", + " },\n", + " {\n", + " \"rel\": \"item\",\n", + " \"href\": \"./local-image/local-image.json\",\n", + " \"type\": \"application/json\"\n", + " }\n", + " ],\n", + " \"stac_extensions\": []\n", "}\n" ] } ], "source": [ - "with open(catalog.get_self_href()) as f:\n", + "with open(item.self_href) as f:\n", " print(f.read())" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 27, "metadata": {}, "outputs": [ { @@ -783,69 +706,70 @@ "output_type": "stream", "text": [ "{\n", - " \"type\": \"Feature\",\n", - " \"stac_version\": \"1.0.0-beta.2\",\n", - " \"id\": \"local-image\",\n", - " \"properties\": {\n", - " \"datetime\": \"2020-08-03T03:47:48.786929Z\"\n", - " },\n", - " \"geometry\": {\n", - " \"type\": \"Polygon\",\n", - " \"coordinates\": [\n", - " [\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ]\n", - " ]\n", + " \"type\": \"Feature\",\n", + " \"stac_version\": \"1.0.0\",\n", + " \"id\": \"local-image\",\n", + " \"properties\": {\n", + " \"datetime\": \"2022-03-29T12:47:45.754444Z\"\n", + " },\n", + " \"geometry\": {\n", + " \"type\": \"Polygon\",\n", + " \"coordinates\": [\n", + " [\n", + " [\n", + " 37.6616853489879,\n", + " 55.73478197572927\n", + " ],\n", + " [\n", + " 37.6616853489879,\n", + " 55.73882710285011\n", + " ],\n", + " [\n", + " 37.66573047610874,\n", + " 55.73882710285011\n", + " ],\n", + " [\n", + " 37.66573047610874,\n", + " 55.73478197572927\n", + " ],\n", + " [\n", + " 37.6616853489879,\n", + " 55.73478197572927\n", " ]\n", - " },\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": \"../catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"parent\",\n", - " \"href\": \"../catalog.json\",\n", - " \"type\": \"application/json\"\n", - " }\n", - " ],\n", - " \"assets\": {\n", - " \"image\": {\n", - " \"href\": \"/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/image.tif\",\n", - " \"type\": \"image/tiff; application=geotiff\"\n", - " }\n", - " },\n", - " \"bbox\": [\n", - " 37.6616853489879,\n", - " 55.73478197572927,\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", + " ]\n", " ]\n", + " },\n", + " \"links\": [\n", + " {\n", + " \"rel\": \"root\",\n", + " \"href\": \"../catalog.json\",\n", + " \"type\": \"application/json\"\n", + " },\n", + " {\n", + " \"rel\": \"parent\",\n", + " \"href\": \"../catalog.json\",\n", + " \"type\": \"application/json\"\n", + " }\n", + " ],\n", + " \"assets\": {\n", + " \"image\": {\n", + " \"href\": \"/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/image.tif\",\n", + " \"type\": \"image/tiff; application=geotiff\"\n", + " }\n", + " },\n", + " \"bbox\": [\n", + " 37.6616853489879,\n", + " 55.73478197572927,\n", + " 37.66573047610874,\n", + " 55.73882710285011\n", + " ],\n", + " \"stac_extensions\": []\n", "}\n" ] } ], "source": [ - "with open(item.get_self_href()) as f:\n", + "with open(item.self_href) as f:\n", " print(f.read())" ] }, @@ -858,7 +782,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -874,7 +798,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -882,68 +806,69 @@ "output_type": "stream", "text": [ "{\n", - " \"type\": \"Feature\",\n", - " \"stac_version\": \"1.0.0-beta.2\",\n", - " \"id\": \"local-image\",\n", - " \"properties\": {\n", - " \"datetime\": \"2020-08-03T03:47:48.786929Z\"\n", - " },\n", - " \"geometry\": {\n", - " \"type\": \"Polygon\",\n", - " \"coordinates\": [\n", - " [\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ]\n", - " ]\n", + " \"type\": \"Feature\",\n", + " \"stac_version\": \"1.0.0\",\n", + " \"id\": \"local-image\",\n", + " \"properties\": {\n", + " \"datetime\": \"2022-03-29T12:47:45.754444Z\"\n", + " },\n", + " \"geometry\": {\n", + " \"type\": \"Polygon\",\n", + " \"coordinates\": [\n", + " [\n", + " [\n", + " 37.6616853489879,\n", + " 55.73478197572927\n", + " ],\n", + " [\n", + " 37.6616853489879,\n", + " 55.73882710285011\n", + " ],\n", + " [\n", + " 37.66573047610874,\n", + " 55.73882710285011\n", + " ],\n", + " [\n", + " 37.66573047610874,\n", + " 55.73478197572927\n", + " ],\n", + " [\n", + " 37.6616853489879,\n", + " 55.73478197572927\n", " ]\n", + " ]\n", + " ]\n", + " },\n", + " \"links\": [\n", + " {\n", + " \"rel\": \"root\",\n", + " \"href\": \"/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/stac/catalog.json\",\n", + " \"type\": \"application/json\"\n", " },\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"self\",\n", - " \"href\": \"/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/stac/local-image/local-image.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": \"/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/stac/catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"parent\",\n", - " \"href\": \"/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/stac/catalog.json\",\n", - " \"type\": \"application/json\"\n", - " }\n", - " ],\n", - " \"assets\": {\n", - " \"image\": {\n", - " \"href\": \"/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/image.tif\",\n", - " \"type\": \"image/tiff; application=geotiff\"\n", - " }\n", + " {\n", + " \"rel\": \"parent\",\n", + " \"href\": \"/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/stac/catalog.json\",\n", + " \"type\": \"application/json\"\n", " },\n", - " \"bbox\": [\n", - " 37.6616853489879,\n", - " 55.73478197572927,\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ]\n", + " {\n", + " \"rel\": \"self\",\n", + " \"href\": \"/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/stac/local-image/local-image.json\",\n", + " \"type\": \"application/json\"\n", + " }\n", + " ],\n", + " \"assets\": {\n", + " \"image\": {\n", + " \"href\": \"/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/image.tif\",\n", + " \"type\": \"image/tiff; application=geotiff\"\n", + " }\n", + " },\n", + " \"bbox\": [\n", + " 37.6616853489879,\n", + " 55.73478197572927,\n", + " 37.66573047610874,\n", + " 55.73882710285011\n", + " ],\n", + " \"stac_extensions\": []\n", "}\n" ] } @@ -962,7 +887,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -972,7 +897,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -980,63 +905,64 @@ "output_type": "stream", "text": [ "{\n", - " \"type\": \"Feature\",\n", - " \"stac_version\": \"1.0.0-beta.2\",\n", - " \"id\": \"local-image\",\n", - " \"properties\": {\n", - " \"datetime\": \"2020-08-03T03:47:48.786929Z\"\n", - " },\n", - " \"geometry\": {\n", - " \"type\": \"Polygon\",\n", - " \"coordinates\": [\n", - " [\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", - " ],\n", - " [\n", - " 37.66573047610874,\n", - " 55.73478197572927\n", - " ],\n", - " [\n", - " 37.6616853489879,\n", - " 55.73478197572927\n", - " ]\n", - " ]\n", + " \"type\": \"Feature\",\n", + " \"stac_version\": \"1.0.0\",\n", + " \"id\": \"local-image\",\n", + " \"properties\": {\n", + " \"datetime\": \"2022-03-29T12:47:45.754444Z\"\n", + " },\n", + " \"geometry\": {\n", + " \"type\": \"Polygon\",\n", + " \"coordinates\": [\n", + " [\n", + " [\n", + " 37.6616853489879,\n", + " 55.73478197572927\n", + " ],\n", + " [\n", + " 37.6616853489879,\n", + " 55.73882710285011\n", + " ],\n", + " [\n", + " 37.66573047610874,\n", + " 55.73882710285011\n", + " ],\n", + " [\n", + " 37.66573047610874,\n", + " 55.73478197572927\n", + " ],\n", + " [\n", + " 37.6616853489879,\n", + " 55.73478197572927\n", " ]\n", - " },\n", - " \"links\": [\n", - " {\n", - " \"rel\": \"root\",\n", - " \"href\": \"../catalog.json\",\n", - " \"type\": \"application/json\"\n", - " },\n", - " {\n", - " \"rel\": \"parent\",\n", - " \"href\": \"../catalog.json\",\n", - " \"type\": \"application/json\"\n", - " }\n", - " ],\n", - " \"assets\": {\n", - " \"image\": {\n", - " \"href\": \"../../image.tif\",\n", - " \"type\": \"image/tiff; application=geotiff\"\n", - " }\n", - " },\n", - " \"bbox\": [\n", - " 37.6616853489879,\n", - " 55.73478197572927,\n", - " 37.66573047610874,\n", - " 55.73882710285011\n", + " ]\n", " ]\n", + " },\n", + " \"links\": [\n", + " {\n", + " \"rel\": \"root\",\n", + " \"href\": \"../catalog.json\",\n", + " \"type\": \"application/json\"\n", + " },\n", + " {\n", + " \"rel\": \"parent\",\n", + " \"href\": \"../catalog.json\",\n", + " \"type\": \"application/json\"\n", + " }\n", + " ],\n", + " \"assets\": {\n", + " \"image\": {\n", + " \"href\": \"../../image.tif\",\n", + " \"type\": \"image/tiff; application=geotiff\"\n", + " }\n", + " },\n", + " \"bbox\": [\n", + " 37.6616853489879,\n", + " 55.73478197572927,\n", + " 37.66573047610874,\n", + " 55.73882710285011\n", + " ],\n", + " \"stac_extensions\": []\n", "}\n" ] } @@ -1064,11 +990,11 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ - "from pystac.extensions.eo import Band\n", + "from pystac.extensions.eo import Band, EOExtension\n", "\n", "# From: https://www.spaceimagingme.com/downloads/sensors/datasheets/DG_WorldView3_DS_2014.pdf\n", "\n", @@ -1098,7 +1024,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -1107,9 +1033,8 @@ " bbox=bbox,\n", " datetime=datetime.utcnow(),\n", " properties={})\n", - "\n", - "eo_item.ext.enable(pystac.Extensions.EO)\n", - "eo_item.ext.eo.apply(bands=wv3_bands)" + "eo = EOExtension.ext(eo_item, add_if_missing=True)\n", + "eo.apply(bands=wv3_bands)" ] }, { @@ -1121,33 +1046,30 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "eo_item.common_metadata.platform = \"Maxar\"\n", - "eo_item.common_metadata.instrument=\"WorldView3\"\n", + "eo_item.common_metadata.instruments = [\"WorldView3\"]\n", "eo_item.common_metadata.gsd = 0.3" ] }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 35, "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] } ], "source": [ - "eo_item" + "print(eo_item)" ] }, { @@ -1159,52 +1081,15 @@ }, { "cell_type": "code", - "execution_count": 34, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Help on method set_bands in module pystac.extensions.eo:\n", - "\n", - "set_bands(bands, asset=None) method of pystac.extensions.eo.EOItemExt instance\n", - " Set an Item or an Asset bands.\n", - " \n", - " If an Asset is supplied, sets the property on the Asset.\n", - " Otherwise sets the Item's value.\n", - "\n" - ] - } - ], - "source": [ - "eo_ext = eo_item.ext.eo\n", - "help(eo_ext.set_bands)\n", - "\n", - "#eo_item.add_asset(key='image', asset=)" - ] - }, - { - "cell_type": "code", - "execution_count": 35, + "execution_count": 36, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 35, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "asset = pystac.Asset(href=img_path, \n", " media_type=pystac.MediaType.GEOTIFF)\n", - "eo_ext.set_bands(wv3_bands, asset)\n", - "eo_item.add_asset(\"image\", asset)" + "eo_item.add_asset(\"image\", asset)\n", + "eo_on_asset = EOExtension.ext(eo_item.assets[\"image\"])\n", + "eo_on_asset.apply(wv3_bands)" ] }, { @@ -1216,14 +1101,14 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 37, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'href': '/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/image.tif',\n", - " 'type': 'image/tiff; application=geotiff',\n", + "{'href': '/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/image.tif',\n", + " 'type': ,\n", " 'eo:bands': [{'name': 'Coastal',\n", " 'common_name': 'coastal',\n", " 'description': 'Coastal: 400 - 450 nm'},\n", @@ -1246,7 +1131,7 @@ " 'description': 'Near-IR2: 860 - 1040 nm'}]}" ] }, - "execution_count": 36, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -1264,7 +1149,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -1273,7 +1158,7 @@ "[]" ] }, - "execution_count": 37, + "execution_count": 38, "metadata": {}, "output_type": "execute_result" } @@ -1285,7 +1170,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 39, "metadata": {}, "outputs": [ { @@ -1294,7 +1179,7 @@ "[]" ] }, - "execution_count": 38, + "execution_count": 39, "metadata": {}, "output_type": "execute_result" } @@ -1306,7 +1191,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -1323,7 +1208,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -1332,7 +1217,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 42, "metadata": {}, "outputs": [ { @@ -1341,69 +1226,50 @@ "[]" ] }, - "execution_count": 41, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } ], "source": [ + "assert isinstance(catalog2, pystac.Catalog)\n", "list(catalog2.get_items())" ] }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 43, "metadata": {}, "outputs": [], "source": [ - "item = next(catalog2.get_all_items())" + "item: pystac.Item = next(catalog2.get_all_items())" ] }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 44, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 43, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "item.ext.implements('eo')" + "assert EOExtension.has_extension(item)" ] }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 45, "metadata": {}, "outputs": [ { - "data": { - "text/plain": [ - "[,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - " ]" - ] - }, - "execution_count": 44, - "metadata": {}, - "output_type": "execute_result" + "name": "stdout", + "output_type": "stream", + "text": [ + "[, , , , , , , ]\n" + ] } ], "source": [ - "item.ext.eo.get_bands(item.assets['image'])" + "eo_on_asset = EOExtension.ext(item.assets[\"image\"])\n", + "print(eo_on_asset.bands)" ] }, { @@ -1419,17 +1285,17 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 46, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "('/var/folders/sv/zr8j0t4j1f726nhlt3vb8c300000gn/T/tmpt1wuelid/image.tif',\n", - " )" + "('/var/folders/9z/lnsvqfqj4gs2d1j1nw3vynrm0000gn/T/tmpzpx86d17/image.tif',\n", + " )" ] }, - "execution_count": 45, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" } @@ -1444,7 +1310,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ @@ -1460,7 +1326,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 48, "metadata": {}, "outputs": [ { @@ -1471,44 +1337,31 @@ " enable discovery.\n", "\n", " Args:\n", - " id (str): Identifier for the collection. Must be unique within the STAC.\n", - " description (str): Detailed multi-line description to fully explain the collection.\n", - " `CommonMark 0.28 syntax `_ MAY be used for rich text\n", - " representation.\n", - " extent (Extent): Spatial and temporal extents that describe the bounds of\n", + " id : Identifier for the collection. Must be unique within the STAC.\n", + " description : Detailed multi-line description to fully explain the\n", + " collection. `CommonMark 0.28 syntax `_ MAY\n", + " be used for rich text representation.\n", + " extent : Spatial and temporal extents that describe the bounds of\n", " all items contained within this Collection.\n", - " title (str or None): Optional short descriptive one-line title for the collection.\n", - " stac_extensions (List[str]): Optional list of extensions the Collection implements.\n", - " href (str or None): Optional HREF for this collection, which be set as the collection's\n", - " self link's HREF.\n", - " license (str): Collection's license(s) as a `SPDX License identifier\n", - " `_, `various`, or `proprietary`. If collection includes\n", - " data with multiple different licenses, use `various` and add a link for each.\n", - " Defaults to 'proprietary'.\n", - " keywords (List[str]): Optional list of keywords describing the collection.\n", - " providers (List[Provider]): Optional list of providers of this Collection.\n", - " properties (dict): Optional dict of common fields across referenced items.\n", - " summaries (dict): An optional map of property summaries,\n", + " title : Optional short descriptive one-line title for the\n", + " collection.\n", + " stac_extensions : Optional list of extensions the Collection\n", + " implements.\n", + " href : Optional HREF for this collection, which be set as the\n", + " collection's self link's HREF.\n", + " catalog_type : Optional catalog type for this catalog. Must\n", + " be one of the values in :class`~pystac.CatalogType`.\n", + " license : Collection's license(s) as a\n", + " `SPDX License identifier `_,\n", + " `various`, or `proprietary`. If collection includes\n", + " data with multiple different licenses, use `various` and add a link for\n", + " each. Defaults to 'proprietary'.\n", + " keywords : Optional list of keywords describing the collection.\n", + " providers : Optional list of providers of this Collection.\n", + " summaries : An optional map of property summaries,\n", " either a set of values or statistics such as a range.\n", - " extra_fields (dict or None): Extra fields that are part of the top-level JSON properties\n", - " of the Collection.\n", - "\n", - " Attributes:\n", - " id (str): Identifier for the collection.\n", - " description (str): Detailed multi-line description to fully explain the collection.\n", - " extent (Extent): Spatial and temporal extents that describe the bounds of\n", - " all items contained within this Collection.\n", - " title (str or None): Optional short descriptive one-line title for the collection.\n", - " stac_extensions (List[str]): Optional list of extensions the Collection implements.\n", - " keywords (List[str] or None): Optional list of keywords describing the collection.\n", - " providers (List[Provider] or None): Optional list of providers of this Collection.\n", - " properties (dict or None): Optional dict of common fields across referenced items.\n", - " summaries (dict or None): An optional map of property summaries,\n", - " either a set of values or statistics such as a range.\n", - " links (List[Link]): A list of :class:`~pystac.Link` objects representing\n", - " all links associated with this Collection.\n", - " extra_fields (dict or None): Extra fields that are part of the top-level JSON properties\n", - " of the Catalog.\n", + " extra_fields : Extra fields that are part of the top-level\n", + " JSON properties of the Collection.\n", " \n" ] } @@ -1526,22 +1379,20 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 49, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Describes the spatio-temporal extents of a Collection.\n", + "Describes the spatiotemporal extents of a Collection.\n", "\n", " Args:\n", - " spatial (SpatialExtent): Potential spatial extent covered by the collection.\n", - " temporal (TemporalExtent): Potential temporal extent covered by the collection.\n", - "\n", - " Attributes:\n", - " spatial (SpatialExtent): Potential spatial extent covered by the collection.\n", - " temporal (TemporalExtent): Potential temporal extent covered by the collection.\n", + " spatial : Potential spatial extent covered by the collection.\n", + " temporal : Potential temporal extent covered by the collection.\n", + " extra_fields : Dictionary containing additional top-level fields defined on the\n", + " Extent object.\n", " \n" ] } @@ -1561,27 +1412,15 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 50, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 49, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "collection_item = pystac.Item(id='local-image-col-1',\n", " geometry=footprint,\n", " bbox=bbox,\n", " datetime=datetime.utcnow(),\n", - " properties={},\n", - " stac_extensions=[pystac.Extensions.EO])\n", + " properties={})\n", "\n", "collection_item.common_metadata.gsd = 0.3\n", "collection_item.common_metadata.platform = 'Maxar'\n", @@ -1589,15 +1428,15 @@ "\n", "asset = pystac.Asset(href=img_path, \n", " media_type=pystac.MediaType.GEOTIFF)\n", - "collection_item.ext.eo.set_bands(wv3_bands, asset)\n", - "collection_item.add_asset('image', asset)\n", + "collection_item.add_asset(\"image\", asset)\n", + "eo = EOExtension.ext(collection_item.assets[\"image\"], add_if_missing=True)\n", + "eo.apply(wv3_bands)\n", "\n", "collection_item2 = pystac.Item(id='local-image-col-2',\n", " geometry=footprint2,\n", " bbox=bbox2,\n", " datetime=datetime.utcnow(),\n", - " properties={},\n", - " stac_extensions=[pystac.Extensions.EO])\n", + " properties={})\n", "\n", "collection_item2.common_metadata.gsd = 0.3\n", "collection_item2.common_metadata.platform = 'Maxar'\n", @@ -1605,11 +1444,11 @@ "\n", "asset2 = pystac.Asset(href=img_path,\n", " media_type=pystac.MediaType.GEOTIFF)\n", - "collection_item2.ext.eo.set_bands([\n", + "collection_item2.add_asset(\"image\", asset2)\n", + "eo = EOExtension.ext(collection_item2.assets[\"image\"], add_if_missing=True)\n", + "eo.apply([\n", " band for band in wv3_bands if band.name in [\"Red\", \"Green\", \"Blue\"]\n", - "], asset2)\n", - "\n", - "collection_item2.add_asset('image', asset2)" + "])\n" ] }, { @@ -1621,7 +1460,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ @@ -1634,7 +1473,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 52, "metadata": {}, "outputs": [], "source": [ @@ -1644,7 +1483,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 53, "metadata": {}, "outputs": [], "source": [ @@ -1653,7 +1492,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -1672,7 +1511,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 55, "metadata": {}, "outputs": [], "source": [ @@ -1681,7 +1520,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 56, "metadata": {}, "outputs": [], "source": [ @@ -1692,7 +1531,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 57, "metadata": {}, "outputs": [ { @@ -1712,7 +1551,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 58, "metadata": {}, "outputs": [], "source": [ @@ -1731,7 +1570,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 59, "metadata": {}, "outputs": [], "source": [ @@ -1758,42 +1597,50 @@ "source": [ "### Allowing PySTAC to read from AWS S3\n", "\n", - "PySTAC aims to be virtually zero-dependency (notwithstanding the why-isn't-this-in-stdlib datetime-util), so it doesn't have the ability to read from or write to anything but the local file system. However, we can hook into PySTAC's IO in the following way. Learn more about how to use STAC_IO in the [documentation on the topic](https://pystac.readthedocs.io/en/latest/concepts.html#using-stac-io):" + "PySTAC aims to be virtually zero-dependency (notwithstanding the why-isn't-this-in-stdlib datetime-util), so it doesn't have the ability to read from or write to anything but the local file system. However, we can hook into PySTAC's IO in the following way. Learn more about how to customize I/O in STAC from the [documentation](https://pystac.readthedocs.io/en/stable/concepts.html#i-o-in-pystac):" ] }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 60, "metadata": {}, "outputs": [], "source": [ + "from typing import Union, Any\n", "from urllib.parse import urlparse\n", + "\n", "import boto3\n", - "from pystac import STAC_IO\n", + "from pystac import Link\n", + "from pystac.stac_io import DefaultStacIO\n", "\n", - "def my_read_method(uri):\n", - " parsed = urlparse(uri)\n", - " if parsed.scheme == 's3':\n", - " bucket = parsed.netloc\n", - " key = parsed.path[1:]\n", - " s3 = boto3.resource('s3')\n", - " obj = s3.Object(bucket, key)\n", - " return obj.get()['Body'].read().decode('utf-8')\n", - " else:\n", - " return STAC_IO.default_read_text_method(uri)\n", "\n", - "def my_write_method(uri, txt):\n", - " parsed = urlparse(uri)\n", - " if parsed.scheme == 's3':\n", - " bucket = parsed.netloc\n", - " key = parsed.path[1:]\n", - " s3 = boto3.resource(\"s3\")\n", - " s3.Object(bucket, key).put(Body=txt)\n", - " else:\n", - " STAC_IO.default_write_text_method(uri, txt)\n", + "class CustomStacIO(DefaultStacIO):\n", + " def __init__(self):\n", + " self.s3 = boto3.resource(\"s3\")\n", "\n", - "STAC_IO.read_text_method = my_read_method\n", - "STAC_IO.write_text_method = my_write_method" + " def read_text(\n", + " self, source: Union[str, Link], *args: Any, **kwargs: Any\n", + " ) -> str:\n", + " parsed = urlparse(uri)\n", + " if parsed.scheme == \"s3\":\n", + " bucket = parsed.netloc\n", + " key = parsed.path[1:]\n", + "\n", + " obj = self.s3.Object(bucket, key)\n", + " return obj.get()[\"Body\"].read().decode(\"utf-8\")\n", + " else:\n", + " return super().read_text(source, *args, **kwargs)\n", + "\n", + " def write_text(\n", + " self, dest: Union[str, Link], txt: str, *args: Any, **kwargs: Any\n", + " ) -> None:\n", + " parsed = urlparse(uri)\n", + " if parsed.scheme == \"s3\":\n", + " bucket = parsed.netloc\n", + " key = parsed.path[1:]\n", + " self.s3.Object(bucket, key).put(Body=txt, ContentEncoding=\"utf-8\")\n", + " else:\n", + " super().write_text(dest, txt, *args, **kwargs)\n" ] }, { @@ -1805,15 +1652,17 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 63, "metadata": {}, "outputs": [], "source": [ "# From https://alexwlchan.net/2017/07/listing-s3-keys/\n", + "from botocore import UNSIGNED\n", + "from botocore.config import Config\n", "\n", "def get_s3_keys(bucket, prefix):\n", " \"\"\"Generate all the keys in an S3 bucket.\"\"\"\n", - " s3 = boto3.client('s3')\n", + " s3 = boto3.client('s3', config=Config(signature_version=UNSIGNED))\n", " kwargs = {'Bucket': bucket, 'Prefix': prefix}\n", " while True:\n", " resp = s3.list_objects_v2(**kwargs)\n", @@ -1835,17 +1684,17 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 64, "metadata": {}, "outputs": [], "source": [ "moscow_training_chip_uris = list(get_s3_keys(bucket='spacenet-dataset', \n", - " prefix='spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS'))" + " prefix='spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/'))" ] }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 65, "metadata": {}, "outputs": [], "source": [ @@ -1870,7 +1719,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 66, "metadata": {}, "outputs": [], "source": [ @@ -1879,7 +1728,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 67, "metadata": {}, "outputs": [ { @@ -1897,7 +1746,7 @@ " '1005': {'img': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/PS-MS/SN5_roads_train_AOI_7_Moscow_PS-MS_chip1005.tif'}}" ] }, - "execution_count": 64, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } @@ -1915,7 +1764,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 68, "metadata": {}, "outputs": [], "source": [ @@ -1935,7 +1784,7 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 71, "metadata": {}, "outputs": [ { @@ -1956,6 +1805,9 @@ } ], "source": [ + "import os\n", + "os.environ[\"AWS_NO_SIGN_REQUEST\"] = \"true\"\n", + "\n", "for chip_id in chip_id_to_data:\n", " img_uri = chip_id_to_data[chip_id]['img']\n", " print('Processing {}'.format(img_uri))\n", @@ -1965,18 +1817,19 @@ " geometry=footprint,\n", " bbox=bbox,\n", " datetime=datetime.utcnow(),\n", - " properties={},\n", - " stac_extensions=[pystac.Extensions.EO]) \n", + " properties={})\n", " \n", " item.common_metadata.gsd = 0.3\n", " item.common_metadata.platform = 'Maxar'\n", " item.common_metadata.instruments = ['WorldView3']\n", " \n", - " item.ext.eo.bands = wv3_bands\n", + " eo = EOExtension.ext(item, add_if_missing=True)\n", + " eo.bands = wv3_bands\n", " asset = pystac.Asset(href=img_uri,\n", " media_type=pystac.MediaType.COG)\n", - " item.ext.eo.set_bands(wv3_bands, asset)\n", " item.add_asset(key='ps-ms', asset=asset)\n", + " eo = EOExtension.ext(item.assets[\"ps-ms\"])\n", + " eo.bands = wv3_bands\n", " chip_id_to_items[chip_id] = item" ] }, @@ -1991,7 +1844,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 72, "metadata": {}, "outputs": [], "source": [ @@ -2005,7 +1858,7 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 73, "metadata": {}, "outputs": [], "source": [ @@ -2016,7 +1869,7 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 74, "metadata": {}, "outputs": [], "source": [ @@ -2025,7 +1878,7 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 75, "metadata": {}, "outputs": [], "source": [ @@ -2037,7 +1890,7 @@ }, { "cell_type": "code", - "execution_count": 71, + "execution_count": 76, "metadata": {}, "outputs": [], "source": [ @@ -2046,7 +1899,7 @@ }, { "cell_type": "code", - "execution_count": 72, + "execution_count": 77, "metadata": {}, "outputs": [ { @@ -2080,7 +1933,7 @@ }, { "cell_type": "code", - "execution_count": 73, + "execution_count": 78, "metadata": {}, "outputs": [], "source": [ @@ -2090,7 +1943,7 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": 79, "metadata": {}, "outputs": [ { @@ -2127,7 +1980,7 @@ }, { "cell_type": "code", - "execution_count": 75, + "execution_count": 80, "metadata": {}, "outputs": [], "source": [ @@ -2137,7 +1990,7 @@ }, { "cell_type": "code", - "execution_count": 76, + "execution_count": 81, "metadata": {}, "outputs": [], "source": [ @@ -2156,7 +2009,7 @@ }, { "cell_type": "code", - "execution_count": 77, + "execution_count": 82, "metadata": {}, "outputs": [], "source": [ @@ -2173,7 +2026,7 @@ }, { "cell_type": "code", - "execution_count": 78, + "execution_count": 84, "metadata": {}, "outputs": [ { @@ -2183,31 +2036,32 @@ "Applies label extension properties to the extended Item.\n", "\n", " Args:\n", - " label_description (str): A description of the label, how it was created,\n", + " label_description : A description of the label, how it was created,\n", " and what it is recommended for\n", - " label_type (str): An ENUM of either vector label type or raster label type. Use\n", + " label_type : An Enum of either vector label type or raster label type. Use\n", " one of :class:`~pystac.LabelType`.\n", - " label_properties (list or None): These are the names of the property field(s) in each\n", + " label_properties : These are the names of the property field(s) in each\n", " Feature of the label asset's FeatureCollection that contains the classes\n", " (keywords from label:classes if the property defines classes).\n", " If labels are rasters, this should be None.\n", - " label_classes (List[LabelClass]): Optional, but reqiured if ussing categorical data.\n", - " A list of LabelClasses defining the list of possible class names for each\n", - " label:properties. (e.g., tree, building, car, hippo)\n", - " label_tasks (List[str]): Recommended to be a subset of 'regression', 'classification',\n", + " label_classes : Optional, but required if using categorical data.\n", + " A list of :class:`LabelClasses` instances defining the list of possible\n", + " class names for each label:properties. (e.g., tree, building, car,\n", + " hippo)\n", + " label_tasks : Recommended to be a subset of 'regression', 'classification',\n", " 'detection', or 'segmentation', but may be an arbitrary value.\n", " label_methods: Recommended to be a subset of 'automated' or 'manual',\n", " but may be an arbitrary value.\n", - " label_overviews (List[LabelOverview]): Optional list of LabelOverview classes\n", - " that store counts (for classification-type data) or summary statistics (for\n", - " continuous numerical/regression data).\n", + " label_overviews : Optional list of :class:`LabelOverview` instances\n", + " that store counts (for classification-type data) or summary statistics\n", + " (for continuous numerical/regression data).\n", " \n" ] } ], "source": [ - "from pystac.extensions import label\n", - "print(label.LabelItemExt.apply.__doc__)" + "from pystac.extensions.label import LabelExtension\n", + "print(LabelExtension.apply.__doc__)" ] }, { @@ -2219,25 +2073,28 @@ }, { "cell_type": "code", - "execution_count": 79, + "execution_count": 85, "metadata": {}, "outputs": [], "source": [ + "from pystac.extensions.label import LabelType\n", + "\n", "for chip_id in chip_id_to_data:\n", " img_item = collection.get_item('img_{}'.format(chip_id))\n", + " assert img_item\n", " label_uri = chip_id_to_data[chip_id]['label']\n", " \n", " label_item = pystac.Item(id='label_{}'.format(chip_id),\n", " geometry=img_item.geometry,\n", " bbox=img_item.bbox,\n", " datetime=datetime.utcnow(),\n", - " properties={},\n", - " stac_extensions=[pystac.Extensions.LABEL])\n", - " label_item.ext.label.apply(label_description=\"SpaceNet 5 Road labels\",\n", - " label_type=label.LabelType.VECTOR,\n", + " properties={})\n", + " label = LabelExtension.ext(label_item, add_if_missing=True)\n", + " label.apply(label_description=\"SpaceNet 5 Road labels\",\n", + " label_type=LabelType.VECTOR,\n", " label_tasks=['segmentation', 'regression'])\n", - " label_item.ext.label.add_source(img_item)\n", - " label_item.ext.label.add_geojson_labels(label_uri)\n", + " label.add_source(img_item)\n", + " label.add_geojson_labels(label_uri)\n", " \n", " label_catalog.add_item(label_item)" ] @@ -2251,7 +2108,7 @@ }, { "cell_type": "code", - "execution_count": 80, + "execution_count": 86, "metadata": {}, "outputs": [ { @@ -2290,39 +2147,45 @@ }, { "cell_type": "code", - "execution_count": 81, + "execution_count": 87, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'type': 'Feature',\n", - " 'stac_version': '1.0.0-beta.2',\n", + " 'stac_version': '1.0.0',\n", " 'id': 'label_1',\n", " 'properties': {'label:description': 'SpaceNet 5 Road labels',\n", - " 'label:type': 'vector',\n", + " 'label:type': ,\n", " 'label:properties': None,\n", " 'label:tasks': ['segmentation', 'regression'],\n", - " 'datetime': '2020-08-03T03:47:56.599629Z'},\n", + " 'datetime': '2022-03-29T12:58:05.404487Z'},\n", " 'geometry': {'type': 'Polygon',\n", " 'coordinates': (((37.68191035616281, 55.73478210707574),\n", " (37.68191035616281, 55.73882710285011),\n", " (37.68595535193718, 55.73882710285011),\n", " (37.68595535193718, 55.73478210707574),\n", " (37.68191035616281, 55.73478210707574)),)},\n", - " 'links': [{'rel': 'source', 'href': None, 'type': 'application/json'},\n", - " {'rel': 'root', 'href': None, 'type': 'application/json'},\n", - " {'rel': 'parent', 'href': None, 'type': 'application/json'}],\n", + " 'links': [{'rel': 'source',\n", + " 'href': None,\n", + " 'type': },\n", + " {'rel': ,\n", + " 'href': None,\n", + " 'type': },\n", + " {'rel': ,\n", + " 'href': None,\n", + " 'type': }],\n", " 'assets': {'labels': {'href': 's3://spacenet-dataset/spacenet/SN5_roads/train/AOI_7_Moscow/geojson_roads_speed/SN5_roads_train_AOI_7_Moscow_geojson_roads_speed_chip1.geojson',\n", - " 'type': 'application/geo+json'}},\n", + " 'type': }},\n", " 'bbox': [37.68191035616281,\n", " 55.73478210707574,\n", " 37.68595535193718,\n", " 55.73882710285011],\n", - " 'stac_extensions': ['label']}" + " 'stac_extensions': ['https://stac-extensions.github.io/label/v1.0.1/schema.json']}" ] }, - "execution_count": 81, + "execution_count": 87, "metadata": {}, "output_type": "execute_result" } @@ -2331,13 +2194,6 @@ "label_item = catalog.get_child('spacenet-data-labels').get_item('label_1')\n", "label_item.to_dict()" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -2356,7 +2212,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.6.5" + "version": "3.9.12" } }, "nbformat": 4,