rggplot2textpanel

How to add text outside the panel when using ggplot coord_polar()?


I want to create a plot that serves as a circular legend for a continuous variable. Additionally, I need to add text outside the panel, specifically below the x-axis (outside the circle). However, since I am using coord_polar(), functions like annotate() and geom_text() do not behave as they normally would. Does anyone have suggestions on how to address this issue?

Here is the data:

# DATA
aspect_class = data.frame(from = c(0, seq(22.5, 337.5, by = 45)),
                          to =   c(seq(22.5, 337.5, by = 45), 360),
                          mid = seq(0, 360, by = 45),
                          class = c("North (0°-22.5°)", 
                                    "Northeast (22.5°-67.5°)",
                                    "East (67.5° - 112.5°)",
                                    "Southeast (112.5°-157.5°)",
                                    "South (157.5°-202.5°)",
                                    "Southwest (202.5°-247.5°)",
                                    "West (247.5°-292.5°)",
                                    "Northwest (292.5°-337.5°)",
                                    "North (337.5°-360°)"))

degrees = seq(0, 360, by = 1)
values = sin(degrees * pi / 180)
aspect_legend_df = data.frame(degrees, values)
compas = data.frame(degrees = seq(0,315,45),
                    direction = c("N", "NE", "E", "SE", "S", "SW", "W", "NW"))

and the plot code:

# PLOT
ggplot() +
  geom_tile(data = aspect_legend_df, 
            aes(x = degrees, y = 0, fill = values), 
            width = 1, height = 0.1) +
  scale_x_continuous(breaks = compas$degrees, 
                     labels = ~ paste0(.x, "\u00B0"),
                     name = "Aspect [°]") +
  scale_fill_viridis_c() +
  geom_vline(data = aspect_class[aspect_class$mid %in% c(0, 90, 180, 270),],
             aes(xintercept = mid), 
             color = "black", linewidth = 0.8) +
  geom_vline(data = aspect_class[!aspect_class$mid %in% c(0, 90, 180, 270, 360),],
             aes(xintercept = mid), 
             color = "black", linewidth = 0.5, linetype = "dotdash") +
  annotate(geom = "text", x = compas$degrees, y = 0.06, label = "") +    
  coord_polar(start = 0) + 
  theme_minimal() +
  theme(legend.position = "none",
        axis.title = element_blank(),
        axis.text.y = element_blank(),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.text.x = element_text(size = 12, color = "black")) 

I want to get this:

plot

If I use annotate(geom = "text", x = compas$degrees, y = 0.06, label = compas$direction) + I get:

plot w annotate

If I use geom_text(data = compas,aes(x = degrees, y = 0.06, label = direction), inherit.aes = FALSE, size = 5) I get:

plot w geom_text


Solution

  • There are a few ways of doing this.

    The simplest is just to specify the x axis labels with line breaks and spaces, though this does not allow you to customize the font size, color, etc.

    For example, if you use your own code but specify the x axis as follows:

    scale_x_continuous(breaks = compas$degrees, 
                       labels = c("N\n0°", "45°", "90° E", "135°", "180°\nS",
                                  "225°", "W 270°", "315°"))
    

    You get

    enter image description here

    A somewhat more sophisticated approach would be to use library(ggtext) and employ element_markdown() to adjust sizes, spacing, color etc. I found I had to switch to coord_radial to get this working properly.

    library(ggtext)
    
    ggplot() +
      geom_tile(data = aspect_legend_df, 
                aes(x = degrees, y = 0, fill = values), 
                width = 1, height = 0.1) +
      scale_x_continuous(breaks = compas$degrees, 
         labels = c("<span style = 'font-size:20pt'>**N**</span><br><br><br>0°", 
                    "45°", 
                    "<span>90°</span>
                    <span style = 'color:white'>AA</span>
                    <span style = 'font-size:20pt'>**E**</span>", "135°", 
                    "180°<br><br><br><span style = 'font-size:20pt'>**S**</span>",
                    "225°", 
                    "<span style = 'font-size:20pt'>**W**</span>
                    <span style = 'color:white'>AA</span>270°", "315°"),
                    name = "Aspect [°]") +
      scale_fill_viridis_c() +
      geom_vline(data = aspect_class[aspect_class$mid %in% c(0, 90, 180, 270),],
                 aes(xintercept = mid), 
                 color = "black", linewidth = 0.8) +
      geom_vline(data = aspect_class[!aspect_class$mid %in% c(0, 90, 180, 270, 360),],
                 aes(xintercept = mid), 
                 color = "black", linewidth = 0.5, linetype = "dotdash") +
      annotate(geom = "text", x = compas$degrees, y = 0.06, label = "") +    
      coord_radial(start = 0, expand = FALSE) +
      theme_minimal() +
      theme(legend.position = "none",
            axis.title = element_blank(),
            axis.text.y = element_blank(),
            panel.grid.major = element_blank(),
            panel.grid.minor = element_blank(),
            axis.text.theta = element_markdown(size = 12)) 
    

    enter image description here

    However, for ultimate control it might be best to draw the labels on using patchwork:

    library(grid)
    library(patchwork)
    
    p <- ggplot(aspect_legend_df, aes(degrees, 0, fill = values)) +
      geom_tile(width = 1, height = 0.1) +
      geom_vline(xintercept = seq(0, 270, 90), lwd = 0.8) +
      geom_vline(xintercept = seq(45, 315, 90), lwd = 0.5, linetype = "dotdash") +  
      scale_x_continuous(breaks = seq(0, 315, 45), labels = ~sprintf("%d°", .x)) +
      scale_y_continuous(limits = c(-0.05, 0.06)) +
      scale_fill_viridis_c(guide = "none") + 
      coord_polar(start = 0) + 
      theme_void() +
      theme(axis.text.x = element_text(size = 12, color = "black")) 
    
    NESW <- lapply(c("N", "E", "S", "W"), function(x) {
      wrap_elements(textGrob(x, gp = gpar(cex = 2, font = 2)))
    })
    
    plot_spacer() + NESW[[1]] + plot_spacer() + 
      NESW[[4]] + p + NESW[[2]] +
      plot_spacer() + NESW[[3]] + plot_spacer() +
      plot_layout(heights = c(1, 30, 1), widths = c(1, 30, 1))
    

    enter image description here