This notebook demonstrates using tools and toolchains when ordering with the orders api. Specifically, this notebook demonstrates the following toolchains:
For background on ordering and downloading with the orders api, see the Ordering and Delivery notebook.
Reference information can be found at Tools & toolchains.
In [1]:
import json
import os
import pathlib
import time
import numpy as np
import rasterio
from rasterio.plot import show
import requests
from requests.auth import HTTPBasicAuth
In [2]:
# API Key stored as an env variable
PLANET_API_KEY = os.getenv('PL_API_KEY')
orders_url = 'https://api.planet.com/compute/ops/orders/v2'
# set up requests to work with api
auth = HTTPBasicAuth(PLANET_API_KEY, '')
headers = {'content-type': 'application/json'}
In [3]:
# define products part of order
single_product = [
{
"item_ids": ["20151119_025740_0c74"],
"item_type": "PSScene4Band",
"product_bundle": "analytic"
}
]
same_src_products = [
{
"item_ids": ["20151119_025740_0c74",
"20151119_025739_0c74"],
"item_type": "PSScene4Band",
"product_bundle": "analytic"
}
]
multi_src_products = [
{
"item_ids": ["20151119_025740_0c74"],
"item_type": "PSScene4Band",
"product_bundle": "analytic"
},
{
"item_ids": ["LC81330492015320LGN01"],
"item_type": "Landsat8L1G",
"product_bundle": "analytic"
},
]
In [4]:
# define helpful functions for submitting, polling, and downloading an order
def place_order(request, auth):
response = requests.post(orders_url, data=json.dumps(request), auth=auth, headers=headers)
print(response)
if not response.ok:
raise Exception(response.content)
order_id = response.json()['id']
print(order_id)
order_url = orders_url + '/' + order_id
return order_url
def poll_for_success(order_url, auth, num_loops=50):
count = 0
while(count < num_loops):
count += 1
r = requests.get(order_url, auth=auth)
response = r.json()
state = response['state']
print(state)
success_states = ['success', 'partial']
if state == 'failed':
raise Exception(response)
elif state in success_states:
break
time.sleep(10)
def download_order(order_url, auth, overwrite=False):
r = requests.get(order_url, auth=auth)
print(r)
response = r.json()
results = response['_links']['results']
results_urls = [r['location'] for r in results]
results_names = [r['name'] for r in results]
results_paths = [pathlib.Path(os.path.join('data', n)) for n in results_names]
print('{} items to download'.format(len(results_urls)))
for url, name, path in zip(results_urls, results_names, results_paths):
if overwrite or not path.exists():
print('downloading {} to {}'.format(name, path))
r = requests.get(url, allow_redirects=True)
path.parent.mkdir(parents=True, exist_ok=True)
open(path, 'wb').write(r.content)
else:
print('{} already exists, skipping {}'.format(path, name))
return dict(zip(results_names, results_paths))
In [5]:
# define helpful functions for visualizing downloaded imagery
def show_rgb(img_file):
with rasterio.open(img_file) as src:
b,g,r,n = src.read()
rgb = np.stack((r,g,b), axis=0)
show(rgb/rgb.max())
def show_gray(img_file):
with rasterio.open(img_file) as src:
g = src.read(1)
show(g/g.max())
In [6]:
request = {
"name": "no processing",
"products": single_product,
}
In [7]:
# allow for caching, replace this with your image file
img_file = 'data/50df6201-ea94-48f1-bec9-65dd9cd8354b/1/files/PSScene4Band/20151119_025740_0c74/analytic/20151119_025740_0c74_3B_AnalyticMS.tif'
img_file
Out[7]:
In [8]:
if not os.path.isfile(img_file):
order_url = place_order(request, auth)
poll_for_success(order_url, auth)
downloaded_files = download_order(order_url, auth)
img_file = next(downloaded_files[d] for d in downloaded_files
if d.endswith('_3B_AnalyticMS.tif'))
print(img_file)
In [9]:
clip_aoi = {
"type":"Polygon",
"coordinates":[[[94.81858044862747,15.858073043526062],
[94.86242249608041,15.858073043526062],
[94.86242249608041,15.894323164978303],
[94.81858044862747,15.894323164978303],
[94.81858044862747,15.858073043526062]]]
}
In [10]:
# define the clip tool
clip = {
"clip": {
"aoi": clip_aoi
}
}
In [11]:
# create an order request with the clipping tool
request_clip = {
"name": "just clip",
"products": single_product,
"tools": [clip]
}
request_clip
Out[11]:
In [12]:
# allow for caching so we don't always run clip
run_clip = True
clip_img_file = 'data/6c23f9e1-d86a-47fe-9eb2-2693196168e0/1/files/20151119_025740_0c74_3B_AnalyticMS_clip.tif'
if os.path.isfile(clip_img_file): run_clip = False
In [13]:
if run_clip:
clip_order_url = place_order(request_clip, auth)
poll_for_success(clip_order_url, auth)
downloaded_clip_files = download_order(clip_order_url, auth)
clip_img_file = next(downloaded_clip_files[d] for d in downloaded_clip_files
if d.endswith('_3B_AnalyticMS_clip.tif'))
clip_img_file
Out[13]:
In [14]:
show_rgb(img_file)
show_rgb(clip_img_file)
Alright! Clipping worked. Beautiful! This saves a lot of download bandwidth and processing on our end.
In [15]:
bandmath = {
"bandmath": {
"pixel_type": "32R",
"b1": "(b4 - b3) / (b4 + b3)"
}
}
In [16]:
bandmath_request = {
"name": "band math",
"products": single_product,
"tools": [bandmath]
}
bandmath_request
Out[16]:
In [17]:
run_bandmath = True
# allow for caching so we don't always run clip
bandmath_img_file = 'data/06a733ff-3034-4ffe-b1c4-8f5d004feaff/1/files/20151119_025740_0c74_3B_AnalyticMS_bandmath.tif'
if os.path.isfile(bandmath_img_file): run_bandmath = False
In [18]:
if run_bandmath:
bandmath_order_url = place_order(bandmath_request, auth)
poll_for_success(bandmath_order_url, auth)
downloaded_bandmath_files = download_order(bandmath_order_url, auth)
bandmath_img_file = next(downloaded_bandmath_files[d] for d in downloaded_bandmath_files
if d.endswith('_bandmath.tif'))
bandmath_img_file
Out[18]:
In [19]:
show_rgb(img_file)
show_gray(bandmath_img_file)
This is a beautiful NDVI image!
In [20]:
toar = {
"toar": {
"scale_factor": 10000
}
}
toar_request = {
"name": "toar",
"products": single_product,
"tools": [toar]
}
toar_request
Out[20]:
In [21]:
run_toar = True
# allow for caching so we don't always run clip
toar_img_file = 'data/d22684ab-49b5-4696-ab9c-d0a893e5c193/1/files/20151119_025740_0c74_3B_AnalyticMS_toar.tif'
if os.path.isfile(toar_img_file): run_toar = False
In [22]:
if run_toar:
toar_order_url = place_order(toar_request, auth)
poll_for_success(toar_order_url, auth)
downloaded_toar_files = download_order(toar_order_url, auth)
toar_img_file = next(downloaded_toar_files[d] for d in downloaded_toar_files
if d.endswith('_toar.tif'))
toar_img_file
Out[22]:
In [23]:
show_rgb(img_file)
show_rgb(toar_img_file)
The effect of conversion to reflectance isn't really apparent in one image. But when comparing two images taken at different times of the year, the conversion to reflectance will remove effects of the sun's location and make the images more similar.
In [24]:
composite = {
"composite":{
}
}
composite_request = {
"name": "composite",
"products": same_src_products,
"tools": [composite]
}
composite_request
Out[24]:
In [25]:
run_composite = True
composite_file = 'data/be20feb1-b084-43b9-9c1c-b1d4a1ffb9fc/1/files/composite.tif'
if os.path.isfile(composite_file): run_composite = False
In [26]:
if run_composite:
composite_order_url = place_order(composite_request, auth)
poll_for_success(composite_order_url, auth)
downloaded_composite_files = download_order(composite_order_url, auth)
composite_file = next(downloaded_composite_files[d] for d in downloaded_composite_files
if d.endswith('composite.tif'))
composite_file
Out[26]:
In [27]:
show_rgb(composite_file)
Beautiful! The images have been combined into one seamless image.
This toolchain demonstrates how we can combine the clipping tool with the NDVI band math tool to only process and download the NDVI values for pixels we are interested in. Combining tools is as simple as combining the tool definitions in a list in the order request.
In [32]:
clip_bandmath_request = {
"name": "clip and bandmath",
"products": single_product,
"tools": [clip, bandmath]
}
clip_bandmath_request
Out[32]:
In [33]:
run_clip_bandmath = True
clip_bandmath_file = 'data/d1a94528-c1c3-4fe2-b3e5-6ff0c340a1d6/1/files/20151119_025740_0c74_3B_AnalyticMS_clip_bandmath.tif'
if os.path.isfile(clip_bandmath_file): run_clip_bandmath = False
In [34]:
if run_clip_bandmath:
clip_bandmath_order_url = place_order(clip_bandmath_request, auth)
poll_for_success(clip_bandmath_order_url, auth)
downloaded_clip_bandmath_files = download_order(clip_bandmath_order_url, auth)
clip_bandmath_file = next(downloaded_clip_bandmath_files[d] for d in downloaded_clip_bandmath_files
if d.endswith('_clip_bandmath.tif'))
clip_bandmath_file
Out[34]:
In [35]:
show_gray(clip_bandmath_file)
It is easy to add tools into a toolchain and saves a 'ton' of unnecessary pixel downloads and processing on our end. Big win!
In [36]:
reproject = {
"reproject": {
"projection": "WGS84",
"kernel": "cubic"
}
}
tile = {
"tile": {
"tile_size": 1232,
"origin_x": -180,
"origin_y": -90,
"pixel_size": 0.000027056277056,
"name_template": "C1232_30_30_{tilex:04d}_{tiley:04d}"
}
}
In [38]:
trt_request = {
"name": "toar reproject tile",
"products": single_product,
"tools": [toar, reproject, tile]
}
trt_request
Out[38]:
In [45]:
run_trt = True
tile_files = [
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8243_3175.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8242_3175.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8247_3178.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8243_3174.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8244_3174.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8244_3177.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8243_3176.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8247_3177.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8242_3176.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8244_3175.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8246_3177.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8245_3175.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8244_3176.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8245_3177.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8246_3178.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8245_3176.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8245_3178.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8246_3176.tif',
'data/8482721f-7711-49f6-8e12-6874c6decb38/1/files/C1232_30_30_8246_3179.tif'
]
if os.path.isfile(tile_files[0]): run_trt = False
In [46]:
if run_trt:
trt_order_url = place_order(trt_request, auth)
poll_for_success(trt_order_url, auth)
downloaded_trt_files = download_order(trt_order_url, auth)
tile_files = list(d for d in downloaded_trt_files.values()
if d.name.startswith('C1232_30_30_'))
for f in tile_files:
print(f)
In [47]:
for f in tile_files[:4]:
show_rgb(f)
In [49]:
test_file = tile_files[0]
In [50]:
!gdalinfo $test_file
The files have indeed been reprojected to WSG 84 and tiled. It is that easy!