ggplot 在一个图例中使用 "colour" 和 "fill" 作为 astetic 到同一时间

ggplot with different types in one legend by using "colour" and "fill" as astetic to same time

我在 ggplot 中绘制图例时遇到问题。我已经在网上搜索了几个小时来寻找解决方案,但还没有找到任何解决方案。

我正在尝试使用不同的线和多边形 shapefile 创建一个 ggplot。我使用函数 geom_sf() 绘制 shapefile。画图不是问题。我的问题与图例创建有关。

目标是创建一个具有单一图例并具有所有不同 shapefile 类型的图,包括它们的颜色和/或填充 astetic。这意味着,线形在图例中应表示为简单的线,多边形应表示为简单的多边形。这些我都做到了。

主要困难在于其中一个 polygonshape(为了更好的可读性)不仅使用“填充”参数作为 astetic,还使用“颜色”参数。

如果我创建一个绘图,其中所有多边形始终使用填充参数作为 astetic,所有线形始终使用 color 参数作为 astetic,一切正常。但是,一旦我在同时使用两个参数的图中包含一个多边形形状,它就会破坏整个图例结构。在这种情况下,例如,线条 shapefile 的符号突然有一个框架 and/or 背景,尽管它们不应该有一个。

用 ggplot 构建这样的东西是不可能的还是我只是笨拙?

总结:

类似于下面创建的最小示例,我想在此图中创建一个图例,其中:

如何让它工作?

library(ggplot2)
library(sf)

poly1 <- cbind(lon = c(5, 6, 7, 5), lat = c(52, 53, 51, 52))
poly2 <- cbind(lon = c(3, 5, 7, 3), lat = c(50, 52, 50, 50))

poly1 <- st_sf(st_sfc(st_polygon(list(poly1))))
poly2 <- st_sf(st_sfc(st_polygon(list(poly2))))
line <- st_sf(st_sfc(list(st_linestring(cbind(lon = c(5.5, 4.5), lat = c(53.5, 54.5))))))

ggplot() +
  geom_sf(data = poly1, 
          aes(fill = "A"),
          colour = NA,
          show.legend = "polygon") +
  geom_sf(data = poly2, 
          aes(fill = "B", 
              colour = "B"),
          show.legend = "polygon") +
  geom_sf(data = line, 
          aes(colour = "C"), 
          show.legend = "line") +
  scale_fill_manual(name="Legend", 
                    values = c("A" = "yellow", "B" = "green"), 
                    guide = guide_legend(override.aes = list(linetype = c("blank","blank"), shape = NA))
                    ) +
  scale_colour_manual(name="Legend",values = c("B" = "blue", "C" = "purple"),
                     guide = guide_legend(override.aes = list(linetype = c(0, 1)))) +
  theme_bw()

您可以使用 ggnewscale 并为每个 geom 层设置键字形来做到这一点:

ggplot() +
  geom_sf(data = poly1, 
          aes(fill = "A"),
          colour = NA,
          key_glyph = "rect") +
  scale_fill_manual(values = "yellow", name = NULL, 
                      guide = guide_legend(order = 1)) +
  ggnewscale::new_scale_fill() +
  ggnewscale::new_scale_color() +
  geom_sf(data = poly2, 
          aes(fill = "B", 
              colour = "B"),
          key_glyph = "polygon") +
  scale_color_manual(values = "blue", name = NULL, 
                      guide = guide_legend(order = 2)) +
  scale_fill_manual(values = "green", name = NULL, 
                      guide = guide_legend(order = 2)) +
  ggnewscale::new_scale_color() +
  geom_sf(data = line, 
          aes(colour = "C"), 
          key_glyph = "path") +
  scale_colour_manual(values = "purple", name = NULL, 
                      guide = guide_legend(order = 3)) +
  theme_bw()

我也喜欢@AllanCameron 的回答,但这里有一种方法可以在没有 ggnewscale.

的情况下做同样的事情

你总共有三把钥匙。对于 AB,它们都应该表示为多边形,但是 B 应该有一个边框和A不应该。 C 是奇数,因为它应该在图例中由一个单独的符号表示。

因此,这里的做法是让AB属于同一个图例(一个来自color=的组合)和 fill=),并将 C 保留在单独的图例中。

对于AB,我在aes()中指定了fill=color=两者,然后使用 scale_*_manual() 函数为两者锻炼颜色。

如果您在 aes() 内将 color= 分配给 C,您将强制 ggplot2 使用 [= 将其推入图例46=]A 和 B。这意味着我们需要在不同的审美下为 C 指定图例!在这种情况下,我使用 alpha=,但同样适用于 linetypesize

p <-
ggplot() +
  geom_sf(
    data = poly1, aes(fill = "A", colour = "A")) +
  geom_sf(
    data = poly2, aes(fill = "B", colour = "B")) +
  geom_sf(
    data = line, aes(alpha = "C"), colour = "purple") +
  
  scale_fill_manual(
    name="Legend", values = c("A" = "yellow", "B" = "green")) +
  scale_colour_manual(
    name="Legend", values = c("A" = "transparent", "B" = "blue", "C" = "purple")) +
  scale_alpha_manual(name=NULL, values=1) +

  guides(
    fill=guide_legend(order=1, override.aes = list(linetype = c(0,1))),
    colour= "none"
  ) +
  
  theme_bw()
p

为了使图例彼此更接近,您可以通过 theme() 指定 legend.spacing。你必须玩弄这个数字,但它有效。在这种情况下,使 legend.background 透明也很重要,因为图例相互夹在一起。

p + theme(
  legend.background = element_rect(fill=NA),
  legend.spacing = unit(-15, "pt")
  )