如何使用 ggplot 将图表添加到空间地图?

How could I add a graph to a spatial map using ggplot?

我有一张加拿大地区的地图,是我使用 ggplot 和 sf 包创建的。在地图上的特定点,我想使用该位置的数据添加一条线,指向图表的标注。我知道如何在传单中弹出窗口,但这需要是一份在我打印出硬拷贝时可以使用的文档。我也可以在 Illustrator 中完成,但最好用它编码!

有什么想法吗?

这是一些示例代码

library(ggmap)
library(sf)
library(tidyverse)

# example point object
point.forMap = data.frame(lon=-111.19, lat=56.55, ID="1A3") %>% 
  st_as_sf(coords=c("lon", "lat")) %>% 
  st_set_crs(4326)
# Example data that I want to add as a graph
data.forGraph = data.frame(ID=rep("1A3", 30), xval=runif(30, min=5, max=20), yval=runif(30, min=0, max=20) )
# data for the background of the map  
background_map = get_map(location=c(lon=-111.19, lat=56.5), zoom=8,maptype="satellite")

# plot the map
ggmap(background_map)+
  geom_sf(data=point.forMap, inherit.aes = FALSE, size=8, color="red")
# plot the graph
ggplot(data=data.forGraph)+
  geom_point(aes(x=xval, y=yval))

现在的问题是,如何让图表显示在地图上该点附近的某处? data.forGraph 和 point.forMap 都有一个 ID 列,我们可以在上面加入两个数据框,但我不确定从这里去哪里。

将图表添加到地图的一个选项是通过 annotation_custom。在我看来,棘手的部分是获得正确的位置,这可能需要一些摆弄才能获得漂亮的情节。

但基本上你可以

  1. 将您的点坐标、图表边界框的坐标和图表本身放入 tibble
  2. 使用辅助函数为一个图表添加 annotation_custom 层,其中包括图表本身以及将图表连接到点的线段。
  3. 使用 purrr::pmap 遍历我们在步骤 1 中设置的 tibble 的行以将图表添加到地图

注意:我添加了第二点来说明添加多个图表的一般方法。

library(ggmap)
library(sf)
library(ggplot2)
library(dplyr)
library(purrr)

point_df <- data.frame(lon = c(-111.19, -110), 
                       lat = c(56.55, 56),
                       ID =  c("1A3", "1A4"))
           
point.forMap <- point_df %>%
  st_as_sf(coords = c("lon", "lat")) %>%
  st_set_crs(4326)

# Example data that I want to add as a graph
data.forGraph <- data.frame(ID = rep("1A3", 30), xval = runif(30, min = 5, max = 20), yval = runif(30, min = 0, max = 20))
# data for the background of the map
background_map <- get_map(location = c(lon = -111.19, lat = 56.5), zoom = 8, maptype = "satellite")

# plot the map
p1 <- ggmap(background_map) +
  geom_sf(data = point.forMap, inherit.aes = FALSE, size = 8, color = "red")
#> Coordinate system already present. Adding new coordinate system, which will replace the existing one.

# Set up a tibble containing coordinates of points, bounding box of the charts and the charts
p2 <- tibble(
  ID =  c("1A3", "1A4"),
  p = list(ggplot(data = data.forGraph) + geom_point(aes(x = xval, y = yval)), 
           ggplot(data = data.forGraph) + geom_point(aes(x = xval, y = yval)))
)

p2 <- left_join(p2, point_df, by = "ID") %>% 
  rename(x = lon, y = lat) %>% 
  mutate(xmin = x - 1, xmax = x - .2,
         ymin = y + .25, ymax = y + .75) %>% 
  select(-ID)

# Helper function
add_chart <- function(p, x, y, xmin, xmax, ymin, ymax) {
  list(
    # Add chart
    annotation_custom(
      grob = ggplotGrob(p),
      xmin = xmin, xmax = xmax, ymin = ymin, ymax = ymax
    ),
    # Add segment
    annotation_custom(
      grob = grid::segmentsGrob(
        y0 = unit(1, "npc"), y1 = unit(0, "npc"),
        gp = grid::gpar(lwd = unit(1, "lines"), col = "white")
      ),
      xmin = x, xmax = xmax, ymin = y, ymax = ymin
    )
  )
}

# Add charts to the map
p1 +
  purrr::pmap(p2, add_chart)