How to draw a half circle on a plot? - r

I am trying to draw some half circles on a plot, using the trigonometric functions in R.
So here is what I have :
matPoints <<- as.data.frame(cbind(X=c(-1, -(sqrt(3)/2), -(sqrt(2)/2), -0.5, 0, 0.5, sqrt(2)/2, sqrt(3)/2, 1), Y=c(0, 0.5, sqrt(2)/2, sqrt(3)/2, 1, sqrt(3)/2, sqrt(2)/2, 0.5, 0)))
plot(x = matPoints$X*W, y = matPoints$Y*W)
For the moment, it prints each point on the plot. What I want to do here is to trace a smooth line between points so it gives me a beautiful half circle of center (0, 0) and of scale W.
Any solution?

Do you mean this?
x <- seq(0, pi, length.out = 500)
W <- 3
plot(cos(x) * W, sin(x) * W, type = "l")

Since the general equation of a circle is x^2 + y^2 = r^2, you can mimic it like below as well,
r=3 # radius
x <- seq(-r,r,0.01)
y <- sqrt(r^2 - x^2)
plot(x,y,type="l")
# plot(c(x,x),c(y,-y),type="l") for a full circle.
gives,

Here's another possibility using complex numbers, and polygon to draw a closed shape.
plot(NA, xlim=c(-2,2), ylim=c(-2,2))
polygon(1i^(seq(0,2,l=100)))
Using this method you can easily change the centre, scale, rotation, fill colour etc:
plot(NA, xlim=c(-2,2), ylim=c(-2,2))
polygon(2*(1i^(seq(0,2,l=100)))*1i^.5 + .1-.3i, col="red")
polygon(1i^(seq(0,2,l=100)), col="blue")

Related

R ggplot plotting map raster with rounded shape - How to remove data outside projected area?

I am trying to plot a raster in a projected in a coordinated system which follows the curvature of the earth like most projections that are not WGS84. The problem is that the places were the globe wraps around the data should not be plotted outside the globe. I realize that ggplot cannot do a rounded/elliptical plot but how do I mask or remove automatically the data outside the globe? I have to plot more than 100 maps and I can't do this manually especially if I want to change to a different projection.
There's an answer here but it's hackish and doesn't seem to apply to every case, is there function or package that deals with this problem? I don't think R users only plot maps in WGS84?
I am attaching a file and code to quickly plot the map. I cannot use xlim because it would cut some parts of the map since the borders are not straight.
#netcdf file
https://ufile.io/fy08x33d
library(terra);library(tidyterra)
r=rast('Beck_KG_V1_present_0p5.tif')
#background map
r[r==0]=NA
ggplot() +geom_spatraster(data=r)+scale_fill_viridis_c(na.value='transparent') +coord_sf(crs=st_crs("+proj=hatano"),expand=FALSE)
With these data
library(terra)
library(tidyterra)
r1 <- rast('Beck_KG_V1_present_0p5.tif')
r <- subst(r1, 0, NA)
You can do
library(ggplot2)
p <- project(r, method="near", "+proj=hatano", mask=TRUE)
ggplot() +geom_spatraster(data=p)+scale_fill_viridis_c(na.value='transparent')
And here are two alternatives with base plot
First with your own color palette and a legend
library(viridis)
g <- graticule(60, 45, "+proj=hatano")
plot(g, background="azure", mar=c(.2,.2,.2,4), lab.cex=0.5, col="light gray")
plot(p, add=TRUE, axes=FALSE, plg=list(shrink=.8), col=viridis(25))
With the colors that came with the file:
coltab(p) <- coltab(r1)
plot(g, background="azure", mar=.5, lab.cex=0.5, col="light gray")
plot(p, add=TRUE, axes=FALSE, col=viridis(25))
I would go with one of Robert Hijman's options here, but if you want to create a mask in ggplot, you could do something like this:
library(grid)
y <- seq(0, 1, length = 100)
x <- ifelse(y < 0.5,
-cos(pi/2 * (2 * y - 1)) * 0.125 + 0.125,
-cos(pi/2 * (2 * y - 1)) * 0.175 + 0.175)
y <- c(0, y, 1, 0)
x <- c(0, x, 0, 0)
ggplot() +
geom_spatraster(data=r)+
scale_fill_viridis_c(na.value = 'transparent') +
coord_sf(crs = st_crs("+proj=hatano"), expand = FALSE) +
annotation_custom(polygonGrob(x = x, y = y,
gp = gpar(col = "white", lwd = 1))) +
annotation_custom(polygonGrob(x = 1-x, y = y,
gp = gpar(col = "white", lwd = 1)))

R plotting a line at the intersection of two lines

I have this simple code that plots two intersecting lines:
x <- c(1,2,3,4,5,6)
y2 <- c(6,5,4,3,2,1)
y1 <- c(1,2,3,4,5,6)
plot(x, y1)
plot(x, y1, type="o", col="blue", pch="o", lty=1)
points(x, y2, col="red", pch="*")
lines(x, y2, col="red", lty=1)
I then use the locator() function to manually find the position of the intersection of the two lines, using the coordinates of the intersection to plot a label at the intersection with the text() function, and draw a vertical line at the intersection position with the abline() function.
p <- locator()
text(p$x, p$y+0.4, labels="S")
abline(v=p$x, lty=3)
However, here I have run into a problem as I want thart the vertical line at the intersection position would stop at the intersection (instead of going along the entire y axis).
Can someone give me a hint on how to do this?
You can use segments to draw a line segment between two x, y points, so you can do:
p <- locator()
text(p$x, p$y + 0.4, labels = "S")
segments(p$x, 0, p$x, p$y, lty = 3)
Note also that if your lines are always straight like this you can find the intersection algrebaically, which is more accurate and reproducible than using locator():
dx <- tail(x, 1) - head(x, 1)
dy1 <- tail(y1, 1) - head(y1, 1)
dy2 <- tail(y2, 1) - head(y2, 1)
grad1 <- dy1/dx
grad2 <- dy2/dx
c1 <- y1[1] - grad1 * x[1]
c2 <- y2[1] - grad2 * x[1]
xi <- (c2 - c1)/(grad1 - grad2)
yi <- grad1 * xi + c1
segments(xi, 0, xi, yi, lty = 3)
text(xi, yi + 0.4, labels = "S")

Shade the area in a PDF plot [duplicate]

I'm having trouble getting polygon() to shade below the distribution all the way to the x-axis. It seems to shade above the exponential distribution to a y=-x line. Here is where what I have so far:
x <- seq(0,50,0.01)
y <- dexp(seq(0,50,0.01),rate=0.11)
plot(x, y, type="l", col=col2rgb("yellow",0.5), xaxs="i", yaxs="i", ylim=c(0,0.15))
polygon(x, y ,border=NA,col=col2rgb("yellow",0.5))
Thanks so much!
Solution is simple, by adding (0,0) to the vertices of the polygon. See below:
x <- seq(0,50,0.01)
y <- dexp(seq(0,50,0.01),rate=0.11)
plot(x, y, type="l", col=col2rgb("yellow",0.5), xaxs="i", yaxs="i", ylim=c(0,0.15))
polygon(c(0, x), c(0, y), border=NA, col=col2rgb("yellow",0.5))
How polygon() works
polygon() will line up all vertices in order. The problem of your original code is that the origin (0, 0) is not one of the vertices, so it will not be part of the polygon. You can also consider the following toy example:
x0 <- c(0, 0.5, 1.5)
y0 <- c(1.5, 0.5, 0)
## triangle, with three vertices
plot(x0, y0, pch = ".")
polygon(x0, y0, col = "red", border = NA)
## area under triangle, four vertices
polygon(c(0, x0), c(0, y0), col = "yellow", border = NA)

How to use polygon() to shade below a probability density curve

I'm having trouble getting polygon() to shade below the distribution all the way to the x-axis. It seems to shade above the exponential distribution to a y=-x line. Here is where what I have so far:
x <- seq(0,50,0.01)
y <- dexp(seq(0,50,0.01),rate=0.11)
plot(x, y, type="l", col=col2rgb("yellow",0.5), xaxs="i", yaxs="i", ylim=c(0,0.15))
polygon(x, y ,border=NA,col=col2rgb("yellow",0.5))
Thanks so much!
Solution is simple, by adding (0,0) to the vertices of the polygon. See below:
x <- seq(0,50,0.01)
y <- dexp(seq(0,50,0.01),rate=0.11)
plot(x, y, type="l", col=col2rgb("yellow",0.5), xaxs="i", yaxs="i", ylim=c(0,0.15))
polygon(c(0, x), c(0, y), border=NA, col=col2rgb("yellow",0.5))
How polygon() works
polygon() will line up all vertices in order. The problem of your original code is that the origin (0, 0) is not one of the vertices, so it will not be part of the polygon. You can also consider the following toy example:
x0 <- c(0, 0.5, 1.5)
y0 <- c(1.5, 0.5, 0)
## triangle, with three vertices
plot(x0, y0, pch = ".")
polygon(x0, y0, col = "red", border = NA)
## area under triangle, four vertices
polygon(c(0, x0), c(0, y0), col = "yellow", border = NA)

How to draw a circle in a log-log plot in R?

I have a plot with two logarithmic axes. I'd like to add a circle to a certain position of the plot. I tried to use plotrix, but this does not give options for "log-radius".
# data to plot
x = 10^(-1 * c(5:0))
y = x ^-1.5
#install.packages("plotrix", dependencies=T)
# use require() within functions
library("plotrix")
plot (x, y, log="xy", type="o")
draw.circle(x=1e-2, y=1e2, radius=1e1, col=2)
How can I add a circle to my log-log plot?
As krlmlr suggests, the easiest solution is to slightly modify plotrix::draw.circle(). The log-log coordinate system distorts coordinates of a circle given in the linear scale; to counteract that, you just need to exponentiate the calculated coordinates, as I've done in the lines marked with ## <- in the code below:
library("plotrix")
draw.circle.loglog <-
function (x, y, radius, nv = 100, border = NULL, col = NA, lty = 1,
lwd = 1)
{
xylim <- par("usr")
plotdim <- par("pin")
ymult <- (xylim[4] - xylim[3])/(xylim[2] - xylim[1]) * plotdim[1]/plotdim[2]
angle.inc <- 2 * pi/nv
angles <- seq(0, 2 * pi - angle.inc, by = angle.inc)
if (length(col) < length(radius))
col <- rep(col, length.out = length(radius))
for (circle in 1:length(radius)) {
xv <- exp(cos(angles) * log(radius[circle])) * x[circle] ## <-
yv <- exp(sin(angles) * ymult * log(radius[circle])) * y[circle] ## <-
polygon(xv, yv, border = border, col = col[circle], lty = lty,
lwd = lwd)
}
invisible(list(x = xv, y = yv))
}
# Try it out
x = 10^(-1 * c(5:0))
y = x ^-1.5
plot (x, y, log="xy", type="o")
draw.circle.loglog(x = c(1e-2, 1e-3, 1e-4), y = c(1e2, 1e6, 1e2),
radius = c(2,4,8), col = 1:3)
A work around would be to apply log10 explicitly.
plot (log10(x), log10(y), type="o")
draw.circle(x=log10(1e-2), y=log10(1e2), radius=log10(1e1), col=2)
Edit (using symbols):
plot (x, y, log="xy", type="o",xlim=c(1e-5,1), ylim=c(1,1e8))
par(new=T)
symbols(x=1e-2, y=1e2, circles=1e1, xlim=c(1e-5,1), ylim=c(1,1e8),
xaxt='n', yaxt='n', ann=F, log="xy")
The function draw.circle from the plotrix package looks like that on my system:
> draw.circle
function (x, y, radius, nv = 100, border = NULL, col = NA, lty = 1,
lwd = 1)
{
xylim <- par("usr")
plotdim <- par("pin")
ymult <- (xylim[4] - xylim[3])/(xylim[2] - xylim[1]) * plotdim[1]/plotdim[2]
angle.inc <- 2 * pi/nv
angles <- seq(0, 2 * pi - angle.inc, by = angle.inc)
if (length(col) < length(radius))
col <- rep(col, length.out = length(radius))
for (circle in 1:length(radius)) {
xv <- cos(angles) * radius[circle] + x
yv <- sin(angles) * radius[circle] * ymult + y
polygon(xv, yv, border = border, col = col[circle], lty = lty,
lwd = lwd)
}
invisible(list(x = xv, y = yv))
}
<environment: namespace:plotrix>
What happens here is essentially that the circle is approximated by a polygon of 100 vertices (parameter nv). You can do either of the following:
Create your own version of draw.circle that does the necessary coordinate transformation to "undo" the log transform of the axes.
The function invisibly returns the list of coordinates that are used for plotting.
(If you pass a vector as radius, then only the coordinates of the last circle are returned.) You might be able to apply a transform to those coordinates and call polygon on the result. Pass appropriate values for border, col, lty and/or lwd to hide the polygon drawn by the functions itself.
The first version sounds easier to me. Simply replace the + x by a * x, same for y, inside the for loop, and you're done. Equivalently, for the second version, you subtract x and then multiply by x each coordinate, same for y. EDIT: These transformations are slightly wrong, see Josh's answer for the correct ones.

Resources