Plot in R out of proportion. Why? - r

I want to plot a world map in R, but for some reason there are unnecessary margins at the top and bottom of the image. I reduced the code to its minimal.
png('mundi.png',1000,500)
par(mar=c(0,0,0,0),bg='green')
plot(NA,xlim=c(-180,180),ylim=c(-90,90),xaxs='i',bty='n',xaxt='n',yaxt='n',asp=1)
par('usr')
rect(-180,-90,180,90,col='white')
dev.off()
The generated image is
The green areas are the unnecessary margins. The dimension of both the image and the coordinates are 2x1 (1000x500 and 360x180, respectively). xaxs='i' should make exact coordinates. asp=1 should not be needed, and is not making any difference. par('usr') is returning
-180.0 180.0 -97.2 97.2
Why isn't it returning
-180.0 180.0 -90.0 90.0
As it (supposedly) should?

You were almost there, actually. To control the limits of the plot window through xlim/ylim without the extra space R tends to add, you have to specify the x/yaxt = 'i' option. You set it for the x axis, but not the y axis.
When I do set it for the y axis, the values outputted are as expected, and the plot is completely white (forgive me for not putting up an image of it ;-) )
png('mundi.png', width = 1000, height = 500)
par(mar=c(0,0,0,0), bg='green')
plot(NA, xlim = c(-180,180), ylim = c(-90,90),
xaxs='i', yaxs='i', # set both axes to 'i' option
bty='n',xaxt='n',yaxt='n',asp=1)
par('usr')
# [1] -180 180 -90 90
rect(-180,-90,180,90,col='white')
dev.off()

Related

Control persp mesh tile border colors

I'm having some trouble creating a perspective plot that looks exactly how I want it to look. In particular, I am trying to get the mesh not to be visible at all. If you look at the image on the left you can see faint lines running between the tiles. I want it looking like the right image with no lines visible:
I specifically want a solution with graphics::persp or other base R function. I am not interested in 3rd party packages like rgl.
I obtained the right by using polygon and specifying a border color to match the col color. If I leave border=NA with polygon I get the same result as with persp. However, it seems persp just takes the first border value and re-uses it, unlike polygon which matches colors to the polygons.
This is the code used to generate the image:
nr <- nc <- 10
mx <- matrix(numeric(nr * nc), nr)
par(mai=numeric(4))
col <- gray((row(mx[-1,-1]) * col(mx[-1,-1])/((nr-1)*(nc-1))))
par(mfrow=c(1,3), mai=c(0, 0, .25, 0), pty='s')
persp(
mx, phi=90, theta=0, border=NA, col=col, r=1e9, zlim=c(0,1),
axes=FALSE, box=FALSE
)
title('Persp border=NA')
persp(
mx, phi=90, theta=0, border=col, col=col, r=1e9, zlim=c(0,1),
axes=FALSE, box=FALSE
)
title('Persp border=col')
plot.new()
mxpoly.x <- rbind(
c(row(mx)[-nr, -nc]), c(row(mx)[-1, -nc]), c(row(mx)[-1, -1]),
c(row(mx)[-nr, -1]), NA
)
mxpoly.y <- rbind(
c(col(mx)[-nr, -nc]), c(col(mx)[-1, -nc]), c(col(mx)[-1, -1]),
c(col(mx)[-nr, -1]), NA
)
title('Polygon')
polygon(
((mxpoly.x - 1) / (max(mxpoly.x,na.rm=TRUE) - 1)),
((mxpoly.y - 1) / (max(mxpoly.y,na.rm=TRUE) - 1)),
col=col, border=col
)
That looks like a result of antialiasing. When each cell is drawn, the background is white, so antialiasing means the border pixels are drawn in a lighter colour.
On a Mac, you can fix this by turning antialiasing off. Your first example gives
by default, but if I open the graphics device using
quartz(antialias = FALSE)
and then run the identical code, I get
Turning off antialiasing can cause jagged edges, so this might not really be an acceptable solution to your real problem if it has diagonal lines.
You might be able to get things to work by drawing the surface twice with antialiasing: the first time will show borders, the second time might still show something, but should show less. However, persp() has no add = TRUE argument, so drawing things the second time is likely to be tricky.
If you're not on a Mac, you'll need to read about the device you're using to find if it allows control of antialiasing.
Edited to add: I tried modifying the C source to the persp function
to draw the surface 2 or 3 times. The boundaries were still slightly
visible when it was drawn twice, but invisible with 3 draws.

Improve the speed of a dilating circle animation in R

I am trying to create an enlarging circle to test the Apparent Looming Threshold of fish. I want to be able to control the radius of the circle and the rate at which the radius increases.
I have the following code:
library(plotrix)
px=1:50
py=1:50
plot(px,py,type="n",xlab="",ylab="",axes = FALSE)#create blank plot
x=25#set x location of circle
y=25#set y location of circle
radius=seq(1,20,by=.5)#set sequence of radii to plot
#plot circle on top of each other to give appearance of growing circle
#at a rate of Sys.sleep(x)
for (i in radius){
draw.circle(x,y,radius = i,col="black")
Sys.sleep(1)#plot one circle per second
}
The code allows me to change the radius of the circle and the rate, however, if the Sys.sleep(x) is set below 1, the for loop takes too long to process and skips circles in the sequence. Is there alternative to a for loop that would speed up the plotting so that I could get the entire animation to run faster than 1 frame per second?
Thank you
I suspect rather plotrix than the for loop which slows down. You could just plot a normal point and let him grow literately with cex; wrap all into a function.
growCycle <- function(x) {
plot(25, 25, xlab="", ylab="", axes=FALSE, asp=1, col=1, pch=20, cex=x)
Sys.sleep(.2) # at least 5 fps
}
Note: Consider a glance into ?plot and ?par for more graphical parameters.
Then use sapply():
radius = seq(1, 20, by=.5)
sapply(radius, growCycle)
Results in an increased speed of 400 %!
Hope this helps.

How to add orientation (direction/angle/azimuth) to all points in scatterplot?

I am trying to create a scatter plot in R where each point has either an arrow or just a line indicating an "orientation" or a direction associated with that specific point. Each point has a different orientation but all of the orientation lines should be the same length. The angles range from 0-360 degrees with 0 being rightwards and then moving counter clockwise from there. The biggest problem is that my axes are in log scale and the x and y axes increase at different rates which throws off the orientation. My data is a 3x35 matrix with X,Y coordinates and my orientation angle in the third column.
I have tried using the arrows function; however, the arrows don't seem to be pointing in the correct direction and their lengths vary based on the angle. Here is the simple code I used:
plot(data$SF.X., data$TF.Y.,log="xy", pch=19, cex=0.8,ylim=c(0.031,20), xlim = c(0.031,1))
arrows(data$SF.X., data$TF.Y., x1=data$SF.X.+length*cos(data$Degrees), y1=data$TF.Y.+length*sin(data$Degrees),
code = 2, length=0.03, col="Red")
This the result of the above code:
Can someone point me in the right direction as to how to get my orientation lines all the same length and pointing in the correct direction based on the angle value in my third column?
Thank you so much!
Here is a sample of my data set:
x = c(0.47,0.80,0.09,0.78,0.14)
y = c(2.71,4.51,1.85,5.56,0.98)
orientation (degrees) = c(42.51,9.27,11.31,0.52,93.4)
For the angles, it's easy, R always works with angles in radians so you just have to multiply your degrees vector by pi/180.
For the length of the arrows, it is much more tricky. There is a deformation due to axis scale and image format so I came up with a workaround by normalizing the axis, then changing the labels and saving the figure in a perfect square format. Probably not the best solution but it gives the expected output.
x = c(0.47,0.80,0.09,0.78,0.14)
y = c(2.71,4.51,1.85,5.56,0.98)
degrees = c(42.51,9.27,11.31,0.52,93.4)
x.norm = (x-min(x))/(max(x)-min(x))
y.norm = (y-min(y))/(max(y)-min(y))
l <- 0.15
png("/home/ubuntu/Downloads/sample.png", width=500, height=500)
par(mar=c(4,4,1,1))
plot(x.norm, y.norm, pch=19, cex=0.8, col="Blue",
axes=FALSE, xlab="x", ylab="y", xlim=c(-0.1,1.1), ylim=c(-0.1,1.1))
arrows(x.norm, y.norm,
x1=x.norm+l*cos(degrees*pi/180), y1=y.norm+l*sin(degrees*pi/180),
length=0.05, col="Gray")
axis(1, at=seq(0,1,0.1), round(seq(min(x),max(x), (max(x)-min(x))/10),1))
axis(2, at=seq(0,1,0.1), round(seq(min(y),max(y), (max(y)-min(y))/10),1))
dev.off()

How to rotate 180 degrees an mtext() in R

Apparently, mtext() in R doesn't support the srt parameter whose job is to rotate a piece of text.
I need mtext() to create an axis title on side 4 of my moving plot (i.e., values to be plotted come from a function so they change and so do the plot axes values). I was wondering then, what options do I have to rotate 180 degrees this side 4 axis title?
An example is BELOW:
curve(dnorm(x),-3,3)
mtext("Strength",side=4,srt=180)
You can use par("usr") to obtain extremes of the plot area and use it to place your text without having to explicitly specify the x and y.
Try
curve(dnorm(x),-3,3)
corners = par("usr") #Gets the four corners of plot area (x1, x2, y1, y2)
par(xpd = TRUE) #Draw outside plot area
text(x = corners[2]+.5, y = mean(corners[3:4]), "Strength", srt = 270)
This way it will always be on the right extreme and vertically in the middle.

Linebreak in R plot legend behavior

I have a linebreak in a legend in R and my problem is that the graphic looks not as expected.
My minimal example is as follows:
plot(1)
legendLabel<-c("t\nu ","tu","wh","trr\nni")
legend("top",legend=legendLabel,horiz=TRUE,fill=c("red","blue","gray","black"))
I would expect that the upper and lower margin of the legend is equal, but this is not the case.
As you can see in the attached image, the lower margin is smaller then the upper.
Does anybody have an idea how to fix it or can anyone tell me what the problem is?
Thanks.
OK, I believe I have a solution for you. I saved the information of the legend position in an object called ld and then create a polygon based on these coordinates. It's a bit tricky to understand, but I am basically expanding the polygon by a few pointsize lengths. In order to do this, I had to first get the character size in inches with par()$cin and covert the pointsize to these dimensions (divide by 72 and multiply by par()$ps. Then, convert this to the units of the plot by scaling with par()$usr to get character width in units (I think this is correct - in any case it works!). I added 3 of these units to the left of the ldcoordinates, 2 to the right, 1 up and 1 down. Here's the result and code:
plot(1)
legendLabel<-c("t\nu ","tu","wh","trr\nni")
ld <- legend("top",legend=legendLabel,horiz=TRUE,fill=c("red","blue","gray","black"), bty="n")
CIN <- par()$cin
PS <- par()$ps
USR <- par()$usr
CIN.USR <- c((CIN[1]/72*PS)/(USR[2]-USR[1]), (CIN[2]/72*PS)/(USR[4]-USR[3]))
xs <- c(ld$text$x[1], ld$text$x[1], ld$text$x[length(ld$text$x)], ld$text$x[length(ld$text$x)])
ys <- c(ld$text$y[1], ld$text$y[1], ld$text$y[length(ld$text$x)], ld$text$y[length(ld$text$x)])
polygon(
x = xs + c(-3*CIN.USR[1], -3*CIN.USR[1], 2*CIN.USR[1], 2*CIN.USR[1]),
y = ys+c(-1*CIN.USR[2], 1*CIN.USR[2], 1*CIN.USR[2], -1*CIN.USR[2])
)
Thanks to #'Marc in the box' I found a good working solution.
Disable the box as he told with bty="n" and then
ld<-legend("top",legend=legendLabel, cex=0.65, fill=colorNames, horiz=TRUE,bty="n")
height<-(ld$rect$top-ld$text$y[1])*2
xs <- c(ld$rect$left, ld$rect$left, ld$rect$left+ld$rect$w, ld$rect$left+ld$rect$w)
ys <- c(ld$rect$top, ld$rect$top-height, ld$rect$top-height, ld$rect$top)
polygon(x = xs , y = ys)
So I first calculated the distance between the top corner and the dataPoint and afterwords draw with this information a polygon.
Works quite general has far as I have seen.
Thanks.

Resources