设为首页|收藏本站|
开启左侧

[问答] 使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ...

[复制链接]
59781 0
没事逛逛双子 发表于 2023-2-27 07:32:21 | 只看该作者 打印 上一主题 下一主题
 
本教程是三部分系列中的第三部分:
1、世界地图说明的一般概念
2、添加附加层:点和多边形的示例
3、复杂地图的定位和布局(本文档)
在介绍了基本地图概念和 ggplot2 中实现的灵活图层方法之后,这部分说明了如何实现复杂的布局,例如使用地图插图或组合多个地图。 根据需要显示的视觉信息,可能需要安排地图及其相应数据以创建易于阅读的图形表示。 本教程将提供不同的方法来安排情节中的地图,以使描绘的信息更具美感,最重要的是,更好地传达信息。
入门

许多 R 包可从 CRAN(综合 R 存档网络)获得,它是 R 包的主要存储库。 可以安装本系列教程所需的完整软件包列表:
install.packages(c("cowplot", "googleway", "ggplot2", "ggrepel",
"ggspatial", "libwgeom", "sf", "rnaturalearth", "rnaturalearthdata"))我们首先加载所有地图所需的基本包,即 ggplot2 和 sf。 我们还建议为 ggplot2 (theme_bw) 使用经典的 dark-on-light 主题,它更适合地图:
library("ggplot2")
theme_set(theme_bw())
library("sf")包 rnaturalearth 提供了整个世界国家的地图。 使用 ne_counties 拉取国家数据并选择比例。 对于 returnclass,设置可以是“sf”和/或“sp”:
library("rnaturalearth")
library("rnaturalearthdata")

world <- ne_countries(scale='medium',returnclass = 'sf')
class(world)

## [1] "sf"  
## [1] "data.frame"一般概念

合并子图有2种方案:
使用“grobs”,即来自 ggplot2 的图形对象,可以使用绘图坐标将其插入绘图区域;
使用包 cowplot 中的 ggdraw,它允许根据相对位置在图形设备上的任何位置(包括外边距)安排新绘图。
这是一个简单的例子,说明了两者之间的区别以及它们的使用。 我们首先准备一个简单的图形,显示 11 个点,具有规则的轴和网格 (g1):
(g1  <- qplot(0:10, 0:10))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第1张图片
ggplot2 中的图形可以像任何其他 R 对象一样保存。 这允许稍后重用和更新图表。 例如,我们在 g1_void 中存储这个图的简化版本,只有点数据,但没有装饰:
(g1_void <- g1 + theme_void() + theme(panel.border = element_rect(colour = "black",
    fill = NA)))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第2张图片
函数 annotation_custom 允许以 grobs 的形式将图形排列在一起(使用 ggplotGrob 生成)。 这里我们首先绘制完整图 g1,然后在绘图区域的左上角和右下角添加两个 g1_void 实例(由 xmin、xmax、ymin 和 ymax 定义):
使用 grobs 和 annotation_custom:
g1 +
    annotation_custom(
        grob = ggplotGrob(g1_void),
        xmin = 0,
        xmax = 3,
        ymin = 5,
        ymax = 10
    ) +
    annotation_custom(
        grob = ggplotGrob(g1_void),
        xmin = 5,
        xmax = 10,
        ymin = 0,
        ymax = 3
    )
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第3张图片
使用 cowplot 包中的 ggdraw 函数的替代方法允许在整个绘图设备中使用相对定位。 在这种情况下,我们在 g1 之上构建图形,但对 ggdraw 的初始调用实际上可以留空以在空图上安排子图。 子图的宽度和高度是从 0 到 1 的相对值,以及 x 和 y 坐标([0,0] 是左下角,[1,1] 是右上角)。 请注意,在这种情况下,子图不限于实际绘图区域,而是可以添加到设备上的任何位置:
library(“cowplot”) ggdraw(g1) +
draw_plot(g1_void, width = 0.25, height = 0.5, x = 0.02, y = 0.48) +
draw_plot(g1_void, width = 0.5, height = 0.25, x = 0.75, y = 0.09)
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第4张图片
并排或在网格上的几张地图

在本节中,我们将介绍一种在网格上并排排列多个地图的方法。 虽然这可以在导出每个单独的地图后手动实现,但这允许 1) 为此目的具有可重现的代码; 2) 完全控制各个地图的定位方式。
在此示例中,墨西哥湾的放大图位于世界地图(包括其图例)的一侧。 这说明了如何使用自定义网格,可以使用更多元素使自定义网格变得更加复杂。
我们现在准备子图,从世界地图开始,在墨西哥湾周围有一个矩形(有关如何准备此地图的详细信息,请参见第 1 节和第 2 节):
准备子图,#1 世界地图:
(gworld <- ggplot(data = world) +
  geom_sf(aes(fill = region_wb)) +
  geom_rect(xmin = -102.15, xmax = -74.12, ymin = 7.65, ymax = 33.97,
     fill = NA, colour = "black", size = 1.5) +
  scale_fill_viridis_d(option = "plasma") +
  theme(panel.background = element_rect(fill = "azure"),
     panel.border = element_rect(fill = NA)))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第5张图片
第二张地图非常相似,但以墨西哥湾为中心(使用 coord_sf):
(ggulf <- ggplot(data = world) +
  geom_sf(aes(fill = region_wb)) +
  annotate(geom = "text", x = -90, y = 26, label = "Gulf of Mexico",
     fontface = "italic", color = "grey22", size = 6) +
  coord_sf(xlim = c(-102.15, -74.12), ylim = c(7.65, 33.97), expand = FALSE) +
  scale_fill_viridis_d(option = "plasma") +
  theme(legend.position = "none", axis.title.x = element_blank(),
     axis.title.y = element_blank(), panel.background = element_rect(fill = "azure"),
     panel.border = element_rect(fill = NA)))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第6张图片
最后我们只需要对这两个图进行排列即可,使用annotation_custom可以轻松搞定。 请注意,在这种情况下,我们使用对 ggplot 的空调用将两个地图定位在空背景(大小为 3.3 × 1)上:
ggplot() +
    coord_equal(xlim = c(0, 3.3), ylim = c(0, 1), expand = FALSE) +
    annotation_custom(ggplotGrob(plot1), xmin = 0, xmax = 1.5, ymin = 0,
                    ymax = 1) +
    annotation_custom(ggplotGrob(plot2), xmin = 1.5, xmax = 3, ymin = 0,
                    ymax = 1) +
    theme_void()
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第7张图片
第二种方法使用 cowplot 中的 plot_grid 函数来排列 ggplot 图形,非常通用。 任何 ggplot 图都可以像上图一样排列。 几个参数调整地图放置,例如分别定义行数和列数的 nrow 和 ncol,以及确定每个地图的相对宽度的 rel_widths。 在我们的例子中,我们希望两个地图都在一行中,第一个地图 gworld 的相对宽度为 2.3,而地图 ggulf 的相对宽度为 1。
plot_grid(gworld, ggulf, nrow = 1, rel_widths = c(2.3, 1))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第8张图片
参数 align 可用于水平(align = "h")、垂直(align = "v")或两者(align = "hv")对齐子图,以便轴和绘图区域相互匹配。 还要注意 get_legend (cowplot) 的存在,它提取图的图例,然后可以将其用作任何对象(例如,将其精确放置在地图上的某个位置)。
上面创建的两个地图(使用 ggplot 和 annotation_custom,或使用 cowplot 和 plot_grid)可以像往常一样使用 ggsave 保存(在绘制所需地图后使用):
ggsave("grid.pdf", width = 15, height =  5)地图插图

要直接在背景地图上插入地图,前面介绍的两种解决方案都是可行的(根据相对或绝对坐标,人们可能更喜欢其中一种)。 我们将使用美国 50 个州的地图来说明这一点,包括阿拉斯加和夏威夷(注意:阿拉斯加和夏威夷都不会按比例绘制)。
我们首先使用参考美国国家地图集等积投影 (CRS 2163) 准备大陆各州。 主要技巧是在使用的投影中找到正确的坐标,这可能会导致在每一步进行一些微调。 在这里,我们有意扩大绘图区域的范围,为插图留出一些空间:
usa <- subset(world, admin == "United States of America")
(mAInland <- ggplot(data = usa) +
     geom_sf(fill = "cornsilk") +
     coord_sf(crs = st_crs(2163), xlim = c(-2500000, 2500000), ylim = c(-2300000,
         730000)))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第9张图片
阿拉斯加地图是使用参考阿拉斯加阿尔伯斯投影 (CRS 3467) 绘制的。 请注意,使用 datum = NA 删除了格线和坐标:
(alaska <- ggplot(data = usa) +
     geom_sf(fill = "cornsilk") +
     coord_sf(crs = st_crs(3467), xlim = c(-2400000, 1600000), ylim = c(200000,
         2500000), expand = FALSE, datum = NA))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第10张图片
现在是夏威夷地图,使用参考旧夏威夷投影 (CRS 4135) 绘制:
(hawaii  <- ggplot(data = usa) +
     geom_sf(fill = "cornsilk") +
     coord_sf(crs = st_crs(4135), xlim = c(-161, -154), ylim = c(18,
         23), expand = FALSE, datum = NA))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第11张图片
在函数 annotation_custom 的帮助下,只能使用 ggplot2 创建最终地图。 在这种情况下,我们使用基于上述子集大小的任意比率(注意基于最大减最小 x/y 坐标的差异):
mainland +
annotation_custom(
      grob = ggplotGrob(alaska),
      xmin = -2750000,
      xmax = -2750000 + (1600000 - (-2400000))/2.5,
      ymin = -2450000,
      ymax = -2450000 + (2500000 - 200000)/2.5
  ) +
  annotation_custom(
      grob = ggplotGrob(hawaii),
      xmin = -1250000,
      xmax = -1250000 + (-154 - (-161))*120000,
      ymin = -2450000,
      ymax = -2450000 + (23 - 18)*120000
  )
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第12张图片
使用 cowplot 和函数 draw_plot 可以通过相同的逻辑实现相同的效果,在这种情况下,首先定义阿拉斯加和夏威夷的比率会更容易:
(ratioAlaska <- (2500000 - 200000) / (1600000 - (-2400000)))

## [1] 0.575

(ratioHawaii  <- (23 - 18) / (-154 - (-161)))

## [1] 0.7142857

ggdraw(mainland) +
    draw_plot(alaska, width = 0.26, height = 0.26 * 10/6 * ratioAlaska,
        x = 0.05, y = 0.05) +
    draw_plot(hawaii, width = 0.15, height = 0.15 * 10/6 * ratioHawaii,
        x = 0.3, y = 0.05)
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第13张图片
同样,两个图都可以使用 ggsave 保存:
ggsave("map-us-ggdraw.pdf", width = 10, height = 6)用箭头连接的几张地图

为了使地图安排更加生动,可以使用箭头将观众的眼睛引导到情节中的特定区域。 下一个示例将创建一个地图,其中包含放大的区域,由箭头连接。
我们首先创建一般地图,这里是带有站点位置的佛罗里达地图(有关详细信息,请参见教程 2):
sites <- st_as_sf(data.frame(longitude = c(-80.15, -80.1), latitude = c(26.5,
26.8)), coords = c("longitude", "latitude"), crs = 4326,
agr = "constant")


(florida <- ggplot(data = world) +
     geom_sf(fill = "antiquewhite1") +
     geom_sf(data = sites, size = 4, shape = 23, fill = "darkred") +
     annotate(geom = "text", x = -85.5, y = 27.5, label = "Gulf of Mexico",
         color = "grey22", size = 4.5) +
     coord_sf(xlim = c(-87.35, -79.5), ylim = c(24.1, 30.8)) +
     xlab("Longitude")+ ylab("Latitude")+
     theme(panel.grid.major = element_line(colour = gray(0.5), linetype = "dashed",
         size = 0.5), panel.background = element_rect(fill = "aliceblue"),
         panel.border = element_rect(fill = NA)))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第14张图片
然后我们准备两个研究地点(这里简称为 A 和 B):
(siteA <- ggplot(data = world) +
     geom_sf(fill = "antiquewhite1") +
     geom_sf(data = sites, size = 4, shape = 23, fill = "darkred") +
     coord_sf(xlim = c(-80.25, -79.95), ylim = c(26.65, 26.95), expand = FALSE) +
     annotate("text", x = -80.18, y = 26.92, label= "Site A", size = 6) +
     theme_void() +
     theme(panel.grid.major = element_line(colour = gray(0.5), linetype = "dashed",
         size = 0.5), panel.background = element_rect(fill = "aliceblue"),
         panel.border = element_rect(fill = NA)))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第15张图片
(siteB <- ggplot(data = world) +
     geom_sf(fill = "antiquewhite1") +
     geom_sf(data = sites, size = 4, shape = 23, fill = "darkred") +
     coord_sf(xlim = c(-80.3, -80), ylim = c(26.35, 26.65), expand = FALSE) +
     annotate("text", x = -80.23, y = 26.62, label= "Site B", size = 6) +
     theme_void() +
     theme(panel.grid.major = element_line(colour = gray(0.5), linetype = "dashed",
         size = 0.5), panel.background = element_rect(fill = "aliceblue"),
         panel.border = element_rect(fill = NA)))
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第16张图片
由于我们想使用箭头将两个子图连接到主地图,因此需要在绘制之前指定两个箭头的坐标。 我们准备一个 data.frame 存储开始和结束坐标(x 轴上的 x1 和 x2,y 轴上的 y1 和 y2):
arrowA <- data.frame(x1 = 18.5, x2 = 23, y1 = 9.5, y2 = 14.5)
arrowB <- data.frame(x1 = 18.5, x2 = 23, y1 = 8.5, y2 = 6.5)仅使用 ggplot,我们只需按照与之前相同的方法将几张地图并排放置,然后使用函数 geom_segment 和参数 arrow = arrow() 添加箭头:
ggplot() +
    coord_equal(xlim = c(0, 28), ylim = c(0, 20), expand = FALSE) +
    annotation_custom(ggplotGrob(florida), xmin = 0, xmax = 20, ymin = 0,
        ymax = 20) +
    annotation_custom(ggplotGrob(siteA), xmin = 20, xmax = 28, ymin = 11.25,
        ymax = 19) +
    annotation_custom(ggplotGrob(siteB), xmin = 20, xmax = 28, ymin = 2.5,
        ymax = 10.25) +
    geom_segment(aes(x = x1, y = y1, xend = x2, yend = y2), data = arrowA,
        arrow = arrow(), lineend = "round") +
    geom_segment(aes(x = x1, y = y1, xend = x2, yend = y2), data = arrowB,
        arrow = arrow(), lineend = "round") +
    theme_void()
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第17张图片
cowplot 包(带有 draw_plot)也可以用于类似的结果,语法可能更简单一些:
ggdraw(xlim = c(0, 28), ylim = c(0, 20)) +
    draw_plot(florida, x = 0, y = 0, width = 20, height = 20) +
    draw_plot(siteA, x = 20, y = 11.25, width = 8, height = 8) +
    draw_plot(siteB, x = 20, y = 2.5, width = 8, height = 8) +
    geom_segment(aes(x = x1, y = y1, xend = x2, yend = y2), data = arrowA,
        arrow = arrow(), lineend = "round") +
    geom_segment(aes(x = x1, y = y1, xend = x2, yend = y2), data = arrowB,
        arrow = arrow(), lineend = "round")
使用 R、sf 和 ggplot2 以编程方式绘制漂亮的地图——第 3 ... 第18张图片
同样,两个图都可以使用 ggsave 保存:
ggsave("florida-sites.pdf", width = 10, height = 7)


上一篇:夏威夷果
下一篇:这次轮到夏威夷了,拜登严重战略失误,给中国送了太多大礼
@



1.西兔生活网 CTLIVES 内容全部来自网络;
2.版权归原网站或原作者所有;
3.内容与本站立场无关;
4.若涉及侵权或有疑义,请点击“举报”按钮,其他联系方式或无法及时处理。
 
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

排行榜
活跃网友
返回顶部快速回复上一主题下一主题返回列表APP下载手机访问
Copyright © 2016-2028 CTLIVES.COM All Rights Reserved.  西兔生活网  小黑屋| GMT+8, 2024-3-29 04:07