library(sf)
library(stars)
library(tidyverse)
library(tmap)
library(rstac)
library(gdalcubes)
5: Fire Severity using Normalized Burn Index
<- "https://34c031f8-c9fd-4018-8c5a-4159cdff6b0d-cdn-endpoint.azureedge.net/-/media/calfire-website/what-we-do/fire-resource-assessment-program---frap/gis-data/april-2023/fire221gdb.zip?rev=9e3e1e5e61e242d5b2994d666d72a91a&hash=F424990CD64BB7C4CF01C6CE211C0A59"
url download.file(url, "fire221.gdb.zip", mode="wb")
unzip("fire221.gdb.zip")
<-
fire_polys read_sf("fire22_1.gdb", layer = "firep22_1") |>
filter(st_is_valid(Shape))
<-
jtree read_sf("/vsicurl/https://huggingface.co/datasets/cboettig/biodiversity/resolve/main/data/NPS.gdb") |>
filter(UNIT_NAME == "Joshua Tree National Park") |>
st_transform(st_crs(fire_polys))
<- st_intersects(fire_polys, jtree, sparse=FALSE)
fire_is_in_jtree <- fire_polys |> filter(fire_is_in_jtree) fire_jtree
Warning: Using one column matrices in `filter()` was deprecated in dplyr 1.1.0.
ℹ Please use one dimensional logical vectors instead.
tmap_mode("view")
tmap mode set to 'view'
tm_shape(jtree) + tm_polygons() +
tm_shape(fire_jtree) + tm_polygons("YEAR_")
Because burned vegetation presents a very different spectral pattern to normal vegetation, burn intensity can be measured by comparing the spectra in different bands. The greatest contrast between burned and health vegetation can be seen in the NIR and SWIR bands, as seen in this typical spectral response curve:
The normalized burn ratio (NBR) is defined as the ratio:
\[ NBR = \frac{NIR - SWIR}{NIR + SWIR}\]
Landsat example
Sentinel-2 Example
<- fire_jtree |>
big_fire filter(YEAR_ > "2015") |>
filter(Shape_Area == max(Shape_Area))
<- big_fire |> st_transform(4326) |> st_bbox()
box <- big_fire$ALARM_DATE alarm_date
<- as.character( alarm_date - days(5) )
start_date <- as.character( alarm_date + days(5) )
end_date
<-
items stac("https://planetarycomputer.microsoft.com/api/stac/v1") |>
stac_search(collections = "sentinel-2-l2a",
datetime = paste(start_date, end_date, sep="/"),
bbox = c(box)) |>
post_request() |>
items_sign(sign_planetary_computer())
<- stac_image_collection(items$features, asset_names = c("B08", "B12", "SCL"))
col
<- cube_view(srs ="EPSG:4326",
cube extent = list(t0 = start_date, t1 = end_date,
left = box[1], right = box[3],
top = box[4], bottom = box[2]),
dx = 0.0001, dy = 0.0001, dt = "P1D")
<- image_mask("SCL", values=c(3, 8, 9)) # mask clouds and cloud shadows
mask <- raster_cube(col, cube, mask = mask) data
<- data |>
nbr apply_pixel("(B08-B12)/(B08+B12)", "NBR")
<- nbr |> slice_time("2022-05-25")
before_fire <- nbr |> slice_time("2022-05-30") after_fire
|> plot() before_fire
|> plot() after_fire
<- after_fire |> st_as_stars() after_fire_stars
Warning in CPL_read_gdal(as.character(x), as.character(options),
as.character(driver), : GDAL Message 1: The dataset has several variables that
could be identified as vector fields, but not all share the same primary
dimension. Consequently they will be ignored.
<- before_fire |> st_as_stars() before_fire_stars
Warning in CPL_read_gdal(as.character(x), as.character(options),
as.character(driver), : GDAL Message 1: The dataset has several variables that
could be identified as vector fields, but not all share the same primary
dimension. Consequently they will be ignored.
st_dimensions(before_fire_stars) <- st_dimensions(after_fire_stars)
<- before_fire_stars - after_fire_stars dnbr
tmap_mode("view")
tmap mode set to 'view'
tm_shape(dnbr) + tm_raster() +
tm_shape(big_fire) + tm_borders()
[scale] tm_raster:() the data variable assigned to 'col' contains positive and negative values, so midpoint is set to 0. Set 'midpoint = NA' in 'fill.scale = tm_scale_intervals(<HERE>)' to use all visual values (e.g. colors)
with slider
We can use leaflet to create a slider bar. This is more verbose than tmap
but very powerful. (Note that this example requires the GitHub)
library(leaflet.extras2)
Loading required package: leaflet
library(terra) # leaflet doesn't know about the stars package yet
terra 1.7.71
Attaching package: 'terra'
The following objects are masked from 'package:gdalcubes':
animate, crop, size
The following object is masked from 'package:tidyr':
extract
<- leaflet() |>
Map addMapPane("right", zIndex = 0) |>
addMapPane("left", zIndex = 0) |>
addTiles(group = "base", layerId = "baseid1", options = pathOptions(pane = "right")) |>
addTiles(group = "base", layerId = "baseid2", options = pathOptions(pane = "left")) |>
addRasterImage(x = rast(after_fire_stars), options = leafletOptions(pane = "right"), group = "r1") |>
addRasterImage(x = rast(before_fire_stars), options = leafletOptions(pane = "left"), group = "r2") |>
addLayersControl(overlayGroups = c("r1", "r2")) |>
addSidebyside(layerId = "sidecontrols",
rightId = "baseid1",
leftId = "baseid2")
Map