Я хочу добавить пути на карту, созданную с помощью geom_sf
в ggplot2.
Я знаком с использованием стандартной карты с центром в Гринвиче. Но когда я меняю дизайн карты(? crs
), обычные методы не работают.
point_df=data.frame(
location=c('Tokyo','Brasilia','France'),
lat=c(35.65,-15.79,48.86),
long=c(139.84,-47.88,2.35)
)
point_df2=data.frame(
from_location=c('Tokyo','Brasilia'),
from_lat=c(35.65,-15.79),
from_long=c(139.84,-47.88),
to_location=c('France'),
to_lat=c(48.86),
to_long=c(2.35)
)
library("rnaturalearth")
world <- ne_countries(scale = "medium", returnclass = "sf")
transpoint = st_as_sf(point_df,coords=c("long","lat"),crs=4326)
world_sf <- st_transform(world, crs=4326)
ggplot(world_sf)+geom_sf()+
geom_point(data=transpoint,aes(geometry=geometry,color=location),
stat = "sf_coordinates")+
geom_text_repel(data=transpoint,aes(geometry=geometry,label=location),
stat = "sf_coordinates")+
geom_curve(data=point_df2,
aes(x=from_long,y=from_lat,xend=to_long,yend=to_lat),
colour='blue')
Однако если я предпочитаю карту с Тихим океаном в центре или эллиптическую карту, они терпят неудачу.
sf::st_break_antimeridian()
Для карты с центром в Тихом океане я использовал код, адаптированный из Закрытые границы на карте при использовании geom_sf для пересечения линии дат
Обратите внимание, что geom_point()
работает нормально. geom_segment()
полностью проваливается...
robinson <- "+proj=robin +lon_0=-90 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
world_pac <- world_sf %>%
st_break_antimeridian(lon_0 = -90) %>%
st_transform(crs = robinson)
transpoint = st_as_sf(point_df,coords=c("long","lat"),crs=4326)
dtran = st_transform(transpoint,robinson)
library(ggrepel)
ggplot(world_pac)+geom_sf()+
geom_point(data=dtran,aes(geometry=geometry,color=location),stat = "sf_coordinates")+
geom_text_repel(data=dtran,aes(geometry=geometry,label=location),stat = "sf_coordinates")+
geom_segment(data=point_df2, aes(x=from_long,y=from_lat,xend=to_long,yend=to_lat),
colour='blue')
На ggspatial::geom_spatial_segment()
показаны кривые/сегменты, но они странные.
ggplot(world_pac)+geom_sf()+
geom_point(data=dtran,aes(geometry=geometry,color=location),stat = "sf_coordinates")+
geom_text_repel(data=dtran,aes(geometry=geometry,label=location),stat = "sf_coordinates")+
geom_spatial_segment(data=point_df2, aes(x=from_long,y=from_lat,xend=to_long,yend=to_lat),crs=4326,
wrap_dateline = FALSE,
colour='blue')
Я также не смогу заставить geom_segment()
или geom_spatial_segment()
работать, если трансформируюсь в crs=2163
.
world_ellipse <- world_sf %>%
st_transform(crs=2163)
transpoint = st_as_sf(point_df,coords=c("long","lat"),crs=4326)
dtran = st_transform(transpoint,crs=2163)
ggplot(world_ellipse)+geom_sf()+
geom_point(data=dtran,aes(geometry=geometry,color=location),stat = "sf_coordinates")+
geom_text_repel(data=dtran,aes(geometry=geometry,label=location),stat = "sf_coordinates")
geom_spatial_segment(data=point_df2,
aes(from_long, from_lat, xend = to_long, yend = to_lat),crs = 2163,
wrap_dateline = FALSE)
Что мне здесь не хватает?
Как работать с geom_segment()
и/или geom_spatial_segment()
с ggplot2::geom_sf()
, когда мы используем разные CRS?
Самый простой способ обойти это — создать научно-фантастическую версию ваших достопримечательностей, а затем создать в научной фантастике переменные from/to для geom_curve()
.
Я исключил ggrepel
из этого примера, так как это похоже на игру «Ударь крота», когда дело доходит до получения удовлетворительных результатов. Я также скорректировал кривизну линий, поскольку значение по умолчанию отклоняет линию Франции/Токио от пользовательской карты Робинсона. Если эти параметры не устраивают, вы можете подправить их там, где это необходимо. Пример графика содержит опцию стрелки (закомментированную), если вы хотите продемонстрировать направление.
Это работает как для вашей пользовательской CRS, так и для EPSG:9311 (обновленная версия EPSG:2163, которая устарела). Единственное изменение, которое вам нужно будет внести для создания версии вашей карты в формате EPSG:9311, — это заменить st_transform(robinson)
на st_transform(9311)
.
Также обратите внимание, что в коде вашей карты с многоточием есть ошибка. Вы пропустили «+» после функции geom_text_repel()
. Возможно, произошла ошибка копирования/вставки, но geom_spatial_segment()
все равно не будет отображать точку point_df2. Однако он будет работать с объектом sf_points, созданным в этом решении.
Вот полный рабочий реплекс:
library(rnaturalearth)
library(sf)
library(dplyr)
library(ggplot2)
# Your point data
point_df <- data.frame(
location = c("Tokyo", "Brasilia", "France"),
lat = c(35.65,-15.79,48.86),
long = c(139.84,-47.88,2.35))
# Custom CRS
robinson <- "+proj=robin +lon_0=-90 +x_0=0 +y_0=0 +ellps=WGS84 +datum=WGS84 +units=m +no_defs"
# Create 90W-centred version of ne_countries with custiom CRS
world_pac <- ne_countries(scale = "medium", returnclass = "sf") %>%
st_break_antimeridian(lon_0 = -90) %>%
st_transform(robinson)
# Create sf of your point data
transpoint <- st_as_sf(point_df, coords = c("long","lat"), crs = 4326) %>%
st_transform(robinson)
# Generate from/to variables between France and the rest
sf_points <- transpoint %>%
st_set_geometry("to") %>%
mutate(from = to[location == "France"]) %>%
filter(!location == "France") %>%
mutate(x1 = st_coordinates(from)[,1],
y1 = st_coordinates(from)[,2],
x2 = st_coordinates(to)[,1],
y2 = st_coordinates(to)[,2])
#Plot
ggplot() +
geom_sf(data = world_pac, colour = "grey70") +
geom_sf(data = transpoint, colour = "#DF536B", size = 3) +
geom_curve(data = sf_points,
aes(x = x1, y = y1,
xend = x2, yend = y2),
colour = "#0072B2",
# arrow = arrow(length = unit(0.03, "npc"), type = "closed"),
curvature = 0.4) +
geom_sf_text(data = transpoint,
aes(label = location),
size = 4,
fun.geometry = st_centroid,
vjust = 2,
colour = "black") +
theme(axis.title = element_blank())
Пользовательский результат CRS:
EPSG:9311 результат:
@hamagust - я удалил свой предыдущий комментарий, поскольку меня исправили. st_cast()
и st_union()
на самом деле не нужны. По какой-то причине, когда я пробовал это раньше, это работало только с объектом LINESTRING. После изучения вопросов, которые вы подняли в своем комментарии, кажется, что единственное требование для правильного построения линий/кривых - это сделать их объектами SF. Соответственно обновили ответ. Это верно независимо от того, используете ли вы geom_curve()
или geom_spatial_segment()
. Спасибо за указание на это.
Дорогой @LTyrone, спасибо за помощь! Первоначально я думал, что решение будет включать использование «st_cast» с «LINESTRING», но я изо всех сил пытался реализовать его из-за моих ограниченных знаний об объектах научной фантастики. Ваш код кажется идеальным решением моей проблемы. Я заметил, что вы извлекли
st_coordinates
из геометрий, вычисленных вtranspoint=st_as_sf(point_df,...
в вашем коде. Я не совсем понимаю функцииst_union()
иst_cast()
. Не могли бы вы объяснить, необходимы ли они?