如何在gganimate中正确动绘同一日期下的多个R语言船舶点位
太平洋船舶航线动画点位缺失问题解决
问题背景
正在制作太平洋区域船舶航线动画,数据集包含ship(船舶ID)、lat(纬度)、lon(经度)、datetime(时间)字段。静态船舶路径图显示正常,但使用gganimate生成的GIF动画出现部分点位丢失,两者路径存在明显差异:


数据集示例
| ship | lat | lon | datetime |
|---|---|---|---|
| 382914 | 49.22597 | -124.8141 | 2022-02-04 |
| 351364 | 54.16689 | -179.9273 | 2022-02-22 |
| ... | ... | ... | ... |
推测原因
同一datetime下,同一ship的多个经纬度点位仅被显示一个,导致点位缺失。
现有代码
静态图代码
map1 <- ggplot() + geom_polygon(data=mapWorld, aes(x=long, y=lat, group=group), color= "darkolivegreen", fill="darkolivegreen") + geom_path(data = BOATS, aes(x=lon, y = lat, group = ship id, color = factor(ship)), size = 1.5, show.legend = FALSE) + geom_point(data = BOATS, aes(lon, lat, group = ship, color = factor(ship)), alpha = 1, size = 2, shape = 21) + coord_sf(xlim=c(-180, -115), ylim=c(27,62), expand = FALSE) + labs(x="Longitude", y="Latitude") + scale_colour_brewer(palette = "Dark2") + guides(colour = guide_legend(nrow = 3, byrow = TRUE, override.aes = list(size=5))) + mapTheme map1
动画代码
library(gganimate) library(gifski) map1.animation = map1 + transition_reveal(along = datetime) + ease_aes('linear') + enter_fade() + exit_fade() animate(map1.animation, fps = 30, nframes = 360, width = 1071, height = 715) anim_save("Boat Path.gif")
解决方案
问题根源
transition_reveal会对每个分组(ship)的datetime进行去重处理,同一时间点的多个点位被合并显示为一个;另外静态代码中geom_path的group = ship id存在语法问题(字段名带空格易引发错误)。
具体修复步骤
1. 预处理数据集,给同时间同船舶的点位添加唯一标识
通过给同一ship和datetime的记录添加微小时间差,让transition_reveal能识别每个独立点位:
library(dplyr) # 先统一字段名(如果原字段名是"ship id",先重命名为ship_id避免空格问题) BOATS <- BOATS %>% rename(ship_id = `ship id`) %>% group_by(ship_id, datetime) %>% mutate(time_seq = row_number()) %>% ungroup() %>% # 给同一时间点的点位添加微小时间偏移,确保每个点位都能被过渡显示 mutate(reveal_time = as.numeric(datetime) + time_seq/1000)
2. 修改静态图和动画代码
修正分组逻辑,使用新的reveal_time作为过渡依据:
# 修正后的静态图代码 map1 <- ggplot() + geom_polygon(data=mapWorld, aes(x=long, y=lat, group=group), color= "darkolivegreen", fill="darkolivegreen") + geom_path(data = BOATS, aes(x=lon, y = lat, group = ship_id, color = factor(ship_id)), size = 1.5, show.legend = FALSE) + # 用interaction(ship_id, time_seq)确保每个点位都是独立分组 geom_point(data = BOATS, aes(lon, lat, group = interaction(ship_id, time_seq), color = factor(ship_id)), alpha = 1, size = 2, shape = 21) + coord_sf(xlim=c(-180, -115), ylim=c(27,62), expand = FALSE) + labs(x="Longitude", y="Latitude") + scale_colour_brewer(palette = "Dark2") + guides(colour = guide_legend(nrow = 3, byrow = TRUE, override.aes = list(size=5))) + mapTheme # 修正后的动画代码 map1.animation = map1 + transition_reveal(along = reveal_time) + ease_aes('linear') + enter_fade() + exit_fade() animate(map1.animation, fps = 30, nframes = 360, width = 1071, height = 715) anim_save("Boat Path.gif")
3. 替代方案:使用transition_time保留所有点位
如果不需要路径逐步绘制的效果,改用transition_time配合shadow_mark可以更简单地保留所有历史点位,同时显示当前时间的全部点位:
map1.animation = map1 + transition_time(datetime) + # 保留所有过去的点位,不显示未来点位 shadow_mark(past = TRUE, future = FALSE) + ease_aes('linear') animate(map1.animation, fps = 30, nframes = 360, width = 1071, height = 715)
内容的提问来源于stack exchange,提问作者mcdejer
相关产品推荐
相关产品推荐

