library(dggridR)
library(dplyr)
library(readr)
library(ggplot2)
library(purrr)
library(sf)
library(rnaturalearth)
library(glue)
library(classInt)Day 15 of 30DayMapChallenge: « Fire » (previously).
An animation of global fires in 2024 using MODIS data.
Data
See the docs. You can use a client like Filezilla to download the data.
- SFTP:
fuoco.geog.umd.edu - Login / Password (as of time of writing):
fire/burnt
Get all year 2024 files available in /data/MODIS/C61/MCD14ML.
We’ll use binning on a 250 km discrete global grid.
# countries background
world <- ne_countries(scale = 10) |>
st_make_valid() |>
st_wrap_dateline()
# build the grid
dggs <- dgconstruct(spacing = 250)
hex <- dggs |>
dgshptogrid(world, cellsize = 0.5) |>
st_make_valid() |>
st_wrap_dateline() |>
st_filter(world) |>
select(seqnum)
# read all MODIS files and find their grid cell ID
modis <- dir("~/data/modis/", full.names = TRUE) |>
read_fwf(
skip = 1,
col_types = cols("YYYYMMDD" = col_date(format = "%Y%m%d")),
fwf_positions(
c(1, 10, 15, 17, 26, 36, 42, 48, 53, 61, 65, 68),
c(9, 14, 16, 25, 35, 41, 47, 52, 60, 64, 67, 69),
c("YYYYMMDD", "HHMM", "sat", "lat", "lon", "T21", "T31", "sample", "FRP",
"conf", "type", "dn")),
num_threads = 10) |>
mutate(seqnum = dgGEO_to_SEQNUM(dggs, lon, lat)$seqnum)Map
We generate one PNG file per day and create the video with a call to a system-installed ffmpeg.
# compute the number of fires for each cell and each day
modis_cells <- modis |>
count(seqnum, YYYYMMDD) |>
left_join(hex,
join_by(seqnum)) |>
st_sf()
# prepare the breaks, to keep them identical on each image
breaks <- classIntervals(modis_cells$n, n = 4, style = "kmeans")
# create a PNG map for one day
create_map <- function(d) {
p <- modis_cells |>
filter(YYYYMMDD == d) |>
ggplot() +
geom_sf(data = world, fill = "#1a3853", color = "#002240") +
geom_sf(aes(fill = n, color = n)) +
scale_fill_viridis_c(aesthetics = c("colour", "fill"),
breaks = round(breaks$brks),
transform = "log",
name = "Fires\nper cell\n(log\nscale)",
option = "B",
limits = c(1, max(modis_cells$seqnum))) +
coord_sf(crs = "EPSG:8857") +
labs(title = "MODIS fire detection",
subtitle = d,
caption = glue("MODIS - Global Monthly Fire Location \\
Product (MCD14ML)
https://r.iresmi.net/ - {Sys.Date()}")) +
theme_void() +
theme(text = element_text(family = "Ubuntu",
color = "white"),
plot.margin = margin(2, 2, 2, 5, unit = "mm"),
plot.caption = element_text(size = 7,
color = "#777"))
ggsave(glue("img/fire_{d}.png"), p, bg = "#002240", width = 9, height = 5)
}
# Iterate
modis |>
distinct(YYYYMMDD) |>
pull(YYYYMMDD) |>
walk(create_map, .progress = TRUE)
# generate the video
system(glue('ffmpeg -framerate 24 -pattern_type glob -i "img/fire*.png" \\
-c:v libx264 -pix_fmt yuv420p modis.mp4'))