I've created an animated plot in ggplot2
that visualizes the GPS tracks of a diurnal species over time. To improve the visualisation, I would like to overlay a semi-transparent layer that dynamically changes based on whether it is day or night. I don't want to use static values, but rather use the actual sun elevation relative to the horizon. I've supplemented my dataset with the sun positions using the suncalc
package. A reproducible example with dummy data can be found below. Anyone who can help me in the right direction?
library(ggplot2)
library(gganimate)
library(sf)
library(suncalc)
library(ggspatial)
library(gifski)
library(prettymapr)
# sample data
set.seed(123)
gps_data_sf <- data.frame(
IMEI = rep(35664665, 300),
Pick.Time = seq.POSIXt(as.POSIXct("2024-08-13 00:00:00"), by = "15 mins", length.out = 300),
Longitude = runif(300, 2.7, 2.8),
Latitude = runif(300, 51.1, 51.2),
Bird = rep("Bird A", 300)
)
# add sun height
gps_data_sf$sunheight <- mapply(function(lat, lon, time) {
sun_position <- getSunlightPosition(date = time, lat = lat, lon = lon)
return(sun_position$altitude)
}, gps_data_sf$Latitude, gps_data_sf$Longitude, gps_data_sf$Pick.Time)
# Convert to sf
gps_data_sf <- st_as_sf(gps_data_sf, coords = c("Longitude", "Latitude"), crs = 4326)
# animate
animated_plot <- ggplot() +
annotation_map_tile(type = "osm", zoom = 12) +
geom_sf(data = gps_data_sf, aes(geometry = geometry, color = Bird, group = Bird), size = 5, alpha = 0.8) +
labs(
title = "Movement",
subtitle = "Time: {frame_time}",
x = "Longitude",
y = "Latitude"
) +
transition_time(Pick.Time) +
ease_aes('linear')
# Render
animate(animated_plot, nframes = 100, fps = 10, width = 800, height = 600, renderer = gifski_renderer())
Using geom_rect
, along with scale_fill_gradient
, can create the look you're trying to achieve.
First, I moved the data and grouping to ggplot
, so that it can be inherited by geom_rect
.
When calling geom_rect
with an sf, if you want to fill the entire plot, you can use -Inf
to Inf
. I used an alpha of .4 for this layer arbitrarily.
Lastly, using scale_fill_gradient
created a high and low color gradient to emulate the sun. (I used black and yellow for night and day.)
The call for geom_sf
is the same as in your question, less the arguments that were moved to ggplot()
(data
and group
).
# animate
animated_plot <- ggplot(gps_data_sf, group = Bird) + # move data and grouping to ggplot
annotation_map_tile(type = "osm", zoom = 12) +
geom_sf(aes(geometry = geometry, color = Bird),
size = 5, alpha = 0.8) +
geom_rect(xmin = -Inf, xmax = Inf, ymin = -Inf, ymax = Inf, # add color space
aes(fill = sunheight), alpha = .4) +
scale_fill_gradient(low = "black", high = "yellow") + # specify colors
labs(
title = "Movement", subtitle = "Time: {frame_time}",
x = "Longitude", y = "Latitude") +
transition_time(Pick.Time) + ease_aes('linear')
A few images so you can see how the gradient overlay changes (this is 3am and 10am)