Search & Download with the Data API

A quick walkthrough of searching & downloading Planet data using the public Data API

Getting started with the Data API

Let's search & download some imagery of farmland near Stockton, CA. Here are the steps we'll follow:

  1. Define an Area of Interest (AOI)
  2. Save our AOI's coordinates to GeoJSON format
  3. Create a few search filters
  4. Search for imagery using those filters
  5. Activate an image for downloading
  6. Download an image

Requirements

Define an Area of Interest

An Area of Interest (or AOI) is how we define the geographic "window" out of which we want to get data.

For the Data API, this could be a simple bounding box with four corners, or a more complex shape, as long as the definition is in GeoJSON format.

For this example, let's just use a simple box. To make it easy, I'll use geojson.io to quickly draw a shape & generate GeoJSON output for our box:

geojsonio.png

We only need the "geometry" object for our Data API request:

In [ ]:
# Stockton, CA bounding box (created via geojson.io) 
geojson_geometry = {
  "type": "Polygon",
  "coordinates": [
    [ 
      [-121.59290313720705, 37.93444993515032],
      [-121.27017974853516, 37.93444993515032],
      [-121.27017974853516, 38.065932950547484],
      [-121.59290313720705, 38.065932950547484],
      [-121.59290313720705, 37.93444993515032]
    ]
  ]
}

Create Filters

Now let's set up some filters to further constrain our Data API search:

In [ ]:
# get images that overlap with our AOI 
geometry_filter = {
  "type": "GeometryFilter",
  "field_name": "geometry",
  "config": geojson_geometry
}

# get images acquired within a date range
date_range_filter = {
  "type": "DateRangeFilter",
  "field_name": "acquired",
  "config": {
    "gte": "2016-08-31T00:00:00.000Z",
    "lte": "2016-09-01T00:00:00.000Z"
  }
}

# only get images which have <50% cloud coverage
cloud_cover_filter = {
  "type": "RangeFilter",
  "field_name": "cloud_cover",
  "config": {
    "lte": 0.5
  }
}

# combine our geo, date, cloud filters
combined_filter = {
  "type": "AndFilter",
  "config": [geometry_filter, date_range_filter, cloud_cover_filter]
}

Searching: Items and Assets

Planet's products are categorized as items and assets: an item is a single picture taken by a satellite at a certain time. Items have multiple asset types including the image in different formats, along with supporting metadata files.

For this demonstration, let's get a satellite image that is best suited for visual applications; e.g., basemaps or visual analysis. Since we're not doing any spectral analysis outside of the visual range, we only need a 3-band (RGB) image. To get the image we want, we will specify an item type of PSScene3Band, and asset type visual.

You can learn more about item & asset types in Planet's Data API here.

Now let's search for all the items that match our filters:

In [ ]:
import os
import json
import requests
from requests.auth import HTTPBasicAuth

# API Key stored as an env variable
PLANET_API_KEY = os.getenv('PL_API_KEY')

item_type = "PSScene3Band"

# API request object
search_request = {
  "interval": "day",
  "item_types": [item_type], 
  "filter": combined_filter
}

# fire off the POST request
search_result = \
  requests.post(
    'https://api.planet.com/data/v1/quick-search',
    auth=HTTPBasicAuth(PLANET_API_KEY, ''),
    json=search_request)

print(json.dumps(search_result.json(), indent=1))

Our search returns metadata for all of the images within our AOI that match our date range and cloud coverage filters. It looks like there are multiple images here; let's extract a list of just those image IDs:

In [ ]:
# extract image IDs only
image_ids = [feature['id'] for feature in search_result.json()['features']]
print(image_ids)

Since we just want a single image, and this is only a demonstration, for our purposes here we can arbitrarily select the first image in that list. Let's do that, and get the asset list available for that image:

In [ ]:
# For demo purposes, just grab the first image ID
id0 = image_ids[0]
id0_url = 'https://api.planet.com/data/v1/item-types/{}/items/{}/assets'.format(item_type, id0)

# Returns JSON metadata for assets in this ID. Learn more: planet.com/docs/reference/data-api/items-assets/#asset
result = \
  requests.get(
    id0_url,
    auth=HTTPBasicAuth(PLANET_API_KEY, '')
  )

# List of asset types available for this particular satellite image
print(result.json().keys())

Activation and Downloading

The Data API does not pre-generate assets, so they are not always immediately availiable to download. In order to download an asset, we first have to activate it.

Remember, earlier we decided we wanted a color-corrected image best suited for visual applications. We can check the status of the visual asset we want to download like so:

In [ ]:
# This is "inactive" if the "visual" asset has not yet been activated; otherwise 'active'
print(result.json()['visual']['status'])

Let's now go ahead and activate that asset for download:

In [ ]:
# Parse out useful links
links = result.json()[u"visual"]["_links"]
self_link = links["_self"]
activation_link = links["activate"]

# Request activation of the 'visual' asset:
activate_result = \
  requests.get(
    activation_link,
    auth=HTTPBasicAuth(PLANET_API_KEY, '')
  )

At this point, we wait for the activation status for the asset we are requesting to change from inactive to active. We can monitor this by polling the "status" of the asset:

In [ ]:
activation_status_result = \
  requests.get(
    self_link,
    auth=HTTPBasicAuth(PLANET_API_KEY, '')
  )
    
print(activation_status_result.json()["status"])

Once the asset has finished activating (status is "active"), we can download it.

Note: the download link on an active asset is temporary

In [ ]:
# Image can be downloaded by making a GET with your Planet API key, from here:
download_link = activation_status_result.json()["location"]
print(download_link)

stockton_thumb.png