Related
Is it possible to draw real solid circle with a radius in "user" coordinates?
I tried the following:
Polygons:
I don't want to use them because I need real circles in the resulting svg.
Segments
segments(x, y, x, y, lwd=px, lend=0)
With segments there is the problem that I don't find a way to specify the segment in "user" coordinates.
The resulting graph is at the end exported to PDF.
Update
I draw a graph with a lot of elements and the elements has a distinct width. The width of the elements depends on the width at the x-axis. If I don't use user coordinates the result in the PDF is not correct in dependence to the x-axis.
A Polygon is an approximation to a circle and if I use them the result e.g. PDF is very large and the performance is not good and memory usage is very high. I draw 10,000 circles and more on one graph.
I use the following code with the described performance problems:
circle <- function(x, y, r, col) {
edgeCount <- 50
intervals <- (1:edgeCount) / edgeCount * 2 * pi
for(i in 1:length(x)) {
polygon(r[i]*sin(intervals) + x[i], r[i]*cos(intervals) + y[i], col=col[i],border=NA)
}
}
If you're comfortable with using a wrapper for sp's SpatialLine object you can try the oceanmap package which has a quite useful function called SpatialCircle(). It essentially builds a circle via seq() and adjusts it for your center point coordinates x and y, and for your radius r. It's still a set of line segments (so not one curved line), but quite simple to use.
Result:
Code:
Pretty straightforward:
# Load libraries.
library(oceanmap)
# Generate plot window and data.
set.seed(1702)
plot.new()
plot.window(xlim = c(0, 20), ylim = c(0, 10),
asp = 1, xaxs = "i", yaxs = "i")
axis(1)
axis(2)
box()
n <- 1000
x <- runif(n, 0, 20)
y <- runif(n, 0, 10)
for (i in 1:n) {
circle <- SpatialCircle(x = x[i], y = y[i], r = 0.1, n = 1000)
lines(circle)
}
This also works with ggplot2 with some data wrangling.
Addendum: Precision of SpatialCircles
If you want to check out what n (precision) in the SpatialCircle() function really means, try the following:
nrow(circle#lines[[1]]#Lines[[1]]#coords)
Result:
[1] 1000
This means that the object has 1,000 coordinate pairs (x and y) through which a line can be drawn. Furthermore, this line will have 999 distinct line segments, as the first and the last coordinate pairs are always identical. Proof:
all.equal(circle#lines[[1]]#Lines[[1]]#coords[1, ],
circle#lines[[1]]#Lines[[1]]#coords[1000, ])
Result:
[1] TRUE
If found a solution myself with the help of Gregor2 which did lead me to the library "grid".
library(grid)
#draw frame using normal plot
plot(0, 0, cex=0)
margins <- par("mar")
#1: bottom 2:left 3:top 4:right
mb <- unit(margins[1], "lines")
ml <- unit(margins[2], "lines")
mt <- unit(margins[3], "lines")
mr <- unit(margins[4], "lines")
#create viewport equivalent to margins in par
pushViewport(viewport(x = ml, y = mb, width = unit(1, "npc") - ml - mr, height = unit(1, "npc") - mb - mt, just=c("left", "bottom"), clip=TRUE))
#draw circle in npc units (easily convertable to user units using grconvertX)
grid.draw(circleGrob(x=0.5, y=0.5, r=0.5, default.units="npc", gp=gpar(col="blue", fill="blue")))
popViewport()
Rotate a plot (with below rotation setting) seems to be impossible in R according to Google.
So I am looking for a way to do that, but still no success till now.
Rotation setting :
The rotation center is the origin of plot ({0,0})
The given angle will be the angle between the y axis and the plot.
Here is a code where
a is something depending on angle and x axis length. (I think)
b is something depending on angle and y axis length. (I think)
:
speed <- cars$speed
dist <- cars$dist
plot(speed,dist, xlim=c(0,121), ylim=c(0,121))
xTemp <- speed
speed <- speed + (a)
dist <- dist + (b)
par(new=TRUE)
plot(speed,dist, xlim=c(0,121), ylim=c(0,121), col="red")
Any idea of a and b values ?
Here is for example a code in wich the plot is rotated by about 50 degrees from the y axis (Notice that is is not a true rotation. speed value is extended after rotation.. I don't now how to fix it.) :
speed <- cars$speed
dist <- cars$dist
plot(speed,dist, xlim=c(0,121), ylim=c(0,121))
xTemp <- speed
speed <- speed + dist
dist <- dist - xTemp
par(new=TRUE)
plot(speed,dist, xlim=c(0,121), ylim=c(0,121), col="red")
Just apply a rotation matrix
to your data.
angle <- pi/3
M <- matrix( c(cos(angle), -sin(angle), sin(angle), cos(angle)), 2, 2 )
plot( as.matrix(cars[,c("speed","dist")]) %*% M )
What happens may be clearer on a different example:
library(mlbench)
d <- mlbench.smiley()$x
op <- par(mfrow=c(1,2))
plot(d, asp=1)
plot(as.matrix(d) %*% M, col="red", asp=1)
par(op)
It looks I found the solution :
speed <- cars$speed
dist <- cars$dist
angle <- 45
plot(speed,dist, xlim=c(-121,121), ylim=c(-121,121))
speed1 <- double(); dist1=double()
for(i in 1:length(speed)){
sq <- sqrt((speed[i]*speed[i]) + (dist[i]*dist[i]))
angleInit <- (180*atan(dist[i]/speed[i]))/pi
angle2 <- angleInit - angle
speed1[i] <- cos(pi*angle2/180) * sq
dist1[i] <- sin(pi*angle2/180) * sq
}
par(new=TRUE)
plot(speed1,dist1, xlim=c(-121,121), ylim=c(-121,121), col="red")
Thank you
I have a pair of points and I would like to find a circles of known r that are determined by these two points. I will be using this in a simulation and possible space for x and y have boundaries (say a box of -200, 200).
It is known that square of radius is
(x-x1)^2 + (y-y1)^2 = r^2
(x-x2)^2 + (y-y2)^2 = r^2
I would now like to solve this non-linear system of equations to get two potential circle centers. I tried using package BB. Here is my feeble attempt which gives only one point. What I would like to get is both possible points. Any pointers into right direction will be met with complimentary beer on first possible occasion.
library(BB)
known.pair <- structure(c(-46.9531139599816, -62.1874917150412, 25.9011462171242,
16.7441676243879), .Dim = c(2L, 2L), .Dimnames = list(NULL, c("x",
"y")))
getPoints <- function(ps, r, tr) {
# get parameters
x <- ps[1]
y <- ps[2]
# known coordinates of two points
x1 <- tr[1, 1]
y1 <- tr[1, 2]
x2 <- tr[2, 1]
y2 <- tr[2, 2]
out <- rep(NA, 2)
out[1] <- (x-x1)^2 + (y-y1)^2 - r^2
out[2] <- (x-x2)^2 + (y-y2)^2 - r^2
out
}
slvd <- BBsolve(par = c(0, 0),
fn = getPoints,
method = "L-BFGS-B",
tr = known.pair,
r = 40
)
Graphically you can see this with the following code, but you will need some extra packages.
library(sp)
library(rgeos)
plot(0,0, xlim = c(-200, 200), ylim = c(-200, 200), type = "n", asp = 1)
points(known.pair)
found.pt <- SpatialPoints(matrix(slvd$par, nrow = 1))
plot(gBuffer(found.pt, width = 40), add = T)
ADDENDUM
Thank you all for your valuable comments and code. I provide timings for answers by posters who complimented their answers with code.
test replications elapsed relative user.self sys.self user.child sys.child
4 alex 100 0.00 NA 0.00 0 NA NA
2 dason 100 0.01 NA 0.02 0 NA NA
3 josh 100 0.01 NA 0.02 0 NA NA
1 roland 100 0.15 NA 0.14 0 NA NA
The following code will get you the points at the centers of the two desired circles. No time right now to comment this up or convert the results to Spatial* objects, but this should give you a good start.
First, here's an ASCII-art diagram to introduce point names. k and K are the known points, B is a point on the horizontal drawn through k, and C1 and C2 are the centers of the circles you are after:
C2
K
k----------------------B
C1
Now the code:
# Example inputs
r <- 40
known.pair <- structure(c(-46.9531139599816, -62.1874917150412,
25.9011462171242, 16.7441676243879), .Dim = c(2L, 2L),
.Dimnames = list(NULL, c("x", "y")))
## Distance and angle (/_KkB) between the two known points
d1 <- sqrt(sum(diff(known.pair)^2))
theta1 <- atan(do.call("/", as.list(rev(diff(known.pair)))))
## Calculate magnitude of /_KkC1 and /_KkC2
theta2 <- acos((d1/2)/r)
## Find center of one circle (using /_BkC1)
dx1 <- cos(theta1 + theta2)*r
dy1 <- sin(theta1 + theta2)*r
p1 <- known.pair[2,] + c(dx1, dy1)
## Find center of other circle (using /_BkC2)
dx2 <- cos(theta1 - theta2)*r
dy2 <- sin(theta1 - theta2)*r
p2 <- known.pair[2,] + c(dx2, dy2)
## Showing that it worked
library(sp)
library(rgeos)
plot(0,0, xlim = c(-200, 200), ylim = c(-200, 200), type = "n", asp = 1)
points(known.pair)
found.pt <- SpatialPoints(matrix(slvd$par, nrow = 1))
points(p1[1], p1[2], col="blue", pch=16)
points(p2[1], p2[2], col="green", pch=16)
This is the basic geometric way of going about solving it that everybody else is mentioning. I use polyroot to get the roots of the resulting quadratic equation but you could easily just use the quadratic equation directly.
# x is a vector containing the two x coordinates
# y is a vector containing the two y coordinates
# R is a scalar for the desired radius
findCenter <- function(x, y, R){
dy <- diff(y)
dx <- diff(x)
# The radius needs to be at least as large as half the distance
# between the two points of interest
minrad <- (1/2)*sqrt(dx^2 + dy^2)
if(R < minrad){
stop("Specified radius can't be achieved with this data")
}
# I used a parametric equation to create the line going through
# the mean of the two points that is perpendicular to the line
# connecting the two points
#
# f(k) = ((x1+x2)/2, (y1+y2)/2) + k*(y2-y1, x1-x2)
# That is the vector equation for our line. Then we can
# for any given value of k calculate the radius of the circle
# since we have the center and a value for a point on the
# edge of the circle. Squaring the radius, subtracting R^2,
# and equating to 0 gives us the value of t to get a circle
# with the desired radius. The following are the coefficients
# we get from doing that
A <- (dy^2 + dx^2)
B <- 0
C <- (1/4)*(dx^2 + dy^2) - R^2
# We could just solve the quadratic equation but eh... polyroot is good enough
k <- as.numeric(polyroot(c(C, B, A)))
# Now we just plug our solution in to get the centers
# of the circles that meet our specifications
mn <- c(mean(x), mean(y))
ans <- rbind(mn + k[1]*c(dy, -dx),
mn + k[2]*c(dy, -dx))
colnames(ans) = c("x", "y")
ans
}
findCenter(c(-2, 0), c(1, 1), 3)
Following #PhilH's solution, just using trigonometry in R:
radius=40
Draw the original points on the radius
plot(known.pair,xlim=100*c(-1,1),ylim=100*c(-1,1),asp=1,pch=c("a","b"),cex=0.8)
Find the midpoint c of ab (which is also the midpoint of de the two circle centers)
AB.bisect=known.pair[2,,drop=F]/2+known.pair[1,,drop=F]/2
C=AB.bisect
points(AB.bisect,pch="c",cex=0.5)
Find the length and angle of the chord ab
AB.vector=known.pair[2,,drop=F]-known.pair[1,,drop=F]
AB.len=sqrt(sum(AB.vector^2))
AB.angle=atan2(AB.vector[2],AB.vector[1])
names(AB.angle)<-NULL
Calculate the length and angle of the line from c to the centers of the two circles
CD.len=sqrt(diff(c(AB.len/2,radius)^2))
CD.angle=AB.angle-pi/2
Calculate and plot the position of the two centers d and e from the perpendicular to ab and the length:
center1=C+CD.len*c(x=cos(CD.angle),y=sin(CD.angle))
center2=C-CD.len*c(x=cos(CD.angle),y=sin(CD.angle))
points(center1[1],center1[2],col="blue",cex=0.8,pch="d")
points(center2[1],center2[2],col="blue",cex=0.8,pch="e")
Shows:
No numerical equation solving required. Just formulae:
You know that since both points A and B lie on the circle, the distance from each to a given centre is the radius r.
Form an isosceles triangle with the chord of the two known points at the base and the third point at the circle centre.
Bisect the triangle midway between A and B, giving you a right-angle triangle.
http://mathworld.wolfram.com/IsoscelesTriangle.html gives you the height in terms of the base length and the radius.
Follow the normal to the AB chord (See this SO Answer) for a distance of the height just calculated in each direction from the point.
Here are the bones of an answer, if I have time later I'll flesh them out. This should be easy enough to follow if you draw along with the words, sorry I don't have the right software on this computer to draw the picture for you.
Leave aside degenerate cases where the points are identical (infinite solutions) or too far apart to lie on the same circle of your chosen radius (no solutions).
Label the points X and Y and the unknown centre points of the 2 circles c1 and c2. c1 and c2 lie on the perpendicular bisector of XY; call this line c1c2, at this stage it's immaterial that we don't know all the details of the locations of c1 and c2.
So, figure out the equation of line c1c2. It passes through the half-way point of XY (call this point Z) and has slope equal to the negative reciprocal of XY. Now you have the equation of c1c2 (or you would if there was any flesh on these bones).
Now construct the triangle from one point to the intersection of the line and its perpendicular bisector and the centre point of a circle (say XZc1). You still don't know exactly where c1 is but that never stopped anyone sketching the geometry. You have a right triangle with two side lengths known (XZ and Xc1), so it's easy to find Zc1. Repeat the process for the other triangle and circle centre.
Of course, this approach is quite different from OP's initial approach and may not appeal.
Some warnings to get rid of, but this should get you started. There might be a performance issue, so solving it completely with basic geometry could be a better approach.
known.pair <- structure(c(-46.9531139599816, -62.1874917150412, 25.9011462171242,
16.7441676243879), .Dim = c(2L, 2L), .Dimnames = list(NULL, c("x",
"y")))
findCenter <- function(p,r) {
yplus <- function(y) {
((p[1,1]+sqrt(r^2-(y-p[1,2])^2)-p[2,1])^2+(y-p[2,2])^2-r^2)^2
}
yp <- optimize(yplus,interval=c(min(p[,2]-r),max(p[,2]+r)))$minimum
xp <- p[1,1]+sqrt(r^2-(yp-p[1,2])^2)
cp <- c(xp,yp)
names(cp)<-c("x","y")
yminus <- function(y) {
((p[1,1]-sqrt(r^2-(y-p[1,2])^2)-p[2,1])^2+(y-p[2,2])^2-r^2)^2
}
ym <- optimize(yminus,interval=c(min(p[,2]-r),max(p[,2]+r)))$minimum
xm <- p[1,1]-sqrt(r^2-(ym-p[1,2])^2)
cm <- c(xm,ym)
names(cm)<-c("x","y")
list(c1=cp,c2=cm)
}
cent <- findCenter(known.pair,40)
I hope you know some basic geometry, because I cannot draw it unfortunately.
The perpendicular bisector is the line where every middle point of a circle which crosses both A and B lays.
Now you have the middle of AB and r, so you can draw a right triangle with the point A, the middle of AB and the unknown middle point of the circle.
Now use the pythagoras' theorem to get the distance from the middle point of AB to the middle point of the circle, and calculate the position of the circle shouldn't be hard from here, using basic sin/cos combinations.
Is there a way to draw a simple ellipse based on the following definition (instead of eigenvalue) in R?
The definition I want to use is that an ellipse is the set of points in a plane for which the sum of the distances to two fixed points F1 and F2 is a constant.
Should I just use a polar cordinate?
This may be more algorithmic question.
As #DWin suggested, there are several implementations for plotting ellipses (such as function draw.ellipse in package plotrix). To find them:
RSiteSearch("ellipse", restrict="functions")
That being said, implementing your own function is fairly simple if you know a little geometry. Here is an attempt:
ellipse <- function(xf1, yf1, xf2, yf2, k, new=TRUE,...){
# xf1 and yf1 are the coordinates of your focus F1
# xf2 and yf2 are the coordinates of your focus F2
# k is your constant (sum of distances to F1 and F2 of any points on the ellipse)
# new is a logical saying if the function needs to create a new plot or add an ellipse to an existing plot.
# ... is any arguments you can pass to functions plot or lines (col, lwd, lty, etc.)
t <- seq(0, 2*pi, by=pi/100) # Change the by parameters to change resolution
k/2 -> a # Major axis
xc <- (xf1+xf2)/2
yc <- (yf1+yf2)/2 # Coordinates of the center
dc <- sqrt((xf1-xf2)^2 + (yf1-yf2)^2)/2 # Distance of the foci to the center
b <- sqrt(a^2 - dc^2) # Minor axis
phi <- atan(abs(yf1-yf2)/abs(xf1-xf2)) # Angle between the major axis and the x-axis
xt <- xc + a*cos(t)*cos(phi) - b*sin(t)*sin(phi)
yt <- yc + a*cos(t)*sin(phi) + b*sin(t)*cos(phi)
if(new){ plot(xt,yt,type="l",...) }
if(!new){ lines(xt,yt,...) }
}
An example:
F1 <- c(2,3)
F2 <- c(1,2)
plot(rbind(F1, F2), xlim=c(-1,5), ylim=c(-1, 5), pch=19)
abline(h=0, v=0, col="grey90")
ellipse(F1[1], F1[2], F2[1], F2[2], k=2, new=FALSE, col="red", lwd=2)
points((F1[1]+F2[1])/2, (F1[2]+F2[2])/2, pch=3)
I have two matrices (of approximately 300 x 100) and I would like to plot a graph to see the parts of the first one that are higher than those of the second.
I can do, for instance:
# Calculate the matrices and put them into m1 and m2
# Note that the values are between -1 and 1
par(mfrow=c(1,3))
image(m1, zlim=c(-1,1))
image(m2, zlim=c(-1,1))
image(m1-m2, zlim=c(0,1))
This will plot only the desired regions in the 3rd plot but I would like to do something a bit different, like putting a line around those areas over the first plot in order to highlight them directly there.
Any idea how I can do that?
Thank you
nico
How about:
par(mfrow = c(1, 3))
image(m1, zlim = c(-1, 1))
contour(m1 - m2, add = TRUE)
image(m2, zlim = c(-1, 1))
contour(m1 - m2, add = TRUE)
image(m1 - m2, zlim = c(0, 1))
contour(m1 - m2, add = TRUE)
This adds a contour map around the regions. Sort of puts rings around the areas of the 3rd plot (might want to fiddle with the (n)levels of the contours to get fewer 'circles').
Another way of doing your third image might be:
image(m1>m2)
this produces a matrix of TRUE/FALSE values which gets imaged as 0/1, so you have a two-colour image. Still not sure about your 'putting a line around' thing though...
Here's some code I wrote to do something similar. I wanted to highlight contiguous regions above a 0.95 threshold by drawing a box round them, so I got all the grid squares above 0.95 and did a clustering on them. Then do a bit of fiddling with the clustering output to get the rectangle coordinates of the regions:
computeHotspots = function(xyz, thresh, minsize=1, margin=1){
### given a list(x,y,z), return a data frame where each row
### is a (xmin,xmax,ymin,ymax) of bounding box of a contiguous area
### over the given threshhold.
### or approximately. lets use the clustering tools in R...
overs <- which(xyz$z>thresh,arr.ind=T)
if(length(overs)==0){
## found no hotspots
return(NULL)
}
if(length(overs)==2){
## found one hotspot
xRange <- cbind(xyz$x[overs[,1]],xyz$x[overs[,1]])
yRange <- cbind(xyz$y[overs[,2]],xyz$y[overs[,2]])
}else{
oTree <- hclust(dist(overs),method="single")
oCut <- cutree(oTree,h=10)
oXYc <- data.frame(x=xyz$x[overs[,1]],y=xyz$y[overs[,2]],oCut)
xRange <- do.call("rbind",tapply(oXYc[,1],oCut,range))
yRange <- do.call("rbind",tapply(oXYc[,2],oCut,range))
}
### add user-margins
xRange[,1] <- xRange[,1]-margin
xRange[,2] <- xRange[,2]+margin
yRange[,1] <- yRange[,1]-margin
yRange[,2] <- yRange[,2]+margin
## put it all together
xr <- apply(xRange,1,diff)
xm <- apply(xRange,1,mean)
xRange[xr<minsize,1] <- xm[xr<minsize]-(minsize/2)
xRange[xr<minsize,2] <- xm[xr<minsize]+(minsize/2)
yr <- apply(yRange,1,diff)
ym <- apply(yRange,1,mean)
yRange[yr<minsize,1] <- ym[yr<minsize]-(minsize/2)
yRange[yr<minsize,2] <- ym[yr<minsize]+(minsize/2)
cbind(xRange,yRange)
}
Test code:
x=1:23
y=7:34
m1=list(x=x,y=y,z=outer(x,y,function(x,y){sin(x/3)*cos(y/3)}))
image(m1)
hs = computeHotspots(m1,0.95)
That should give you a matrix of rectangle coordinates:
> hs
[,1] [,2] [,3] [,4]
1 13 15 8 11
2 3 6 17 20
3 22 24 18 20
4 13 16 27 30
Now you can draw them over the image with rect:
image(m1)
rect(hs[,1],hs[,3],hs[,2],hs[,4])
and to show they are where they should be:
image(list(x=m1$x,y=m1$y,z=m1$z>0.95))
rect(hs[,1],hs[,3],hs[,2],hs[,4])
You could of course adapt this to draw circles, but more complex shapes would be tricky. It works best when the regions of interest are fairly compact.
Barry