3

I would like to first draw a bunch of areas and then have the resulting overall area be displayed with the same single alpha value. So instead of this:

library(tidyverse)

dat <- tribble(
  ~xmin, ~xmax, ~ymin, ~ymax,
     10,    30,    10,    30,
     20,    40,    20,    40,
     15,    35,    15,    25,
     10,    15,    35,    40
)

ggplot() +
  geom_rect(data = dat,
            aes(
              xmin = xmin,
              xmax = xmax,
              ymin = ymin,
              ymax = ymax
            ),
            alpha = 0.5)

I would want to have this as my outcome:

Created on 2022-07-26 by the reprex package (v2.0.1)

I feel like the answer to my question may be similar to the one in this thread but I don't fully understand it and thus am not sure. Also note that I used geom_rect() for the reprex, but ultimately I want this to work for ggforce::geom_circle().

EDIT 1

Quinten's first answer, which points to scale_alpha(range = ..., limits = ...), is unfortunately not answering my question, since it can apparently only result in non-transparent areas.

EDIT 2

Quinten's updated answer is a workaround I could accept for the reprex above. However, as I've mentioned above, too, I want this to work for ggforce::geom_circle(). Unfortunately, I guess I have to be more specific now and create another reprex. (Sorry)

library(ggforce)
#> Lade nötiges Paket: ggplot2

dat <- data.frame(
  x = c(1, 1.3, 1.6),
  y = c(1, 1, 1),
  circle = c("yes", "yes", "no")
)

ggplot() +
  coord_equal() +
  theme_classic() +
  geom_circle(
    data = subset(dat, circle == "yes"),
    aes(x0 = x, y0 = y, r = 0.5, alpha = circle),
    fill = "grey",
    color = NA,
    show.legend = TRUE
  ) +
  geom_point(
    data = dat,
    aes(x, y, color = circle)
  ) +
  scale_color_manual(
    values = c("yes" = "blue", "no" = "red")
  ) +
  scale_alpha_manual(
    values = c("yes" = 0.25, "no" = 0)
  )

Created on 2022-08-17 by the reprex package (v2.0.1)

Paul Schmidt
  • 1,072
  • 10
  • 23

3 Answers3

3

ggblend

I also saw ggblend few moments ago, but wasn't sure it would fix your problem, luckily it does! You can do two things:

  1. You can change your Graphics in Rstudio to "Cairo" like this:

enter image description here

Code:

#remotes::install_github("mjskay/ggblend")
library(ggblend)
library(ggforce)

# reprex 1 ----------------------------------------------------------------
library(tidyverse)

dat <- tribble(
  ~xmin, ~xmax, ~ymin, ~ymax,
  10,    30,    10,    30,
  20,    40,    20,    40,
  15,    35,    15,    25,
  10,    15,    35,    40
)

p1 <- ggplot() +
  geom_rect(data = dat,
            aes(
              xmin = xmin,
              xmax = xmax,
              ymin = ymin,
              ymax = ymax
            ),
            alpha = 0.3) %>% blend("source")

p1

enter image description here

# reprex 2 ----------------------------------------------------------------
dat <- data.frame(
  x = c(1, 1.3, 1.6),
  y = c(1, 1, 1),
  circle = c("yes", "yes", "no")
)

p2 <- ggplot() +
  coord_equal() +
  theme_classic() +
  geom_circle(
    data = subset(dat, circle == "yes"),
    aes(x0 = x, y0 = y, r = 0.5, alpha = circle),
    fill = "grey",
    color = NA,
    show.legend = TRUE
  ) %>% blend("source") +
  geom_point(
    data = dat,
    aes(x, y, color = circle)
  ) +
  scale_color_manual(
    values = c("yes" = "blue", "no" = "red")
  ) +
  scale_alpha_manual(
    values = c("yes" = 0.25, "no" = 0)
  )

p2
#ggsave(plot = p2, "p2.pdf", device = cairo_pdf)

enter image description here

  1. You can save the object as a png with type = "cairo" like this:
library(ggblend)
library(ggforce)

# reprex 1 ----------------------------------------------------------------
library(tidyverse)

dat <- tribble(
  ~xmin, ~xmax, ~ymin, ~ymax,
  10,    30,    10,    30,
  20,    40,    20,    40,
  15,    35,    15,    25,
  10,    15,    35,    40
)

p1 <- ggplot() +
  geom_rect(data = dat,
            aes(
              xmin = xmin,
              xmax = xmax,
              ymin = ymin,
              ymax = ymax
            ),
            alpha = 0.3) %>% blend("source")
#> Warning: Your graphics device, "quartz_off_screen", reports that blend = "source" is not supported.
#>  - If the blending output IS NOT as expected (e.g. geoms are not being
#>    drawn), then you must switch to a graphics device that supports
#>    blending, like png(type = "cairo"), svg(), or cairo_pdf().
#>  - If the blending output IS as expected despite this warning, this is
#>    likely a bug *in the graphics device*. Unfortunately, several
#>    graphics do not correctly report their capabilities. You may wish to
#>    a report a bug to the authors of the graphics device. In the mean
#>    time, you can disable this warning via options(ggblend.check_blend =
#>    FALSE).
#>  - For more information, see the Supported Devices section of
#>    help('blend').

png(filename = "plot1.png", type = "cairo")
# Output in your own folder:

p1
dev.off()

enter image description here 2

#ggsave(plot = p1, "p1.pdf", device = cairo_pdf)

# reprex 2 ----------------------------------------------------------------
dat <- data.frame(
  x = c(1, 1.3, 1.6),
  y = c(1, 1, 1),
  circle = c("yes", "yes", "no")
)

p2 <- ggplot() +
  coord_equal() +
  theme_classic() +
  geom_circle(
    data = subset(dat, circle == "yes"),
    aes(x0 = x, y0 = y, r = 0.5, alpha = circle),
    fill = "grey",
    color = NA,
    show.legend = TRUE
  ) %>% blend("source") +
  geom_point(
    data = dat,
    aes(x, y, color = circle)
  ) +
  scale_color_manual(
    values = c("yes" = "blue", "no" = "red")
  ) +
  scale_alpha_manual(
    values = c("yes" = 0.25, "no" = 0)
  )
#> Warning: Your graphics device, "quartz_off_screen", reports that blend = "source" is not supported.
#>  - If the blending output IS NOT as expected (e.g. geoms are not being
#>    drawn), then you must switch to a graphics device that supports
#>    blending, like png(type = "cairo"), svg(), or cairo_pdf().
#>  - If the blending output IS as expected despite this warning, this is
#>    likely a bug *in the graphics device*. Unfortunately, several
#>    graphics do not correctly report their capabilities. You may wish to
#>    a report a bug to the authors of the graphics device. In the mean
#>    time, you can disable this warning via options(ggblend.check_blend =
#>    FALSE).
#>  - For more information, see the Supported Devices section of
#>    help('blend').

png(filename = "plot2.png", type = "cairo")

# Output in your folder
p2

enter image description here

dev.off()
#ggsave(plot = p2, "p2.pdf", device = cairo_pdf)

Created on 2022-08-17 with reprex v2.0.2

Update

What you could do is take the UNION of the areas using st_union from the sf package so you get one area instead of overlapping areas like this:

library(tidyverse)
library(sf)

dat <- tribble(
  ~xmin, ~xmax, ~ymin, ~ymax,
  10,    30,    10,    30,
  20,    40,    20,    40,
  15,    35,    15,    25,
  10,    15,    35,    40
)

area1 <- dat %>%
  slice(1) %>%
  as_vector() %>%
  st_bbox() %>%
  st_as_sfc()

area2 <- dat %>%
  slice(2) %>%
  as_vector() %>%
  st_bbox() %>%
  st_as_sfc()

area3 <- dat %>%
  slice(3) %>%
  as_vector() %>%
  st_bbox() %>%
  st_as_sfc()

area4 <- dat %>%
  slice(4) %>%
  as_vector() %>%
  st_bbox() %>%
  st_as_sfc()

all_areas <- st_union(area1, area2) %>%
  st_union(area3) %>%
  st_union(area4)

ggplot(all_areas) +
  geom_sf(alpha = 0.5, fill = "grey", colour = "grey") +
  theme(legend.position = "none")

ggplot(all_areas) +
  geom_sf(alpha = 0.8, fill = "grey", colour = "grey") +
  theme(legend.position = "none")

Created on 2022-08-17 by the reprex package (v2.0.1)

First answer

Maybe you want this where you can use a scale_alpha with range and limits to keep the area in the same alpha like this:

library(tidyverse)

dat <- tribble(
  ~xmin, ~xmax, ~ymin, ~ymax,
  10,    30,    10,    30,
  20,    40,    20,    40,
  15,    35,    15,    25,
  10,    15,    35,    40
)

ggplot() +
  geom_rect(data = dat,
            aes(
              xmin = xmin,
              xmax = xmax,
              ymin = ymin,
              ymax = ymax,
              alpha = 0.5
            )) +
  scale_alpha(range = c(0, 1), limits = c(0, 0.5)) +
  theme(legend.position = "none")

Created on 2022-07-26 by the reprex package (v2.0.1)

Quinten
  • 35,235
  • 5
  • 20
  • 53
  • Correct me if I'm wrong, but this approach can only make the entire area non-transparent (alpha = 1), but not make the entire area semi-transparent with the same alpha-value e.g. 0.5? – Paul Schmidt Jul 26 '22 at 09:19
  • 1
    @PaulSchmidt, Yes you are right, so it is a bit tricky to keep the alpha 0.5. So this fixed to have one area. – Quinten Jul 26 '22 at 10:52
  • 1
    @PaulSchmidt, I updated my answer. You could UNION the areas to get one area using `st_union` from the `sf` package. As you can see in the answer, the alpha is now the same and you can even change it. – Quinten Aug 17 '22 at 11:44
  • this is indeed an answer I would accept for the reprex I have above. However, as you can see in my 2nd edit now, it is my fault that my reprex was not precise enough, as your solution would not translate (I think?) to when I want to ultimately use ggforce::geom_circle(). – Paul Schmidt Aug 17 '22 at 12:32
2

With sf and purrr:

  • Create a list of polygons (rectangles)
  • Create a multipolygon as union of these rectangles
  • Display the result with geom_sf
library(tidyverse)
library(sf)

dat <- tribble(
  ~xmin, ~xmax, ~ymin, ~ymax,
  10,    30,    10,    30,
  20,    40,    20,    40,
  15,    35,    15,    25,
  10,    15,    35,    40
)

sf_rectangle <- function(xmin,xmax,ymin,ymax) {
  st_polygon(list(rbind(c(xmin, ymin), c(xmin, ymax), c(xmax, ymax), c(xmax, ymin), c(xmin, ymin))))
}

rect_union <- dat %>% purrr::pmap(sf_rectangle) %>%
                      purrr::reduce(st_union)


ggplot(rect_union) + geom_sf(alpha=.2)

Waldi
  • 39,242
  • 6
  • 30
  • 78
2

Okay, I randomly stumbled upon {ggblend} and its blend() function. Basically, I only need to add %>% blend("source") right after the geom_ that would overlay the multiple semi-transparent areas and it does exactly what I was looking for.

However, it does come with the limitation that can be seen in the Warning of the reprex below, i.e. you cannot actually get a preview of the plot in RStudio and even exporting it to some file types may not be possible, since not all devices can handle this. However, exporting to pdf via ggsave(..., device = cairo_pdf) works fine and I am showing screenshots of it below.

Nevertheless, I will leave this question open until I either find a way to also export .png and .svg or somebody comes up with an alternative solution that works for both reprex 1 and reprex 2.

# remotes::install_github("mjskay/ggblend")
library(ggblend)
library(ggforce)
library(tidyverse)


# reprex 1 ----------------------------------------------------------------
library(tidyverse)

dat <- tribble(
  ~xmin, ~xmax, ~ymin, ~ymax,
  10,    30,    10,    30,
  20,    40,    20,    40,
  15,    35,    15,    25,
  10,    15,    35,    40
)

p1 <- ggplot() +
  geom_rect(data = dat,
            aes(
              xmin = xmin,
              xmax = xmax,
              ymin = ymin,
              ymax = ymax
            ),
            alpha = 0.3) %>% blend("source")
#> Warning: Your graphics device, "png", reports that blend = "source" is not supported.
#>  - If the blending output IS NOT as expected (e.g. geoms are not being
#>    drawn), then you must switch to a graphics device that supports
#>    blending, like png(type = "cairo"), svg(), or cairo_pdf().
#>  - If the blending output IS as expected despite this warning, this is
#>    likely a bug *in the graphics device*. Unfortunately, several
#>    graphics do not correctly report their capabilities. You may wish to
#>    a report a bug to the authors of the graphics device. In the mean
#>    time, you can disable this warning via options(ggblend.check_blend =
#>    FALSE).
#>  - For more information, see the Supported Devices section of
#>    help('blend').

ggsave(plot = p1, "p1.pdf", device = cairo_pdf)
#> Saving 7 x 5 in image

enter image description here


# reprex 2 ----------------------------------------------------------------
dat <- data.frame(
  x = c(1, 1.3, 1.6),
  y = c(1, 1, 1),
  circle = c("yes", "yes", "no")
)

p2 <- ggplot() +
  coord_equal() +
  theme_classic() +
  geom_circle(
    data = subset(dat, circle == "yes"),
    aes(x0 = x, y0 = y, r = 0.5, alpha = circle),
    fill = "grey",
    color = NA,
    show.legend = TRUE
  ) %>% blend("source") +
  geom_point(
    data = dat,
    aes(x, y, color = circle)
  ) +
  scale_color_manual(
    values = c("yes" = "blue", "no" = "red")
  ) +
  scale_alpha_manual(
    values = c("yes" = 0.25, "no" = 0)
  )
#> Warning: Your graphics device, "png", reports that blend = "source" is not supported.
#>  - If the blending output IS NOT as expected (e.g. geoms are not being
#>    drawn), then you must switch to a graphics device that supports
#>    blending, like png(type = "cairo"), svg(), or cairo_pdf().
#>  - If the blending output IS as expected despite this warning, this is
#>    likely a bug *in the graphics device*. Unfortunately, several
#>    graphics do not correctly report their capabilities. You may wish to
#>    a report a bug to the authors of the graphics device. In the mean
#>    time, you can disable this warning via options(ggblend.check_blend =
#>    FALSE).
#>  - For more information, see the Supported Devices section of
#>    help('blend').

ggsave(plot = p2, "p2.pdf", device = cairo_pdf)
#> Saving 7 x 5 in image

Created on 2022-08-17 with reprex v2.0.2

enter image description here

Paul Schmidt
  • 1,072
  • 10
  • 23
  • 1
    Great you find your solution. I also saw `ggblend` package and wasn't sure if it would fit your problem. I added some code to my answer to save the output as png. You can use `png` with `type = "cairo"`. – Quinten Aug 17 '22 at 14:18