I would like to draw angles on the perimeter of a circle. This solution with grid package looked promising Circular plot with vectors in R for the customisation I need but I'm having trouble plotting my angles on the edge of the circle.
This is close to what I need with circular package. It has arrows that intersect the circle edge at specific angles and more angles plotted as points on the perimeter
angles <- read.table(text ='
degree Year
120 2000
30 2001
160 2002
35 2003
150 2004
90 2005
70 2006
20 2007',header=T)
angles = circular(angles$degree, units = "degrees",
template = "geographics")
plot(angles, col = "black",stack=F)
arrows.circular(limits, col = c("red","red","blue"))
However, I'm having trouble with using grid package for segments and points. I thought a solution might be to convert the angles to cartesian coordinates using a function here <https://gis.stackexchange.com/questions/465134/create-conical-gradient-around-a-point-using-r< but that doesn't give me the result I need.
a=57
b=75
c=42
limits<-data.frame(lims=c(a,b,c))
angles <- read.table(text ='
degree Year
120 2000
30 2001
160 2002
35 2003
150 2004
90 2005
70 2006
20 2007',header=T)
limits.custom <- function(limits){
pushViewport(viewport(layout.pos.col=2,layout.pos.row=2))
apply(limits,1,function(x){
pushViewport(viewport(angle=x['lims']))
grid.segments(x0=0.5,y0=0.5,x1=0.8,y1=0.5,gp=gpar(lty=5,lwd=2))
popViewport()
})
popViewport()
}
circ_coords <- function(r, t, h, k){
t <- t * pi/180
x <- r * cos(t) + h
y <- r * sin(t) + k
z <- c(x, y)
names(z) <- c('X', 'Y')
return(z)
}
angles.custom <- function(angles){
pushViewport(viewport(layout.pos.col=2,layout.pos.row=2))
apply(angles,1,function(x){
coord<-circ_coords(r=0.5,t=x['degree'],h=0.5,k=0.5)
grid.points(x=coord["X"],y=coord["Y"],gp=gpar(pch=16))
})
popViewport()
}
pushViewport(viewport(layout.pos.col=2,layout.pos.row=2))
grid.circle(x=0.5,y=0.5,r=0.5,gp = gpar(ltw=c(3),col=c('black')))
limits.custom(limits)
angles.custom(angles)
pushViewport(viewport(layout.pos.col=2,layout.pos.row=2))
grid.segments(x0=0.5,y0=0,x1=0.5,y=1,gp=gpar(col='grey'))
grid.segments(x0=0,y0=0.5,x1=1,y=0.5,gp=gpar(col='grey'))
popViewport()
Here's a quick hack using ggplot2
.
I'm choosing to change limits
in two ways:
limits$lims
to limits$degree
to match angles
;type
in order to demonstrate ggplot's categorical way of dealing with things. While it's certainly possible to include literal colors instead, it's a good thing (especially if you want legends) to allow ggplot to use the "real category names" that you have, and then if desired manually control the colors assigned to each level in the categoricalslibrary(ggplot2)
angles <- read.table(text ='
degree Year
120 2000
30 2001
160 2002
35 2003
150 2004
90 2005
70 2006
20 2007',header=T)
a=57
b=75
c=42
limits <- data.frame(degree=c(a,b,c), type=c("R","R","B"))
ggplot(angles, aes(x=degree)) +
geom_point(y = 1) +
geom_segment(
y = 0, yend = 1,
aes(xend = degree, color = type),
arrow = grid::arrow(),
data = limits) +
coord_polar(theta = "x") +
scale_x_continuous(
name = NULL,
limits = c(0, 360),
breaks = seq(0, 360, length.out = 5)[-1],
labels = c("E", "S", "W", "N")
# minor_breaks = seq(0, 360, length.out = 9)[-1] # if you want to specify semi-cardinals or more
) +
scale_color_manual(
values = c(R="red", B="blue")
) +
guides(color = "none") +
theme_minimal() +
theme(
panel.grid.minor = element_blank() # suppress minor_breaks
)
You can control much of the arrows by reading ?grid::arrow
and changing its options, I'm using the defaults.
Also, I hard-code the x-limits, required to keep it to a 360 degree circle (since this is really just a polar plot with undefined circular limits).