Skip to content

Instantly share code, notes, and snippets.

@stijnvanhoey
Last active October 25, 2021 14:39
Show Gist options
  • Save stijnvanhoey/f7522feec20de928801f31373f571008 to your computer and use it in GitHub Desktop.
Save stijnvanhoey/f7522feec20de928801f31373f571008 to your computer and use it in GitHub Desktop.
Activate and download assets from Planet Labs API
library(glue)
library(httr)
library(tidyverse)
library(jsonlite)
#' Search for planet labs images
#'
#' Credits to https://www.lentilcurtain.com/posts/accessing-planet-labs-data-api-from-r/
#'
#' @param bbox bounding box
#' @param date_end date of last image (date)
#' @param date_start date of first image (date)
#' @param cloud_lim cloud cover limit (double)
#' @param cover_lim type of image (string)
#' @param item_name char,
#' @param api_key char, planet labs API Key (to request)
#'
#' @return
#' @export
#'
#' @examples
planet_search_asset <- function(bbox, date_end, date_start,
cloud_lim=0.2, cover_lim=0.5,
item_name="PSScene4Band",
api_key) {
#convert shapefile to geojson
#shapefile of bounding box must be EPSG:4326 Projection
geo_json_geometry <- list(
type = jsonlite::unbox("Polygon"),
coordinates = list(list(
c(bbox@xmin,
bbox@ymin),
c(bbox@xmin,
bbox@ymax),
c(bbox@xmax,
bbox@ymax),
c(bbox@xmax,
bbox@ymin),
c(bbox@xmin,
bbox@ymin)
))
)
# filter for items the overlap with our chosen geometry
geometry_filter <- list(
type = jsonlite::unbox("GeometryFilter"),
field_name = jsonlite::unbox("geometry"),
config = geo_json_geometry
)
# we will search for images for up to a month before the date
# are interested in
dategte <- paste0(date_start, "T00:00:00.000Z")
datelte <- paste0(date_end, "T00:00:00.000Z")
# filter images by daterange
date_range_filter <- list(
type = jsonlite::unbox("DateRangeFilter"),
field_name = jsonlite::unbox("acquired"),
config = list(
gte = jsonlite::unbox(dategte),
lte = jsonlite::unbox(datelte))
)
# filter by cloud cover
cloud_cover_filter <- list(
type = jsonlite::unbox("RangeFilter"),
field_name = jsonlite::unbox("cloud_cover"),
config = list(
lte = jsonlite::unbox(cloud_lim))
)
# filter by coverage of bounding box
coverage_filter <- list(
type = jsonlite::unbox("RangeFilter"),
field_name = unbox("usable_data"),
config = list(
gte = jsonlite::unbox(cover_lim))
)
# combine filters
filter_configs <- list(
type = jsonlite::unbox("AndFilter"),
config = list(date_range_filter, cloud_cover_filter,
geometry_filter, coverage_filter)
)
#build request
search_endpoint_request <- list(
item_types = item_name,
filter = filter_configs
)
#convert request to JSON
body_json <- jsonlite::toJSON(search_endpoint_request, pretty = TRUE)
#API request config
url <- 'https://api.planet.com/data/v1/quick-search'
body <- body_json
#send API request
request <- httr::POST(url, body = body_json, content_type_json(),
authenticate(api_key, ""))
#get request content
response <- httr::content(request)
return(response)
}
#' Setup api key header for planet API calls
#'
#' @param api_key char key received from planet labs
#'
#' @return formatted authentification header
#'
#' @examples
#' api_key <- "THIS_IS_YOUR_KEY"
#' planet_translate_key(api_key)
planet_translate_key <- function(api_key) {
glue::glue("api-key {api_key}")
}
#' Check current activity of an asset
#'
#' Can be used to check the current activity of the asset or to get the
#' activiation link if not yet active.
#'
#' @param itemtype char, type of asset to download, e.g. 'PSOrthoTile'
#' @param item_ID char, planet labs asset ID
#' @param api_key char key received from planet labs
#'
#' @return content of response
#' @export
#'
#' @examples
#' planet_activate_asset("PSOrthoTile",
#' "846486_3162616_2017-10-18_0c81",
#' "YOUR_KEY_FROM_PLANET_LABS")
planet_get_item <- function(itemtype, item_ID, api_key) {
url <- glue::glue("https://api.planet.com/data/v1/item-types/{itemtype}/items/{item_ID}/assets/")
response <- GET(url,
add_headers(Authorization = planet_translate_key(api_key)),
config("location"))
return(content(response))
}
#' Activate an asset of planet labs
#'
#' @param itemtype char, type of asset to download, e.g. 'PSOrthoTile'
#' @param item_ID char, planet labs asset ID
#' @param api_key char key received from planet labs
#' @param asset_type char, type of asset to download, e.g. visual, analytic
#'
#' @return response of the activation
#'
#' @examples
planet_activate_asset <- function(itemtype, item_ID, api_key,
asset_type = "visual") {
response <- planet_get_item(itemtype, item_ID, api_key)
activation_link <- response[[asset_type]][["_links"]][["activate"]]
# Activate the asset with activation link
post_response <- POST(activation_link,
add_headers(Authorization = planet_translate_key(api_key)))
return(post_response)
}
#' Download asset after activation
#'
#' @param itemtype char, type of asset to download, e.g. 'PSOrthoTile'
#' @param item_ID char, planet labs asset ID
#' @param api_key char, key received from planet labs
#' @param fpath char, local file path to store downloaded images
#' @param asset_type char, type of asset to download, e.g. visual, analytic
#'
#' @examples
#' planet_download_asset("PSOrthoTile",
#' "846486_3162616_2017-10-18_0c81",
#' "YOUR_KEY_FROM_PLANET_LABS")
planet_download_asset <- function(itemtype, item_ID, api_key,
fpath = "data",
asset_type = "visual") {
# wait until ativated
response <- planet_get_item(itemtype, item_ID, api_key)
current_status <- response$visual$status
while (current_status != "active") {
message("Not yet activated, waiting...")
Sys.sleep(2)
response <- planet_get_item(itemtype, item_ID, api_key)
current_status <- response$visual$status
}
response <- planet_get_item(itemtype, item_ID, api_key)
image_url <- response[[asset_type]]$location
# download
message("Downloading...")
GET(image_url,
write_disk(file.path(fpath, glue::glue("{itemtype}_{item_ID}.tif"))),
overwrite = TRUE)
message("... done with image", glue::glue("{itemtype}_{item_ID}.tif"))
}
library(tidyverse)
library(httr)
library(sp)
library(raster)
library(rgdal)
# load the functionalities
source('src/planet_labs_api.R')
# YOUR API KEY FROM PLANET LABS
api_key <- "YOUR_PLANET_API_KEY" # keep secret!
# search assets
date_start <- "2010-07-01"
date_end <- "2018-08-01"
studysite <- readOGR("data/BE2300005-1 perimeter_WGS84.shp")
bbox_ss <- extent(bbox(studysite))
response <- planet_search_asset(bbox_ss, date_end, date_start,
item_name = "PSOrthoTile",
api_key = api_key)
# create dataframe of assets properties and id
items <- map_df(response$features, ~.x$properties) %>%
mutate(item_id = map_chr(response$features, ~.x$id))
#--------------
# example case:
#--------------
item_ID <- items %>% top_n(1) %>% pull(item_id)
itemtype <- "PSOrthoTile"
asset_type <- "visual"
planet_activate_asset(itemtype, item_ID, api_key, asset_type)
planet_download_asset(itemtype, item_ID, api_key, asset_type)
#------------------------
# apply on list of assets
#------------------------
# Read csv with ItemID
planet_items <- items %>%
select(item_type, item_id) %>%
head(2) # for the example, I'm just using 2 of them
# activate all assets
map2(planet_items$item_type, planet_items$item_id,
planet_activate_asset, api_key, asset_type)
# download all assets
map2(planet_items$item_type, planet_items$item_id,
planet_download_asset, api_key, asset_type)
acquired anomalous_pixels black_fill cloud_cover columns epsg_code grid_cell ground_control gsd item_type origin_x origin_y pixel_resolution provider published rows satellite_id strip_id sun_azimuth sun_elevation updated usable_data view_angle item_id
2018-07-25T10:07:58.892915Z 0.14 0.3 0.136 8000 32631 3162616 TRUE 4 PSOrthoTile 523500 5664500 3.125 planetscope 2018-10-24T17:56:53Z 8000 1105 1587476 137 52.4 2018-10-25T21:24:39Z 0.56 0.1 1587476_3162616_2018-07-25_1105
2018-07-25T10:07:55.647572Z 0.15 0.07 0.153 8000 32631 3162716 TRUE 4 PSOrthoTile 523500 5688500 3.125 planetscope 2018-10-24T17:56:52Z 8000 1105 1587476 137.3 52.3 2018-10-25T21:24:28Z 0.77 0.1 1587476_3162716_2018-07-25_1105
2018-05-11T10:03:29.86267Z 0.01 0.48 0.009 8000 32631 3162716 TRUE 4 PSOrthoTile 523500 5688500 3.125 planetscope 2018-10-24T17:16:33Z 8000 1105 1418103 140.3 51.3 2018-10-25T21:51:23Z 0.51 0 1418103_3162716_2018-05-11_1105
2018-07-26T10:15:05.89996Z 0.02 0.24 0.02 8000 32631 3162616 TRUE 3.9 PSOrthoTile 523500 5664500 3.125 planetscope 2018-07-26T15:00:09Z 8000 100c 1589140 139.6 53 2018-07-27T06:28:57Z 0.74 0.1 1589140_3162616_2018-07-26_100c
2018-07-26T10:15:02.734309Z 0.02 0.05 0.019 8000 32631 3162716 TRUE 3.9 PSOrthoTile 523500 5688500 3.125 planetscope 2018-07-26T14:59:57Z 8000 100c 1589140 139.9 52.9 2018-07-27T06:28:50Z 0.93 0.1 1589140_3162716_2018-07-26_100c
2018-07-25T10:14:34.614106Z 0.01 0.17 0.008 8000 32631 3162616 TRUE 3.9 PSOrthoTile 523500 5664500 3.125 planetscope 2018-07-25T15:04:13Z 8000 1033 1587321 139.5 53.2 2018-07-26T06:53:32Z 0.82 1.5 1587321_3162616_2018-07-25_1033
@Leprechault
Copy link

@stijnvanhoey, please, I get a new API key (Planet support confirm that's my API key is OK), update the code and the problem continues to happen and my output is:

item_ID <- "1563545_2229401_2018-07-13_0f49"
item_ID
#[1] "1563545_2229401_2018-07-13_0f49"
itemtype <- "PSOrthoTile"
 asset_type <- "analytic"
 
planet_get_item <-  function(itemtype, item_ID, api_key) {
     url <- glue::glue("https://api.planet.com/data/v1/item-types/{itemtype}/items/{item_ID}/assets/")
     response <- GET(url,
                     add_headers(Authorization = planet_translate_key(api_key)),
                     config("location"))
     return(content(response))
 }
#planet_get_item(itemtype, item_ID, api_key)
planet_activate_asset(itemtype, item_ID, api_key, asset_type)
Response [https://api.planet.com/data/v1/assets/eyJpIjogIjE1NjM1NDVfMjIyOTQwMV8yMDE4LTA3LTEzXzBmNDkiLCAiYyI6ICJQU09ydGhvVGlsZSIsICJ0IjogImFuYWx5dGljIiwgImN0IjogIml0ZW0tdHlwZSJ9/activate]
  Date: 2021-10-22 15:01
  Status: 204
  Content-Type: <unknown>
<EMPTY BODY>
planet_download_asset(itemtype, item_ID, api_key, asset_type)
Not yet activated, waiting...
Not yet activated, waiting...
Not yet activated, waiting...
Not yet activated, waiting...
Not yet activated, waiting...
Not yet activated, waiting...

@stijnvanhoey
Copy link
Author

@Leprechault It takes indeed a while, but I'm still able to download the data using the code, e.g. for the example from the gist:

item_ID <- "1587476_3162616_2018-07-25_1105"
itemtype <- "PSOrthoTile"
asset_type <- "visual"

planet_activate_asset(itemtype, item_ID, api_key, asset_type)
planet_download_asset(itemtype, item_ID, api_key, asset_type)

works, if a subfolder visual does exists in the folder to write the data in. The activation took around 4 minutes for this example. This makes me think the code/API is still working fine and I would check at Planet why the activation of the item "1563545_2229401_2018-07-13_0f49" is taking so long (I tested for 10minutes without succes; but I did not get a time out)? Otherwise, maybe give https://github.com/bevingtona/planetR a try and check if the activation works using that R wrapper?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment