This vignette shows the functionality of the PlanetNICFI R package based on a change detection use case. The official website of NICFI (Norway’s International Climate and Forest Initiative) includes all the details about the initiative against global deforestation. This initiative was also covered extensively on the web especially from the provider of the free Satellite Imagery. Users have the opportunity to download high-resolution imagery of forests globally using a simple sign up form.
To take advantage of the PlanetNICFI R package you will need also an API key which you can receive once you are registered. For more details see the Getting Started with Planet APIs website.
The Documentation of the R package includes details on how to download and process monthly data for an Area of Interest (AOI), however, in this vignette I’ll use bi-annual data because they are available since 2015 whereas monthly data is available since September 2020.
To spot deforestation areas we will perform change detection based on a Sugarcane cultivation area in Bolivia. This use case is not new and there is also a small YouTube video that shows the change of the area over time (from 2016 to 2018). For this change detection task we will use two images:
and the following Well Known Text (WKT) as a cropped area utilizing geojson.io,
= system.file('data_files/Sugar_Cane_Bolivia.wkt', package = "PlanetNICFI")
wkt_file = readLines(wkt_file, warn = FALSE)
WKT WKT
First, we have to create a variable and store the API key (which a user receives as explained previously),
= 'use_your_planet_nicfi_API_key_here' api_key
Then we have to extract the bi-annual mosaics
require(PlanetNICFI)
= nicfi_mosaics(planet_api_key = api_key,
mosaic_files type = 'bi_annually',
crs_bbox = 4326,
URL = 'https://api.planet.com/basemaps/v1/mosaics',
verbose = FALSE)
= mosaic_files$dtbl_mosaic
dtbl colnames(dtbl)
The output of the nicfi_mosaics function is a named list of length 2 and we will keep the data.table (dtbl_mosaic) which includes the mosaic id’s and the bi-annual Dates (among other columns) as the previous and the next output shows,
= c('id', 'first_acquired', 'last_acquired')
cols_keep = dtbl[, ..cols_keep]
dtbl_keep dtbl_keep
Then we will extract the mosaic Quads of the images for years 2016 and 2018. The Planet Web API for the NICFI data includes both Quads (pixel images of size 4096 x 4096) and tiles (pixel images of size 256 x 256 - see Planet-Tile-Streaming) for the global forest areas.
The PlanetNICFI R package makes use of Quads because it allows the user to extract a specified Area of Interest (AOI) based on a bounding box, as I’ll explain later in this vignette.
In the previous subset dtbl_keep data.table we see that
Therefore, we will first extract the first 10 pages for the Quads for the year 2016 (because a maximum of 10 pages is sufficient and will cover our AOI),
= 1
index_year_2016 = dtbl_keep$id[index_year_2016]
mosaic_ID_2016
= nicfi_quads_bbox(planet_api_key = api_key,
quad_files mosaic_id = mosaic_ID_2016,
bbox_AOI = NULL,
wkt_AOI = WKT,
page_size = 10,
crs_bbox = 4326,
verbose = FALSE)
= quad_files$quads
dtbl_quads colnames(dtbl_quads)
In the same way as with the mosaics, the output of the nicfi_quads_bbox function is a named list (this time of length 3) and we will keep the data.table (dtbl_quads) which includes the id_quad_page, quad_link_download and the quad_link_thumbnail columns which are required for our analysis.
= c('id_quad_page', 'quad_link_download', 'quad_link_thumbnail')
cols_keep_quads = dtbl_quads[, ..cols_keep_quads]
dtbl_keep_quads 'id_quad_page'] dtbl_keep_quads[,
Although we specified a maximum of 10 pages, as we see from the output, only 2 Quads are returned for our input bounding box.
In first place we can download and visualize the thumbnail .png images of these 2 files, which are small in size. For this purpose we’ll utilize the aria2c download utility. For installation instructions of aria2c (on all 3 Operating Systems) have a look at the README.md file of the PlanetNICFI R package. A nice feature of aria2c is that it allows paralleled download of files and it takes also various other parameters such as a retry limit or maximum retries (you can have a look at the Documentation of aria2c for more information)
The aria2c_download_paths function of the R package allows the user to format the download web URL paths either of the thumbnail png or of the bigger tif files,
= aria2c_download_paths(mosaic_output = mosaic_files,
url_paths_2016 mosaic_id = mosaic_ID_2016,
quads_output = quad_files,
img_type = 'thumbnail')
url_paths_2016
and we have also to specify a temporary directory so that we can download all required files,
= tempdir()
temp_dir_out temp_dir_out
We set the number of threads to run in parallel equal to the number of the downloaded files and adjust the aria2c parameters too (for a sequential download see the ‘sequential_download_paths()’ function that doesn’t make use of the ‘aria2c’ software),
= parallel::detectCores()
all_threads = length(url_paths_2016) / 2
set_threads = ifelse(set_threads < all_threads, set_threads, all_threads)
num_threads = '--allow-overwrite --file-allocation=none --retry-wait=5 --max-tries=0'
aria_args
= aria2c_bulk_donwload(vector_or_file_path = url_paths_2016,
res_downl default_directory = temp_dir_out,
threads = num_threads,
verbose = FALSE,
secondary_args_aria = aria_args)
The following images correspond to the downloaded .png’s of the year 2016 and show the AOI in low resolution,
Image (a) | Image (b) |
---|---|
We can now proceed to download the .tif files which are required for the change detection task. We slightly modify the aria2c_download_paths function by adjusting the img_type parameter to ‘tif’. The following code snippet downloads the .tif images of year 2016,
= aria2c_download_paths(mosaic_output = mosaic_files,
url_paths_2016_tif mosaic_id = mosaic_ID_2016,
quads_output = quad_files,
img_type = 'tif')
= aria2c_bulk_donwload(vector_or_file_path = url_paths_2016_tif,
res_downl_tif default_directory = temp_dir_out,
threads = num_threads,
verbose = FALSE,
secondary_args_aria = aria_args)
We have 2 images for our bounding box area (each one is more than 100 MB), therefore we have to create a single cropped image and for this to happen we’ll have to
The following code snippet shows the code of the mentioned steps,
#..................................................................
# create a Virtual Raster (VRT) file from the downloaded .tif files
#..................................................................
= file.path(temp_dir_out, glue::glue("{mosaic_ID_2016}.vrt"))
VRT_out
= create_VRT_from_dir(dir_tifs = temp_dir_out,
res_vrt output_path_VRT = VRT_out,
file_extension = '.tif',
verbose = TRUE)
#.......................................................
# Adjust the Coordinate Reference System of the bounding
# box from 4326 to the projection of the .tif files
#.......................................................
= sf::st_as_sfc(WKT, crs = 4326)
wkt_sf = proj_info_extract(path_to_raster = VRT_out)
proj_info
= sf::st_transform(wkt_sf, crs = proj_info)
wkt_transf = sf::st_bbox(wkt_transf)
bbx_transf
#..............................................................................
# crop the output .vrt file based on the bounding box and save the output image
#..............................................................................
= file.path(temp_dir_out, glue::glue("{mosaic_ID_2016}_CROPPED.tif"))
pth_crop_out
= list(xmin = as.numeric(bbx_transf['xmin']),
bbx_crop xmax = as.numeric(bbx_transf['xmax']),
ymin = as.numeric(bbx_transf['ymin']),
ymax = as.numeric(bbx_transf['ymax']))
= nicfi_crop_images(input_pth = VRT_out,
warp_obj output_pth = pth_crop_out,
bbox_AOI = bbx_crop,
threads = num_threads,
of = 'GTiff',
resize_method = 'lanczos',
verbose = TRUE)
The next image shows the cropped area for the year 2016 in high resolution,
We can come to the cropped image for the year 2018 in the same way by adjusting the code in the following way,
= 5
index_year_2018 = dtbl_keep$id[index_year_2018]
mosaic_ID_2018
= nicfi_quads_bbox(planet_api_key = api_key,
quad_files_2018 mosaic_id = mosaic_ID_2018,
bbox_AOI = NULL,
wkt_AOI = WKT,
page_size = 10,
crs_bbox = 4326,
verbose = FALSE)
= aria2c_download_paths(mosaic_output = mosaic_files,
url_paths_2018 mosaic_id = mosaic_ID_2018,
quads_output = quad_files_2018,
img_type = 'tif')
#.....................................................................
# create a new temporary directory to save the .tif files of year 2018
#.....................................................................
= file.path(temp_dir_out, 'year_2018')
temp_dir_2018 if (!dir.exists(temp_dir_2018)) dir.create(temp_dir_2018)
= aria2c_bulk_donwload(vector_or_file_path = url_paths_2018,
res_downl_tif_2018 default_directory = temp_dir_2018,
threads = num_threads,
verbose = FALSE,
secondary_args_aria = aria_args)
= file.path(temp_dir_2018, glue::glue("{mosaic_ID_2018}.vrt"))
VRT_out_2018
= create_VRT_from_dir(dir_tifs = temp_dir_2018,
res_vrt output_path_VRT = VRT_out_2018,
file_extension = '.tif',
verbose = TRUE)
= file.path(temp_dir_2018, glue::glue("{mosaic_ID_2018}_CROPPED.tif"))
pth_crop_2018
= nicfi_crop_images(input_pth = VRT_out_2018,
warp_obj output_pth = pth_crop_2018,
bbox_AOI = bbx_crop,
threads = num_threads,
of = 'GTiff',
resize_method = 'lanczos',
verbose = TRUE)
The next image shows the cropped area for the year 2018,
Now that we have the cropped images for both years we can perform change detection. One method that can be used directly to the cropped images is Change Vector Analysis. I’ll use the Rstoolbox package which includes the rasterCVA function. Based on the documentation “… Change Vector Analysis (CVA) is used to identify spectral changes between two identical scenes which were acquired at different times …”. The following code snippet loads the .tif files and computes CVA using the output image of the year 2018 as a reference,
# The 'RStoolbox' R Package is required, please install from Github
# 'https://github.com/bleutner/RStoolbox' using remotes::install_github('bleutner/RStoolbox')
= terra::rast(x = pth_crop_2018) # image 2018: origin or reference
orig_rst = terra::rast(x = pth_crop_out) # image 2016
change_rst
= c(1, 5) # bands 1 and 5 depict the difference better
bands
= RStoolbox::rasterCVA(x = orig_rst[[bands]], y = change_rst[[bands]]) cva
To observe the differences in more detail we can arrange the plots side-by-side and add the CVA plot too,
AOI (2016) | AOI (2018) |
---|---|
::plot(cva$angle, axes = F) terra
Change Vector Analysis |
---|
By solely plotting the angle of the Change Vector Analysis we can clearly see the deforested areas in green color.