From 93e086fe35485f15524615b2a9122672ccdf1f74 Mon Sep 17 00:00:00 2001 From: sbueche Date: Thu, 11 Dec 2025 13:48:03 -0800 Subject: [PATCH 1/2] features SDK notebook --- .../planet_sdk_features_demo.ipynb | 582 ++++++++++++++++++ 1 file changed, 582 insertions(+) create mode 100644 jupyter-notebooks/api_guides/features_api/planet_sdk_features_demo.ipynb diff --git a/jupyter-notebooks/api_guides/features_api/planet_sdk_features_demo.ipynb b/jupyter-notebooks/api_guides/features_api/planet_sdk_features_demo.ipynb new file mode 100644 index 00000000..699b2443 --- /dev/null +++ b/jupyter-notebooks/api_guides/features_api/planet_sdk_features_demo.ipynb @@ -0,0 +1,582 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "77185d9d", + "metadata": {}, + "source": [ + "# Planet Features API Python Client Introduction\n" + ] + }, + { + "cell_type": "markdown", + "id": "c5e76294", + "metadata": {}, + "source": [ + "This tutorial is an introduction to [Planet's](https://www.planet.com) Features API using the official [Python client](https://github.com/planetlabs/planet-client-python), the `Planet` module. The Features API is used to create *Collections* made up of features that follow [OGC-compliant features standards](https://ogcapi.ogc.org/features/). The features are stored as GeoJson format FeatureCollections, and will be then be available to use across the Planet platform, including in Planet Explorer and Features Manager. The features within your feature collection are referred to as *items* within the FeatureCollection.\n", + "\n", + "## Requirements\n", + "\n", + "An account on the [Planet Platform](https://www.planet.com/account/) is required to access any of Planet's API's. If you are not logged in, you will be prompted to do so below in the *SDK Authentication* section.\n", + "\n", + "## Useful links \n", + "* [Planet SDK for Python](https://planet-sdk-for-python.readthedocs.io/en/stable/get-started/quick-start-guide/)\n", + "* [Planet Python Client Repo](https://github.com/planetlabs/planet-client-python)\n", + "* [Planet Features API Documentation](https://docs.planet.com/develop/apis/features/)" + ] + }, + { + "cell_type": "markdown", + "id": "1be92e24", + "metadata": {}, + "source": [ + "The basic workflow for interaction with the Features API is:\n", + "1. Define Feature(s) through filepath or text\n", + "2. Create a Collection with a name\n", + "3. Add features to the collection\n", + "4. Access items in other Planet API calls\n", + "\n", + "Collections and the Features they hold will be viewable in the Planet Platform [Features Manager](https://www.planet.com/features/). You should be able to see all the Features your org has access to.\n", + "

**Note** that when you upload a feature, it is not required to have a title. Your feature may display without a 'Feature Name' in the GUI without a title input when you upload it. This is intended, and does not affect any of the usage of the features we will be creating.\n", + "\n", + "Using this workflow this tutorial will walk you through using a predefined AOI in Cairo, Egypt around Cairo Lake as the feature for our Collection. This Collection will then be used in an order of PlanetScope Sandbox data during May 2022. Planet Sandbox data is included with both trial and paid Planet Accounts." + ] + }, + { + "cell_type": "markdown", + "id": "90c47ef3", + "metadata": {}, + "source": [ + "____" + ] + }, + { + "cell_type": "markdown", + "id": "f4d93f03", + "metadata": {}, + "source": [ + "## Set up\n", + "\n", + "In order to interact with the Planet API using the Python client, we need to import the necessary packages and authenticate our Planet account credentials." + ] + }, + { + "cell_type": "markdown", + "id": "3c69445b", + "metadata": {}, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b967c46", + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import os\n", + "\n", + "import geopandas as gpd\n", + "from datetime import datetime\n", + "import planet\n", + "from planet import Auth, Planet, Session" + ] + }, + { + "cell_type": "markdown", + "id": "8eb30bd9", + "metadata": {}, + "source": [ + "### SDK Authentication\n", + "\n", + "Your Planet login is used to authenticate and activate the Python SDK. You will be prompted via a link below to login and confirm on the page that the code displayed matches the authorization code printed. If this is your first time accessing the Planet SDK, you will also be prompted first to authorize the SDK access to your Planet account. \n", + "If you would like to know more, please visit the [authentication documentation](https://docs.planet.com/develop/authentication)." + ] + }, + { + "cell_type": "code", + "execution_count": 164, + "id": "481bb763", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Opening browser to login.\n", + "Confirm the authorization code when prompted: GHHD-VLWB\n", + "\n" + ] + } + ], + "source": [ + "# OAuth2 python client authentication\n", + "# If you are not already logged in, this will prompt you to open a web browser to log in.\n", + "\n", + "auth = Auth.from_profile('planet-user', save_state_to_storage=False)\n", + "auth.ensure_initialized(allow_open_browser=True, allow_tty_prompt=True)\n", + "\n", + "session = Session(auth)\n", + "pl = Planet(session)" + ] + }, + { + "cell_type": "markdown", + "id": "960dec0b", + "metadata": {}, + "source": [ + "_____" + ] + }, + { + "cell_type": "markdown", + "id": "faf6ab86", + "metadata": {}, + "source": [ + "### Define Features\n", + "\n", + "First, define the FeatureCollection you wish to post to the FeaturesAPI. \n", + "\n", + "Your FeatureCollection must be:\n", + "\n", + "- Standard GeoJSON FeatureCollection format\n", + "- Must use WGS84/EPSG:4326 projection\n", + "- Be 2 dimensional\n", + "- Contain 1500 vertices or *less*\n", + "\n", + "Full feature rules may be found [here](https://docs.planet.com/develop/apis/features/uploading-and-validating-features/#rules-for-creating-a-feature).\n", + "

\n", + "Below we have defined a FeatureCollection of two areas in Cairo, Egypt. You may use this FeatureCollection to follow along with the tutorial, or import your own from a filepath." + ] + }, + { + "cell_type": "markdown", + "id": "0c215ebb", + "metadata": {}, + "source": [ + "You have a few options to define features to post to your Collections:\n", + "- The most simple option is to use a predefined FeatureCollection, so that the Features API can separate each feature into individual items for you.\n", + "- You may alternatively pass in just the geometries from feature collections. \n", + " - There is a free tool [geojson.io](https://geojson.io/) that will allow you to create geoetries and FeatureCollections by hand. You can also draw and download custom AOI geojson's from [Planet Explorer](https://www.planet.com/explorer/).\n", + "- You can read a geojson file from a local path, a snippet is provided below to do this. " + ] + }, + { + "cell_type": "markdown", + "id": "ab8ef406", + "metadata": {}, + "source": [ + "Features can have properties and keys beyond just the `geometry` and the `id`. If a feature is uploaded without specifying the `property_id`, all properties of the feature are maintained in the item.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33202e83", + "metadata": {}, + "outputs": [], + "source": [ + "lake_poly = [[[31.23905453, 29.99579775], [31.26630889, 29.99579775], [31.26630889, 30.01908084], [31.23905453, 30.01908084], [31.23905453, 29.99579775]]]\n", + "institute_poly = [[[31.23344423, 30.05109414], [31.25491533, 30.05109414], [31.25491533, 30.06461941], [31.23344423, 30.06461941], [31.23344423, 30.05109414]]]\n", + "\n", + "cairo_fc = {\n", + " \"type\": \"FeatureCollection\",\n", + " \"features\": [\n", + " {\n", + " \"id\": \"0\",\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\n", + " \"name_str\": \"area_1\",\n", + " \"extra_property_1\": 'extra property 1 here',\n", + " \"extra_property_2\": 'extra property 2 here'},\n", + " \"geometry\": {\"coordinates\": lake_poly, \"type\": \"Polygon\"},\n", + " },\n", + " {\n", + " \"id\": \"1\",\n", + " \"type\": \"Feature\",\n", + " \"properties\": {\"name_str\": \"area_2\"},\n", + " \"geometry\": {\"coordinates\": institute_poly, \"type\": \"Polygon\"},\n", + " },\n", + "]}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f4f8ce61", + "metadata": {}, + "outputs": [], + "source": [ + "# Read a geojson from a path\n", + "geojson_file = 'PATH TO YOUR GEOJSON FILE'\n", + "\n", + "def load_geojson(filename):\n", + " with open(filename, 'r') as f:\n", + " return json.load(f)\n", + "\n", + "complex_geom = load_geojson(geojson_file)" + ] + }, + { + "cell_type": "markdown", + "id": "809f257d", + "metadata": {}, + "source": [ + "___" + ] + }, + { + "cell_type": "markdown", + "id": "70faedbb", + "metadata": {}, + "source": [ + "### Name and create your FeatureCollection in the Features API\n", + "\n", + "Before you upload your FeatureCollection items, you need to create the Collection with the Features API. Set your `collection_title`, which will serve as the title or name of the whole FeatureCollection once its uploaded. You may optionally set a description for your Collection. Then we can create our new feature_collection using `pl.features.create_collection`, which returns the newly created Collections's `collection_id`. The `collection_id` will be used to add items to the Collection." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9532483d", + "metadata": {}, + "outputs": [], + "source": [ + "collection_title = 'cairo_areas'\n", + "description = 'FeatureCollection of two areas in Cario, Egypt'" + ] + }, + { + "cell_type": "code", + "execution_count": 187, + "id": "5f925cb6", + "metadata": {}, + "outputs": [], + "source": [ + "new_collection_id = pl.features.create_collection(title=collection_title, description=description)" + ] + }, + { + "cell_type": "code", + "execution_count": 120, + "id": "8db31a6c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'cairo_areas-proeNRv'" + ] + }, + "execution_count": 120, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_collection_id" + ] + }, + { + "cell_type": "markdown", + "id": "63aa9b82", + "metadata": {}, + "source": [ + "### Post Features to your Collection\n", + "\n", + "After the Collection is created with the Features API, you may post your features from the FeatureCollection we defined above. We will also set the `property_id` to use for the features. \n", + "

\n", + "The `property_id` is optional, it refers to which key from the `properties` block of the supplied feature(s) to use as the name for the collection. This gives you control of the identifier for items once they are in your collection. Without the `property_id` specified, a random string is generated for you. The example FeatureColleciton uses the property `name_str`, so our features will be identified with the strings `area_1` and `area_2`." + ] + }, + { + "cell_type": "code", + "execution_count": 183, + "id": "d31c6418", + "metadata": {}, + "outputs": [], + "source": [ + "property_id = 'name_str'" + ] + }, + { + "cell_type": "code", + "execution_count": 188, + "id": "4855833d", + "metadata": {}, + "outputs": [], + "source": [ + "new_items = pl.features.add_items(collection_id=new_collection_id, feature = cairo_fc, property_id=property_id)" + ] + }, + { + "cell_type": "code", + "execution_count": 124, + "id": "b10dafc8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['pl:features/my/cairo_areas-proeNRv/area_1-0KZLn6Z',\n", + " 'pl:features/my/cairo_areas-proeNRv/area_2-mWVX7Qr']" + ] + }, + "execution_count": 124, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "new_items" + ] + }, + { + "cell_type": "markdown", + "id": "dbb6594f", + "metadata": {}, + "source": [ + "`add_items` returned a list of strings, which are the `pl reference ids` of the items we just added to the colleciton. These string are what we can use to as the `geometry` input for Orders and Subscriptions later. Before we order imagery with these, we will first explore the other functionalities of the Features python client." + ] + }, + { + "cell_type": "markdown", + "id": "eaad89f4", + "metadata": {}, + "source": [ + "____" + ] + }, + { + "cell_type": "markdown", + "id": "3bcf7ccd", + "metadata": {}, + "source": [ + "### Access your feature Collecitons from the Features API" + ] + }, + { + "cell_type": "markdown", + "id": "e94f5cb5", + "metadata": {}, + "source": [ + "Basic information (title, description, extent, etc.) about all the feature collections you have access to is available with `list_collections()`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6fff398e", + "metadata": {}, + "outputs": [], + "source": [ + "collections = pl.features.list_collections()\n", + "collections_list = [collection for collection in collections]\n", + "collections_list" + ] + }, + { + "cell_type": "markdown", + "id": "d6864356", + "metadata": {}, + "source": [ + "Individual Collection items can be listed with `list_items()`. You can then use the item ids' returned from the items to access the individual items.\n", + "\n", + "We will use the collection_id of the collection we created above, `new_collection_id`:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9c06b2a3", + "metadata": {}, + "outputs": [], + "source": [ + "items = pl.features.list_items(new_collection_id)\n", + "item_list = [item for item in items]\n", + "item_list" + ] + }, + { + "cell_type": "markdown", + "id": "6e981de2", + "metadata": {}, + "source": [ + "To get an individual item from the feature collection, you need the `collection_id` as well as the `feature_id` itself. We will be reusing the `collection_id` from before, and using the first item in our list in that collection." + ] + }, + { + "cell_type": "code", + "execution_count": 138, + "id": "07788648", + "metadata": {}, + "outputs": [], + "source": [ + "collection_id = new_collection_id\n", + "feature_id = item_list[0]['id']" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a69f7322", + "metadata": {}, + "outputs": [], + "source": [ + "pl.features.get_item(collection_id, feature_id)" + ] + }, + { + "cell_type": "markdown", + "id": "d8f60483", + "metadata": {}, + "source": [ + "___" + ] + }, + { + "cell_type": "markdown", + "id": "40384055", + "metadata": {}, + "source": [ + "### Deleting Collections\n", + "\n", + "Using the `collection_id` you may delete a collection using `delete_collection()`. Be careful when you do this, as there is no confirmation output from this method. " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b61da442", + "metadata": {}, + "outputs": [], + "source": [ + "coll_id = ''\n", + "pl.features.delete_collection(coll_id)" + ] + }, + { + "cell_type": "markdown", + "id": "5d2b33e6", + "metadata": {}, + "source": [ + "_____" + ] + }, + { + "cell_type": "markdown", + "id": "861cc6e6", + "metadata": {}, + "source": [ + "# Using Feature References as input geometries\n", + "\n", + "The `'pl:ref'` property of features is can be used in other Planet APIs to link to the geometry of the feature instead of having the user tracking bounding box and geometry variables. Planet `Subscriptions`, `Orders`, and `Data` APIs are all able to make use of these pl feature reference links. \n", + "\n", + "Pl ref ids are submitted in the `geometery` argument as a dictionary: `{'type': 'ref', 'content': 'pl:features/my//'}`\n", + "\n", + "Below provided is consilidated code to use the SDK to get a `pl:ref` id from the features API, and then input that id into an order_request. If you are unfamiliar with the mechanics of the `order_request` functions of the SDK, there is an `` that will walk you through how to use the Planet Python SDK to create and submit Orders.\n", + "\n", + "as well as adding it to a request body for a `request` call. These snippets can be copied into other workflows or scripts you may have to utilize the Features API." + ] + }, + { + "cell_type": "code", + "execution_count": 150, + "id": "c70b7db8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pl:features/my/cairo_areas-proeNRv/area_1-0KZLn6Z\n" + ] + } + ], + "source": [ + "### SDK Order workflow\n", + "## example: get the first Collection you have, and use the id of its first item as a geometry in an Order\n", + "\n", + "# list all your collections\n", + "collections = pl.features.list_collections()\n", + "collections_list = [collection for collection in collections]\n", + "\n", + "# Get the id of the first collection in your list\n", + "collection_id = collections_list[0]['id']\n", + "\n", + "collection_items = list(pl.features.list_items(collection_id))\n", + "feature_ref = collection_items[1]['properties']['pl:ref']\n", + "print(feature_ref)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0912c4a8", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n", + "prod = planet.order_request.product()\n", + "tools = [planet.order_request.clip_tool(feature_ref)]" + ] + }, + { + "cell_type": "code", + "execution_count": 158, + "id": "8040f22f", + "metadata": {}, + "outputs": [], + "source": [ + "name = 'test_cairo_order'\n", + "\n", + "\n", + "order_made = planet.order_request.build_request(name, prod, tools=tools)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0e699260", + "metadata": {}, + "outputs": [], + "source": [ + "order_made" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "baf6af91", + "metadata": {}, + "outputs": [], + "source": [ + "pl.orders.create_order?" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": ".venv (3.13.7)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From f04cc45d3b4a8e516f2c78b0a21e98fe3afb8a0b Mon Sep 17 00:00:00 2001 From: sbueche Date: Fri, 12 Dec 2025 11:41:43 -0800 Subject: [PATCH 2/2] polishing --- .../planet_sdk_features_demo.ipynb | 332 +++++++++++++----- 1 file changed, 246 insertions(+), 86 deletions(-) diff --git a/jupyter-notebooks/api_guides/features_api/planet_sdk_features_demo.ipynb b/jupyter-notebooks/api_guides/features_api/planet_sdk_features_demo.ipynb index 699b2443..960fcf19 100644 --- a/jupyter-notebooks/api_guides/features_api/planet_sdk_features_demo.ipynb +++ b/jupyter-notebooks/api_guides/features_api/planet_sdk_features_demo.ipynb @@ -13,7 +13,7 @@ "id": "c5e76294", "metadata": {}, "source": [ - "This tutorial is an introduction to [Planet's](https://www.planet.com) Features API using the official [Python client](https://github.com/planetlabs/planet-client-python), the `Planet` module. The Features API is used to create *Collections* made up of features that follow [OGC-compliant features standards](https://ogcapi.ogc.org/features/). The features are stored as GeoJson format FeatureCollections, and will be then be available to use across the Planet platform, including in Planet Explorer and Features Manager. The features within your feature collection are referred to as *items* within the FeatureCollection.\n", + "This tutorial is an introduction to [Planet's](https://www.planet.com) Features API using the official [Python client](https://github.com/planetlabs/planet-client-python), the `Planet` module. The Features API is used to create *Collections* made up of *Features* that follow [OGC-compliant features standards](https://ogcapi.ogc.org/features/). The features are stored as GeoJson format FeatureCollections, and will be then be available to use across the Planet APIs or platform, including in Planet Explorer and Features Manager. Your features are referred to as *items* within the FeatureCollection by the API and API documentation.\n", "\n", "## Requirements\n", "\n", @@ -31,15 +31,15 @@ "metadata": {}, "source": [ "The basic workflow for interaction with the Features API is:\n", - "1. Define Feature(s) through filepath or text\n", - "2. Create a Collection with a name\n", - "3. Add features to the collection\n", - "4. Access items in other Planet API calls\n", + "1. Define Feature(s) through filepath or text.\n", + "2. Create a Collection with a unique name.\n", + "3. Add features to the collection.\n", + "4. Use feature reference code 'pl:ref' as geometry input in other API calls.\n", "\n", "Collections and the Features they hold will be viewable in the Planet Platform [Features Manager](https://www.planet.com/features/). You should be able to see all the Features your org has access to.\n", - "

**Note** that when you upload a feature, it is not required to have a title. Your feature may display without a 'Feature Name' in the GUI without a title input when you upload it. This is intended, and does not affect any of the usage of the features we will be creating.\n", + "

\n", "\n", - "Using this workflow this tutorial will walk you through using a predefined AOI in Cairo, Egypt around Cairo Lake as the feature for our Collection. This Collection will then be used in an order of PlanetScope Sandbox data during May 2022. Planet Sandbox data is included with both trial and paid Planet Accounts." + "Using the workflow in this tutorial will walk you through using a predefined AOI in Cairo, Egypt around Cairo Lake as the feature for our Collection. You may also input your own Feature Collection from an area of your choice." ] }, { @@ -70,18 +70,15 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 68, "id": "6b967c46", "metadata": {}, "outputs": [], "source": [ - "import json\n", "import os\n", - "\n", "import geopandas as gpd\n", - "from datetime import datetime\n", "import planet\n", - "from planet import Auth, Planet, Session" + "from planet import Auth, Planet, Session, order_request" ] }, { @@ -97,20 +94,10 @@ }, { "cell_type": "code", - "execution_count": 164, + "execution_count": null, "id": "481bb763", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Opening browser to login.\n", - "Confirm the authorization code when prompted: GHHD-VLWB\n", - "\n" - ] - } - ], + "outputs": [], "source": [ "# OAuth2 python client authentication\n", "# If you are not already logged in, this will prompt you to open a web browser to log in.\n", @@ -139,16 +126,16 @@ "\n", "First, define the FeatureCollection you wish to post to the FeaturesAPI. \n", "\n", - "Your FeatureCollection must be:\n", + "The features must follow the same rules for AOI geometry used in other Planet APIs, including:\n", "\n", - "- Standard GeoJSON FeatureCollection format\n", - "- Must use WGS84/EPSG:4326 projection\n", - "- Be 2 dimensional\n", - "- Contain 1500 vertices or *less*\n", + "- Standard GeoJSON FeatureCollection format.\n", + "- Must use WGS84/EPSG:4326 projection.\n", + "- Be 2 dimensional.\n", + "- Contain 1500 vertices or *less* per feature.\n", "\n", - "Full feature rules may be found [here](https://docs.planet.com/develop/apis/features/uploading-and-validating-features/#rules-for-creating-a-feature).\n", + "Full feature rules may be found [here](https://docs.planet.com/develop/apis/features/uploading-and-validating-features/#rules-for-creating-a-feature). \n", "

\n", - "Below we have defined a FeatureCollection of two areas in Cairo, Egypt. You may use this FeatureCollection to follow along with the tutorial, or import your own from a filepath." + "Below we have defined a FeatureCollection of two areas in Cairo, Egypt. You may use this FeatureCollection to follow along with the tutorial, define your own, or import one from a filepath of something like a geojson." ] }, { @@ -159,8 +146,7 @@ "You have a few options to define features to post to your Collections:\n", "- The most simple option is to use a predefined FeatureCollection, so that the Features API can separate each feature into individual items for you.\n", "- You may alternatively pass in just the geometries from feature collections. \n", - " - There is a free tool [geojson.io](https://geojson.io/) that will allow you to create geoetries and FeatureCollections by hand. You can also draw and download custom AOI geojson's from [Planet Explorer](https://www.planet.com/explorer/).\n", - "- You can read a geojson file from a local path, a snippet is provided below to do this. " + " - There is a free tool [geojson.io](https://geojson.io/) that will allow you to create geoetries and FeatureCollections by hand. You can also draw and download custom AOI geojson's from [Planet Explorer](https://www.planet.com/explorer/)." ] }, { @@ -168,12 +154,14 @@ "id": "ab8ef406", "metadata": {}, "source": [ - "Features can have properties and keys beyond just the `geometry` and the `id`. If a feature is uploaded without specifying the `property_id`, all properties of the feature are maintained in the item.\n" + "Features can have properties and keys beyond just the `geometry` and the `id`. If a feature is uploaded without specifying the `property_id`, all properties of the feature are maintained in the item.\n", + "\n", + "You can manually have your Feature's `id` be set with an 'id' key. You may also give you feature a display name in the Feature Manager web interface with a 'title' key in the features property. **Note** that when you upload a feature, it is not required to have a title. Your feature will still display without a 'Feature Name' in the [Features Manager](https://www.planet.com/features/) GUI, and is usable and accessible via its `id`. \n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "33202e83", "metadata": {}, "outputs": [], @@ -188,6 +176,7 @@ " \"id\": \"0\",\n", " \"type\": \"Feature\",\n", " \"properties\": {\n", + " \"title\": \"lake_area\",\n", " \"name_str\": \"area_1\",\n", " \"extra_property_1\": 'extra property 1 here',\n", " \"extra_property_2\": 'extra property 2 here'},\n", @@ -196,7 +185,11 @@ " {\n", " \"id\": \"1\",\n", " \"type\": \"Feature\",\n", - " \"properties\": {\"name_str\": \"area_2\"},\n", + " \"properties\": {\n", + " \"title\": \"institute_area\",\n", + " \"name_str\": \"area_2\",\n", + " \"extra_property_1\": 'extra property 1 here',\n", + " \"extra_property_2\": 'extra property 2 here'},\n", " \"geometry\": {\"coordinates\": institute_poly, \"type\": \"Polygon\"},\n", " },\n", "]}" @@ -209,14 +202,12 @@ "metadata": {}, "outputs": [], "source": [ - "# Read a geojson from a path\n", + "# Read your geojson path, then send it to a geo_dict.\n", "geojson_file = 'PATH TO YOUR GEOJSON FILE'\n", "\n", - "def load_geojson(filename):\n", - " with open(filename, 'r') as f:\n", - " return json.load(f)\n", + "gdf = gpd.read_file(geojson_file)\n", "\n", - "complex_geom = load_geojson(geojson_file)" + "local_fc = gdf.to_geo_dict()" ] }, { @@ -234,12 +225,12 @@ "source": [ "### Name and create your FeatureCollection in the Features API\n", "\n", - "Before you upload your FeatureCollection items, you need to create the Collection with the Features API. Set your `collection_title`, which will serve as the title or name of the whole FeatureCollection once its uploaded. You may optionally set a description for your Collection. Then we can create our new feature_collection using `pl.features.create_collection`, which returns the newly created Collections's `collection_id`. The `collection_id` will be used to add items to the Collection." + "Before you upload your FeatureCollection items, you need to create the Collection with the Features API. Set your `collection_title`, which will serve as the title or name of the whole FeatureCollection once its uploaded. You may optionally set a description for your Collection. Then we can create our new Collection using `pl.features.create_collection`, which returns the newly created Collections's `collection_id`. The `collection_id` is needed to add items to the Collection." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "9532483d", "metadata": {}, "outputs": [], @@ -250,7 +241,7 @@ }, { "cell_type": "code", - "execution_count": 187, + "execution_count": 7, "id": "5f925cb6", "metadata": {}, "outputs": [], @@ -260,17 +251,17 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 8, "id": "8db31a6c", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "'cairo_areas-proeNRv'" + "'cairo_areas-orELyjq'" ] }, - "execution_count": 120, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -286,24 +277,29 @@ "source": [ "### Post Features to your Collection\n", "\n", - "After the Collection is created with the Features API, you may post your features from the FeatureCollection we defined above. We will also set the `property_id` to use for the features. \n", + "After the Collection is created with the Features API, you may post your features from the FeatureCollection we defined above. \n", + "

\n", + "The `property_id` is optional, it refers to which key from the `properties` block of the supplied feature(s) to use in the `id` for the collection. This gives you some control of the identifier for items once they are in your collection. Note that an alphanumeric string will be added onto the end of the id even if you set the key to use.\n", "

\n", - "The `property_id` is optional, it refers to which key from the `properties` block of the supplied feature(s) to use as the name for the collection. This gives you control of the identifier for items once they are in your collection. Without the `property_id` specified, a random string is generated for you. The example FeatureColleciton uses the property `name_str`, so our features will be identified with the strings `area_1` and `area_2`." + "Without the `property_id` specified, if the features has an `id` key, that will be used as the `id` when you post your feature. Otherwise, a random string is generated for a features' id. \n", + "

\n", + "The example FeatureColleciton has some extra properties like `name_str` that can be used as the id, but will retain all keys if posted without a `property_id` key." ] }, { "cell_type": "code", - "execution_count": 183, + "execution_count": 9, "id": "d31c6418", "metadata": {}, "outputs": [], "source": [ - "property_id = 'name_str'" + "# Optional\n", + "property_id = None" ] }, { "cell_type": "code", - "execution_count": 188, + "execution_count": 10, "id": "4855833d", "metadata": {}, "outputs": [], @@ -313,18 +309,18 @@ }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 11, "id": "b10dafc8", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "['pl:features/my/cairo_areas-proeNRv/area_1-0KZLn6Z',\n", - " 'pl:features/my/cairo_areas-proeNRv/area_2-mWVX7Qr']" + "['pl:features/my/cairo_areas-orELyjq/0-m2z6LNQ',\n", + " 'pl:features/my/cairo_areas-orELyjq/1-o6DvKBY']" ] }, - "execution_count": 124, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -370,7 +366,43 @@ "execution_count": null, "id": "6fff398e", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[[{'id': 'cairo_areas-orELyjq',\n", + " 'title': 'cairo_areas',\n", + " 'description': 'FeatureCollection of two areas in Cario, Egypt',\n", + " 'item_type': 'feature',\n", + " 'created': '2025-12-12T18:27:21Z',\n", + " 'updated': '2025-12-12T18:34:58Z',\n", + " 'extent': {'spatial': {'bbox': [[31.23344423,\n", + " 29.99579775,\n", + " 31.26630889,\n", + " 30.06461941]]}},\n", + " 'links': [{'href': 'https://api.planet.com/features/v1/ogc/my/collections/cairo_areas-orELyjq',\n", + " 'rel': 'self',\n", + " 'title': 'This collection',\n", + " 'type': 'application/json'},\n", + " {'href': 'https://api.planet.com/features/v1/ogc/my/collections/cairo_areas-orELyjq/items',\n", + " 'rel': 'features',\n", + " 'title': 'Features',\n", + " 'type': 'application/json'}],\n", + " 'feature_count': 2,\n", + " 'area': 9890943.929543883,\n", + " 'title_property': None,\n", + " 'description_property': None,\n", + " 'properties': {},\n", + " 'permissions': {'can_write': True, 'shared': False, 'is_owner': True},\n", + " 'ownership': {'owner_id': '', 'org_id': ''},\n", + " 'pl:ref': 'pl:features/my/cairo_areas-orELyjq'}]]" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "collections = pl.features.list_collections()\n", "collections_list = [collection for collection in collections]\n", @@ -389,10 +421,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "id": "9c06b2a3", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[{'type': 'Feature',\n", + " 'id': '1',\n", + " 'properties': {'title': 'institute_area',\n", + " 'name_str': 'area_2',\n", + " 'extra_property_1': 'extra property 1 here',\n", + " 'extra_property_2': 'extra property 2 here',\n", + " 'pl:ref': 'pl:features/my/cairo_areas-orELyjq/1-o6DvKBY',\n", + " 'pl:area': 3104292.7715780144},\n", + " 'geometry': {'type': 'Polygon',\n", + " 'coordinates': [[[31.23344423, 30.05109414],\n", + " [31.25491533, 30.05109414],\n", + " [31.25491533, 30.06461941],\n", + " [31.23344423, 30.06461941],\n", + " [31.23344423, 30.05109414]]]}},\n", + " {'type': 'Feature',\n", + " 'id': '0',\n", + " 'properties': {'title': 'lake_area',\n", + " 'name_str': 'area_1',\n", + " 'extra_property_1': 'extra property 1 here',\n", + " 'extra_property_2': 'extra property 2 here',\n", + " 'pl:ref': 'pl:features/my/cairo_areas-orELyjq/0-m2z6LNQ',\n", + " 'pl:area': 6786651.157965869},\n", + " 'geometry': {'type': 'Polygon',\n", + " 'coordinates': [[[31.23905453, 29.99579775],\n", + " [31.26630889, 29.99579775],\n", + " [31.26630889, 30.01908084],\n", + " [31.23905453, 30.01908084],\n", + " [31.23905453, 29.99579775]]]}}]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "items = pl.features.list_items(new_collection_id)\n", "item_list = [item for item in items]\n", @@ -409,7 +479,7 @@ }, { "cell_type": "code", - "execution_count": 138, + "execution_count": 14, "id": "07788648", "metadata": {}, "outputs": [], @@ -420,10 +490,38 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "a69f7322", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'type': 'Feature',\n", + " 'id': '1',\n", + " 'properties': {'title': 'institute_area',\n", + " 'name_str': 'area_2',\n", + " 'extra_property_1': 'extra property 1 here',\n", + " 'extra_property_2': 'extra property 2 here',\n", + " 'pl:ref': 'pl:features/my/cairo_areas-orELyjq/1-o6DvKBY',\n", + " 'pl:area': 3104292.7715780144},\n", + " 'geometry': {'type': 'Polygon',\n", + " 'coordinates': [[[31.23344423, 30.05109414],\n", + " [31.25491533, 30.05109414],\n", + " [31.25491533, 30.06461941],\n", + " [31.23344423, 30.06461941],\n", + " [31.23344423, 30.05109414]]]},\n", + " 'links': [{'rel': 'self',\n", + " 'href': 'https://api.planet.com/features/v1/ogc/my/collections/cairo_areas-orELyjq/items/1-o6DvKBY'},\n", + " {'rel': 'collection',\n", + " 'href': 'https://api.planet.com/features/v1/ogc/my/collections/cairo_areas-orELyjq'}]}" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "pl.features.get_item(collection_id, feature_id)" ] @@ -443,7 +541,7 @@ "source": [ "### Deleting Collections\n", "\n", - "Using the `collection_id` you may delete a collection using `delete_collection()`. Be careful when you do this, as there is no confirmation output from this method. " + "Using the `collection_id` you may delete a collection using `delete_collection()`. Be careful when you do this, as there is no confirmation output from this method, and the collection will just be deleted." ] }, { @@ -472,27 +570,34 @@ "source": [ "# Using Feature References as input geometries\n", "\n", - "The `'pl:ref'` property of features is can be used in other Planet APIs to link to the geometry of the feature instead of having the user tracking bounding box and geometry variables. Planet `Subscriptions`, `Orders`, and `Data` APIs are all able to make use of these pl feature reference links. \n", + "The `'pl:ref'` property of a feature is used in other Planet APIs to link to the geometry of the feature instead of having the user track bounding box and geometry variables. Planet `Subscriptions`, `Orders`, and `Data` APIs are all able to make use of these pl feature reference links. \n", "\n", - "Pl ref ids are submitted in the `geometery` argument as a dictionary: `{'type': 'ref', 'content': 'pl:features/my//'}`\n", + "PL ref links are submitted in the `geometry` argument in API calls as a dictionary: \n", + "```python\n", + "geometry = {\n", + " 'type': 'ref',\n", + " 'content': 'pl:features/my//'\n", + "}\n", + "```\n", "\n", - "Below provided is consilidated code to use the SDK to get a `pl:ref` id from the features API, and then input that id into an order_request. If you are unfamiliar with the mechanics of the `order_request` functions of the SDK, there is an `` that will walk you through how to use the Planet Python SDK to create and submit Orders.\n", - "\n", - "as well as adding it to a request body for a `request` call. These snippets can be copied into other workflows or scripts you may have to utilize the Features API." + "Provided below is consolidated code to use the SDK to get a `pl:ref` id from the features API, and then input that id into an order request. If you are unfamiliar with the mechanics of the `order_request` functions of the SDK, there is a full notebook, `planet_sdk_orders_demo.ipynb` located in `api_guids/orders_api/` that will walk you through how to use the Planet Python SDK to create and submit Orders." ] }, { "cell_type": "code", - "execution_count": 150, + "execution_count": 29, "id": "c70b7db8", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "pl:features/my/cairo_areas-proeNRv/area_1-0KZLn6Z\n" - ] + "data": { + "text/plain": [ + "{'type': 'ref', 'content': 'pl:features/my/cairo_areas-orELyjq/0-m2z6LNQ'}" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ @@ -508,7 +613,8 @@ "\n", "collection_items = list(pl.features.list_items(collection_id))\n", "feature_ref = collection_items[1]['properties']['pl:ref']\n", - "print(feature_ref)" + "geometry = {'type': 'ref', 'content': feature_ref}\n", + "geometry" ] }, { @@ -518,43 +624,97 @@ "metadata": {}, "outputs": [], "source": [ + "# This example item_id can be used to order using the example FeatureCollection given.\n", + "# You will get an error if you tried to order this item with a different feature.\n", + "item_ids = ['20220501_074038_04_2420']\n", + "product_bundle = 'analytic_8b_udm2'\n", + "item_type = \"PSScene\"\n", + "fallback_bundle = 'analytic_udm2,analytic_3b_udm2'\n", "\n", - "\n", - "prod = planet.order_request.product()\n", - "tools = [planet.order_request.clip_tool(feature_ref)]" + "prod = order_request.product(item_ids, product_bundle, item_type, fallback_bundle)\n", + "tools = [order_request.clip_tool(geometry)]" ] }, { "cell_type": "code", - "execution_count": 158, + "execution_count": 64, "id": "8040f22f", "metadata": {}, "outputs": [], "source": [ - "name = 'test_cairo_order'\n", + "name = 'geometry_ref_order'\n", + "products = [prod]\n", "\n", - "\n", - "order_made = planet.order_request.build_request(name, prod, tools=tools)" + "order_made = planet.order_request.build_request(name, products, tools=tools)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 65, "id": "0e699260", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'geometry_ref_order',\n", + " 'products': [{'item_ids': ['20220501_074038_04_2420'],\n", + " 'item_type': 'PSScene',\n", + " 'product_bundle': 'analytic_8b_udm2,analytic_udm2,analytic_3b_udm2'}],\n", + " 'tools': [{'clip': {'aoi': {'type': 'ref',\n", + " 'content': 'pl:features/my/cairo_areas-orELyjq/0-m2z6LNQ'}}}]}" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "order_made" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 66, "id": "baf6af91", "metadata": {}, "outputs": [], "source": [ - "pl.orders.create_order?" + "order = pl.orders.create_order(request=order_made)" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "id": "3a4f2a85", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'_links': {'_self': 'https://api.planet.com/compute/ops/orders/v2/8d83f77c-e6f2-4699-9662-eb4a10b47647'},\n", + " 'created_on': '2025-12-12T19:20:44.501167Z',\n", + " 'error_hints': [],\n", + " 'id': '8d83f77c-e6f2-4699-9662-eb4a10b47647',\n", + " 'last_message': 'Preparing order',\n", + " 'last_modified': '2025-12-12T19:20:44.501167Z',\n", + " 'name': 'geometry_ref_order',\n", + " 'products': [{'item_ids': ['20220501_074038_04_2420'],\n", + " 'item_type': 'PSScene',\n", + " 'product_bundle': 'analytic_8b_udm2'}],\n", + " 'state': 'queued',\n", + " 'tools': [{'clip': {'aoi': {'content': 'pl:features/my/cairo_areas-orELyjq/0-m2z6LNQ',\n", + " 'type': 'ref'}}}]}" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "order" ] } ],