Как использовать ggplot2::geom_segment() или ggspatial::geom_spatial_segment() для объектов научной фантастики, не сосредоточенных в Гринвиче?

Я хочу добавить пути на карту, созданную с помощью 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')

Не работает: карта с другим CRS или не в центре Гринвича.

Однако если я предпочитаю карту с Тихим океаном в центре или эллиптическую карту, они терпят неудачу.

Карта Тихоокеанского региона с 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?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
106
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Самый простой способ обойти это — создать научно-фантастическую версию ваших достопримечательностей, а затем создать в научной фантастике переменные 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 результат:

Дорогой @LTyrone, спасибо за помощь! Первоначально я думал, что решение будет включать использование «st_cast» с «LINESTRING», но я изо всех сил пытался реализовать его из-за моих ограниченных знаний об объектах научной фантастики. Ваш код кажется идеальным решением моей проблемы. Я заметил, что вы извлекли st_coordinates из геометрий, вычисленных в transpoint=st_as_sf(point_df,... в вашем коде. Я не совсем понимаю функции st_union() и st_cast(). Не могли бы вы объяснить, необходимы ли они?

hamagust 11.04.2024 22:01

@hamagust - я удалил свой предыдущий комментарий, поскольку меня исправили. st_cast() и st_union() на самом деле не нужны. По какой-то причине, когда я пробовал это раньше, это работало только с объектом LINESTRING. После изучения вопросов, которые вы подняли в своем комментарии, кажется, что единственное требование для правильного построения линий/кривых - это сделать их объектами SF. Соответственно обновили ответ. Это верно независимо от того, используете ли вы geom_curve() или geom_spatial_segment(). Спасибо за указание на это.

L Tyrone 11.04.2024 23:22

Другие вопросы по теме