Search code examples
rggplot2polar-coordinates

How to wrap around the polar coordinate limits in ggplot2?


I have a circular space where angles 0 and 360 are equivalent. I want to plot rectangles in this space such that rectangles can cross this value. However, I am having trouble with ggplot2.

base <- ggplot() +
  scale_x_continuous(breaks = seq(45, 360, 45), limits = c(0, 360)) +
  scale_y_continuous(breaks = seq(0, 1, 0.2), limits = c(0, 1)) +
  coord_polar(theta = "x", start = 1.5 * pi, direction = -1)

enter image description here

1. Attempt to plot with values exceeding xlim:

base + geom_rect(aes(xmin = 340, xmax = 380, ymin = 0.4, ymax = 0.6), 
  color = "darkblue", fill = "steelblue")
#> Warning message:
#> Removed 1 rows containing missing values (geom_rect). 

All values outside of xlim are removed so this does not work.

2. Attempt to plot with rescaled values

base + geom_rect(aes(xmin = 340, xmax = 380 %% 360, ymin = 0.4, ymax = 0.6), 
  color = "darkblue", fill = "steelblue")

enter image description here This at least produces a plot, but plots the opposite of what I want. Instead of going from 340 to 380 CCW, this plots 340 to 20 CW.

3. Attempt to plot as two conjoining elements

  base + geom_rect(aes(xmin = c(350, 0), xmax = c(360, 10), ymin = 0.4, ymax = 0.6), 
    color = "darkblue", fill = "steelblue")

enter image description here

This shows the rectangle where I want it, but this is not satisfying as a solution because of the stroke lines at angle 0/360 and because I now have to represent each rectangle as two rectangles.

4. Attempt 1 to use zooming rather than clipping

ggplot() +
  scale_x_continuous(breaks = seq(45, 360, 45)) +
  scale_y_continuous(breaks = seq(0, 1, 0.2), limits = c(0, 1)) +
  coord_cartesian(xlim = c(0, 360)) +
  coord_polar(theta = "x", start = 1.5 * pi, direction = -1) +
  geom_rect(aes(xmin = 340, xmax = 380, ymin = 0.4, ymax = 0.6), 
    color = "darkblue", fill = "steelblue")

enter image description here This seems to lose the zooming and the limits.

5. Attempt 2 to use zooming rather than clipping

ggplot() +
  scale_x_continuous(breaks = seq(45, 360, 45)) +
  scale_y_continuous(breaks = seq(0, 1, 0.2), limits = c(0, 1)) +
  coord_polar(theta = "x", start = 1.5 * pi, direction = -1) +
  coord_cartesian(xlim = c(0, 360)) +
  geom_rect(aes(xmin = 340, xmax = 380, ymin = 0.4, ymax = 0.6), 
    color = "darkblue", fill = "steelblue")

enter image description here This accomplishes the zooming correctly, but overwrites the polar coordinate system.

If anyone can provide a solution or ideas for this problem, I would really appreciate it. Again, I am looking for something that looks like #3 but without the inner stroke and without needing to use two rectangles.

Edit: This question is related and also unanswered.


Solution

  • Is it critical that the underlying coordinate system is polar? geom_arc_bar() from the ggforce package behaves as you would expect, so you can use it to draw arcs in arbitrary angles. But you have a cartesian coordinate system underneath, so you may have to draw the coordinate lines yourself if you need them.

    library(ggforce)
    library(dplyr)
    
    data_deg <- data.frame(xmin = 340,
                       xmax = 380,
                       ymin = 0.4,
                       ymax = 0.6)
    
    offset = 90 # by how much are angles offset
    dir = 1 # should we go counterclockwise (1) or clockwise (-1)
    
    # convert angles from degrees into radians, apply offset and direction
    data_rad <- mutate(data_deg,
                   xmin = dir*2*pi*(xmin + offset)/360,
                   xmax = dir*2*pi*(xmax + offset)/360)
    
    ggplot(data_rad) + geom_arc_bar(aes(x0 = 0, y0 = 0, r0 = ymin, r = ymax,
                                    start = xmin, end = xmax),
                                color = "darkblue", fill = "steelblue") +
      scale_x_continuous(limits = c(-1, 1)) +
      scale_y_continuous(limits = c(-1, 1)) +
      coord_fixed()
    

    enter image description here

    This doesn't solve the other issue you linked to, but in general you will probably find that converting coordinates from polar to Euclidean yourself gives you much more flexibility to get the plot looking the way you want it to.