如何创建车辆沿路线从 A 移动到 B 的动画?
How to create animation of vehicle moving form A to B along a route?
下面是使用 osrm
包在 R 中查找从 'One World Trade Center, NYC' 到 'Madison Square Park, NYC' 的路线、旅行时间和旅行距离的示例。(我从 Road Routing in R 学到的).这里的行车时间是10.37分钟。
我想制作可视化视频。
问。如何创建车辆(由标记表示)沿路线从 'One World Trade Center, NYC' 移动到 'Madison Square Park, NYC' 的动画?
理想情况下,我们应该知道每个路段的速度。但是让我们假设车辆在两个位置之间以恒定速度 (= distance/time) 不停地移动。
我们可以简单地使用 tmap
而不是传单来创建动画。
library(sf)
library(dplyr)
library(tidygeocoder)
library(osrm)
# 1. One World Trade Center, NYC
# 2. Madison Square Park, NYC
adresses <- c("285 Fulton St, New York, NY 10007",
"11 Madison Ave, New York, NY 10010")
# geocode the two addresses & transform to {sf} data structure
data <- tidygeocoder::geo(adresses, method = "osm") %>%
st_as_sf(coords = c("long", "lat"), crs = 4326)
osroute <- osrm::osrmRoute(loc = data,
returnclass = "sf")
summary(osroute)
library(leaflet)
leaflet(data = data) %>%
addProviderTiles("CartoDB.Positron") %>%
addMarkers(label = ~address) %>%
addPolylines(data = osroute,
label = "OSRM engine",
color = "red")
使用您想要的点数对路线(LINESTRING)进行采样,然后使用 lapply
函数制作地图对象,并使用 tmap_animate
为它们设置动画。
添加到上面的代码中:
library(tmap)
library(gifski)
# sample osroute 50 times regularly, cast to POINT, return sf (not sfc) object
osroute_sampled <- st_sample(osroute, type = 'regular', size = 50) %>%
st_cast('POINT') %>%
st_as_sf()
# use lapply to crate animation maps. taken from reference page:
# https://mtennekes.github.io/tmap/reference/tmap_animation.html
m0 <- lapply(seq_along(1:nrow(osroute_sampled)), function(point){
x <- osroute_sampled[point,] ## bracketted subsetting to get only 1 point
tm_shape(osroute) + ## full route
tm_sf() +
tm_shape(data) + ## markers for start/end points
tm_markers() +
tm_shape(x) + ## single point
tm_sf(col = 'red', size = 3)
})
# Render the animation
tmap_animation(m0, width = 300, height = 600, delay = 10)
自从我使用 tmap
以来已经有一段时间了,所以我不是最新的添加提供商磁贴。将它们添加到 lapply
函数中应该不会太难。
作为@mrhellman 提出的 tmap
方法的替代方法,我提供了一种基于 ggplot
、ggmap
(用于底图)和基于 gganimate
的工作流程的替代方法.
我发现通过 {gganimate} 创建的动画效果更可取,因为 {gganimate} 给了我更多的控制权 - 例如 shadow_wake 在我看来很好地展示了汽车沿线的运动.如果我没记错的话,tmap 在底层使用了 gganimate。
ggmap
不支持 CartoDB 底图 - 例如上面使用的 Positron - 但我发现碳粉背景足够。
请注意,ggmap
不能很好地与 ggplot2::geom_sf()
配合使用,我发现将我的工作流程转换为旧的 ggplot2::geom_point()
方法更容易 - 即提取 x 和 y 坐标并通过 aes().
映射它们
由于只有一条路线可以显示,因此计算 transition_reveal()
中用于动画的技术变量 seq
就足够了;如果有必要(例如在单个动画中显示具有不同旅行时间的更多路线时),这可能会被时间维度取代。
library(sf)
library(dplyr)
library(tidygeocoder)
library(osrm)
# 1. One World Trade Center, NYC
# 2. Madison Square Park, NYC
adresses <- c("285 Fulton St, New York, NY 10007",
"11 Madison Ave, New York, NY 10010")
# geocode the two addresses & transform to {sf} data structure
data <- tidygeocoder::geo(adresses, method = "osm") %>%
st_as_sf(coords = c("long", "lat"), crs = 4326)
osroute <- osrm::osrmRoute(loc = data,
returnclass = "sf")
# sample osroute 50 times regularly, cast to POINT, return sf (not sfc) object
osroute_sampled <- st_sample(osroute, type = 'regular', size = 50) %>%
st_cast('POINT') %>%
st_as_sf()
library(ggplot2)
library(ggmap) # warning: has a naming conflict with tidygeocoder!
library(gganimate)
# ggmap does not quite like geom_sf(),
# the "old school" geom_point will be easier to work with
osroute_xy <- osroute_sampled %>%
mutate(seq = 1:nrow(.),
x = st_coordinates(.)[,"X"],
y = st_coordinates(.)[,"Y"])
# basemap / the bbox depends on yer area of interest
NYC <- get_stamenmap(bbox = c(-74.05, 40.68, -73.9, 40.8),
zoom = 13,
maptype = "toner-background")
# draw a map
animation <- ggmap(NYC) +
geom_point(data = osroute_xy,
aes(x = x, y = y),
color = "red",
size = 4) +
theme_void() +
transition_reveal(seq) +
shadow_wake(wake_length = 1/6)
# create animation
gganimate::animate(animation,
nframes = 2*(nrow(osroute_xy)+1),
height = 800,
width = 760,
fps = 10,
renderer = gifski_renderer(loop = T))
# save animation
gganimate::anim_save('animated_nyc.gif')
这是一个 {mapdeck}
方法,它为您提供交互式地图(如传单)和动画旅行,一次可以轻松处理数千次旅行
library(mapdeck)
set_token( secret::get_secret("MAPBOX") )
mapdeck(
location = as.numeric( data[1, ]$geometry[[1]] ) ## for 'trips' you need to specify the location
, zoom = 12
, style = mapdeck_style("dark")
) %>%
add_trips(
data = sf
, stroke_colour = "#FFFFFF" #white
, trail_length = 12
, animation_speed = 8
, stroke_width = 50
)
add_trips()
函数采用具有 Z 和 M 维度(z = 海拔,m = 时间)的 sf
线串对象。所以你可以有一个与每个坐标相关联的时间戳
library(mpadeck)
library(sfheaders)
df_route <- sfheaders::sf_to_df(osroute, fill = TRUE)
## Assume 'duration' is constant
## we want the cumulative time along the rute
df_route$cumtime <- cumsum(df_route$duration)
## and we also need a Z component.
## since we don't know the elevation, I'm setting it to '0'
df_route$elevation <- 0
## Build the 'sf' object wtih the Z and M dimensions
sf <- sfheaders::sf_linestring(
obj = df_route
, x = "x"
, y = "y"
, z = "elevation"
, m = "cumtime"
)
website 有更多详细信息。
下面是使用 osrm
包在 R 中查找从 'One World Trade Center, NYC' 到 'Madison Square Park, NYC' 的路线、旅行时间和旅行距离的示例。(我从 Road Routing in R 学到的).这里的行车时间是10.37分钟。
我想制作可视化视频。
问。如何创建车辆(由标记表示)沿路线从 'One World Trade Center, NYC' 移动到 'Madison Square Park, NYC' 的动画?
理想情况下,我们应该知道每个路段的速度。但是让我们假设车辆在两个位置之间以恒定速度 (= distance/time) 不停地移动。
我们可以简单地使用 tmap
而不是传单来创建动画。
library(sf)
library(dplyr)
library(tidygeocoder)
library(osrm)
# 1. One World Trade Center, NYC
# 2. Madison Square Park, NYC
adresses <- c("285 Fulton St, New York, NY 10007",
"11 Madison Ave, New York, NY 10010")
# geocode the two addresses & transform to {sf} data structure
data <- tidygeocoder::geo(adresses, method = "osm") %>%
st_as_sf(coords = c("long", "lat"), crs = 4326)
osroute <- osrm::osrmRoute(loc = data,
returnclass = "sf")
summary(osroute)
library(leaflet)
leaflet(data = data) %>%
addProviderTiles("CartoDB.Positron") %>%
addMarkers(label = ~address) %>%
addPolylines(data = osroute,
label = "OSRM engine",
color = "red")
使用您想要的点数对路线(LINESTRING)进行采样,然后使用 lapply
函数制作地图对象,并使用 tmap_animate
为它们设置动画。
添加到上面的代码中:
library(tmap)
library(gifski)
# sample osroute 50 times regularly, cast to POINT, return sf (not sfc) object
osroute_sampled <- st_sample(osroute, type = 'regular', size = 50) %>%
st_cast('POINT') %>%
st_as_sf()
# use lapply to crate animation maps. taken from reference page:
# https://mtennekes.github.io/tmap/reference/tmap_animation.html
m0 <- lapply(seq_along(1:nrow(osroute_sampled)), function(point){
x <- osroute_sampled[point,] ## bracketted subsetting to get only 1 point
tm_shape(osroute) + ## full route
tm_sf() +
tm_shape(data) + ## markers for start/end points
tm_markers() +
tm_shape(x) + ## single point
tm_sf(col = 'red', size = 3)
})
# Render the animation
tmap_animation(m0, width = 300, height = 600, delay = 10)
自从我使用 tmap
以来已经有一段时间了,所以我不是最新的添加提供商磁贴。将它们添加到 lapply
函数中应该不会太难。
作为@mrhellman 提出的 tmap
方法的替代方法,我提供了一种基于 ggplot
、ggmap
(用于底图)和基于 gganimate
的工作流程的替代方法.
我发现通过 {gganimate} 创建的动画效果更可取,因为 {gganimate} 给了我更多的控制权 - 例如 shadow_wake 在我看来很好地展示了汽车沿线的运动.如果我没记错的话,tmap 在底层使用了 gganimate。
ggmap
不支持 CartoDB 底图 - 例如上面使用的 Positron - 但我发现碳粉背景足够。
请注意,ggmap
不能很好地与 ggplot2::geom_sf()
配合使用,我发现将我的工作流程转换为旧的 ggplot2::geom_point()
方法更容易 - 即提取 x 和 y 坐标并通过 aes().
由于只有一条路线可以显示,因此计算 transition_reveal()
中用于动画的技术变量 seq
就足够了;如果有必要(例如在单个动画中显示具有不同旅行时间的更多路线时),这可能会被时间维度取代。
library(sf)
library(dplyr)
library(tidygeocoder)
library(osrm)
# 1. One World Trade Center, NYC
# 2. Madison Square Park, NYC
adresses <- c("285 Fulton St, New York, NY 10007",
"11 Madison Ave, New York, NY 10010")
# geocode the two addresses & transform to {sf} data structure
data <- tidygeocoder::geo(adresses, method = "osm") %>%
st_as_sf(coords = c("long", "lat"), crs = 4326)
osroute <- osrm::osrmRoute(loc = data,
returnclass = "sf")
# sample osroute 50 times regularly, cast to POINT, return sf (not sfc) object
osroute_sampled <- st_sample(osroute, type = 'regular', size = 50) %>%
st_cast('POINT') %>%
st_as_sf()
library(ggplot2)
library(ggmap) # warning: has a naming conflict with tidygeocoder!
library(gganimate)
# ggmap does not quite like geom_sf(),
# the "old school" geom_point will be easier to work with
osroute_xy <- osroute_sampled %>%
mutate(seq = 1:nrow(.),
x = st_coordinates(.)[,"X"],
y = st_coordinates(.)[,"Y"])
# basemap / the bbox depends on yer area of interest
NYC <- get_stamenmap(bbox = c(-74.05, 40.68, -73.9, 40.8),
zoom = 13,
maptype = "toner-background")
# draw a map
animation <- ggmap(NYC) +
geom_point(data = osroute_xy,
aes(x = x, y = y),
color = "red",
size = 4) +
theme_void() +
transition_reveal(seq) +
shadow_wake(wake_length = 1/6)
# create animation
gganimate::animate(animation,
nframes = 2*(nrow(osroute_xy)+1),
height = 800,
width = 760,
fps = 10,
renderer = gifski_renderer(loop = T))
# save animation
gganimate::anim_save('animated_nyc.gif')
这是一个 {mapdeck}
方法,它为您提供交互式地图(如传单)和动画旅行,一次可以轻松处理数千次旅行
library(mapdeck)
set_token( secret::get_secret("MAPBOX") )
mapdeck(
location = as.numeric( data[1, ]$geometry[[1]] ) ## for 'trips' you need to specify the location
, zoom = 12
, style = mapdeck_style("dark")
) %>%
add_trips(
data = sf
, stroke_colour = "#FFFFFF" #white
, trail_length = 12
, animation_speed = 8
, stroke_width = 50
)
add_trips()
函数采用具有 Z 和 M 维度(z = 海拔,m = 时间)的 sf
线串对象。所以你可以有一个与每个坐标相关联的时间戳
library(mpadeck)
library(sfheaders)
df_route <- sfheaders::sf_to_df(osroute, fill = TRUE)
## Assume 'duration' is constant
## we want the cumulative time along the rute
df_route$cumtime <- cumsum(df_route$duration)
## and we also need a Z component.
## since we don't know the elevation, I'm setting it to '0'
df_route$elevation <- 0
## Build the 'sf' object wtih the Z and M dimensions
sf <- sfheaders::sf_linestring(
obj = df_route
, x = "x"
, y = "y"
, z = "elevation"
, m = "cumtime"
)
website 有更多详细信息。