Skip to content

Instantly share code, notes, and snippets.

@NeilCFD
Last active September 25, 2022 19:58
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save NeilCFD/913273692fb385f8d5c09fbfaddabf60 to your computer and use it in GitHub Desktop.
Save NeilCFD/913273692fb385f8d5c09fbfaddabf60 to your computer and use it in GitHub Desktop.
CLIWOC historical ship movements animation
library(tidyverse) # 1.3.1
library(rayrender) # 0.23.6
library(sf) # 1.0-4
library(magrittr)
# Earth daymap from https://www.solarsystemscope.com/textures/download/2k_earth_daymap.jpg
image_texture_path <- "/path/to/2k_earth_daymap.jpg"
# CLIWOC Data source: https://www.historicalclimatology.com/cliwoc.html
f_cliwoc <- ("/path/to/cliwoc21.gpkg")
cliwoc_data <- sf::st_read(f_cliwoc)
# output path for frames of animation
output_path <- "/path/to/output/cliwoc/"
tracks <- cliwoc_data %>%
sf::st_set_geometry(NULL) %>% # remove geometry, coerce to data.frame
dplyr::select(c('ShipName', 'Nationality', 'VoyageIni', 'VoyageFrom', 'VoyageTo', 'YR', 'MO', 'DY', 'HR', 'latitude', 'longitude')) %>%
dplyr::filter(!(is.na(latitude))) %>%
dplyr::filter(!(is.na(longitude)))
# build up a date field, remove double-reported positions
tracks <- tracks %>%
dplyr::mutate(my_date = lubridate::as_date(paste0(YR, '-', MO, '-', DY))) %>%
dplyr::distinct(ShipName, my_date, .keep_all = TRUE)
# colour mapping
Nationality <- c("SPANISH", "DUTCH", "BRITISH", "FRENCH")
colour <- c("#ff0000", "#FF8000", "white", "blue")
df_colour <- data.frame(Nationality, colour)
# 1778 was the year with the most voyages
df <- tracks %>%
dplyr::filter(YR == 1778) %>%
dplyr::arrange(my_date) %>%
dplyr::left_join(df_colour, by = "Nationality")
# set up the days in that year
date_start <- lubridate::as_date('1778-01-02') # start on day 2 so we have some trails
date_end <- lubridate::as_date('1778-12-31')
my_dates <- seq(date_start, date_end, by = 'days')
unique_codes <- unique(df$ShipName)
for (ts in seq_along(my_dates)) {
cutoff_date <- my_dates[ts]
# the track behind each vessel
df_tracks <- df %>%
dplyr::filter(my_date <= cutoff_date) %>%
dplyr::arrange(my_date)
# the current position of each vessel
df_current_positions <- df %>%
dplyr::group_by(ShipName) %>%
dplyr::arrange(my_date, .by_group = TRUE) %>%
dplyr::filter(my_date <= cutoff_date) %>%
dplyr::slice_tail(n = 1)
tracks_list <- list()
for (i in 1:length(unique_codes)) {
track <- df_tracks %>%
dplyr::filter(ShipName == unique_codes[i]) %>%
mutate(
x = sinpi(longitude / 180) * cospi(latitude / 180),
y = sinpi(latitude / 180),
z = cospi(longitude / 180) * cospi(latitude / 180)
)
if (nrow(track) > 1) {
print(track$colour)
my_colour = track$colour[1]
tracks_list[[i]] = track %>%
dplyr::select(x, y, z) %>%
raster::as.matrix() %>%
rayrender::path(
material = diffuse(color = my_colour),
width = 0.001,
type = "flat",
straight = FALSE
)
} else {
tracks_list[[i]] = NULL
}
}
all_tracks_ray = do.call(rbind, tracks_list)
initial_objects <-
rayrender::group_objects(all_tracks_ray, scale = c(1, 1, 1) * 1.0002) %>%
rayrender::add_object(rayrender::sphere(
radius = 1,
material = rayrender::diffuse(image_texture = image_texture_path),
angle = c(0, -90, 0)
))
for (row in 1:nrow(df_current_positions)) {
initial_objects <- initial_objects %>%
rayrender::add_object(
rayrender::sphere(
x = sinpi(df_current_positions[row, ]$longitude / 180) * cospi(df_current_positions[row, ]$latitude / 180),
y = sinpi(df_current_positions[row, ]$latitude / 180),
z = cospi(df_current_positions[row, ]$longitude / 180) * cospi(df_current_positions[row, ]$latitude / 180),
radius = 0.005,
material = rayrender::diffuse(color = df_current_positions[row, ]$colour)
)
)
}
initial_objects %>%
rayrender::group_objects(angle = c(0, 30, 0)) %>%
rayrender::add_object(sphere(
y = 2.5,
z = 8,
x = 2.5,
material = rayrender::light(intensity = 80, color = "lightblue")
)) %>%
rayrender::add_object(sphere(
y = 5,
z = 5,
x = -5,
material = rayrender::light(intensity = 10, color = "orange")
)) %>%
rayrender::add_object(sphere(
y = -10,
material = rayrender::light(intensity = 3, color = "white")
)) %>%
rayrender::render_scene(
samples = 200,
width = 1200,
height = 1200,
fov = 0,
aperture = 0,
ortho_dimensions = c(2.3, 2.3),
sample_method = "sobol_blue",
verbose = TRUE,
filename = sprintf(glue::glue(output_path,"frame%d.png"),
ts
)
)
}
# Add whatever annotations you want to each frame, then magick into mp4
output_mp4 = 'cliwoc.mp4'
img_frames <- paste0(output_path, "frame", seq_along(my_dates), ".png")
magick::image_write_video(magick::image_read(img_frames), path = output_mp4, framerate = 20)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment