Suppose I have an rgl
spherical object and I want to plot contours on the surface of this sphere. I figure that the contourLines3d
function from the rgl
R package is my best bet, so I'm going to adapt their MWE for us here:
lat <- matrix(seq(90, -90, len = 50)*pi/180, 50, 50, byrow = TRUE)
long <- matrix(seq(-180, 180, len = 50)*pi/180, 50, 50)
r <- 6378.1 # radius of Earth in km
x <- r*cos(lat)*cos(long)
y <- r*cos(lat)*sin(long)
z <- r*sin(lat)
open3d()
ids <- persp3d(x, y, z, col = "white",
specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "",
normal_x = x, normal_y = y, normal_z = z, polygon_offset = 1)
contourLines3d(ids, func)
where I need to specify func properly to plot the contours. This is where I'm struggling. Suppose I have a pdf function (dSphereFunc
) that computes the density of any point on the sphere. How can I plot contours using my dSphereFunc
? I'm sure there's a relatively simple way to do this, but I'm really having troubles here. Thank you for your help.
Update: As requested in the comments, I have supplied the density function that I'm trying to evaluate:
dSphereFunc = function(y, mu, Vinv){
d = length(mu)
ytVinvy = t(y)%*%Vinv%*%y
ytmu = t(y)%*%mu
result = ((2*pi)^(-(d-1)/2))/(ytVinvy^(d/2))*exp((ytmu^2/ytVinvy - t(mu)%*%mu)/2)*mdmo(d, ytmu/(ytVinvy^(1/2)))
return(result)
}
mdmo = function(d, t){
t = c(t) # formatting from matrix to constant
integrand = function(x) x^(d-1)*exp(-(x-t)^2/2)
integral = integrate(integrand, lower = 0, upper = Inf)
return((2*pi)^(-1/2)*integral$value)
}
In the above function, we are treating mu
(a dx1
vector) and V
(a dxd
matrix) as fixed (i.e., already estimated), where d=3
; additionally, y
is a dx1
vector which is the spherical coordinates x, y, and z.
To have a MWE, here are some values for mu
and Vinv
:
mu=c(2,2,2)
Vinv=solve(matrix(c(1.57,-0.08,-0.5,-0.08,0.74,0.34,-0.5,0.34,1.16),nrow=3))
test=c(1,3,4)
test=test/norm(test,type="2")
dSphereFunc(test,mu,Vinv) # 1.156622
I think my trouble is that dSphereFunc
takes three arguments, the latter two being fixed. So, I tried using the following function which only requires one argument, xyz
which is a dx1
vector for the spherical coordinates x, y, and z:
densFunc = function(xyz){apply(xyz, 1, function(point) dSphereFunc(point, mu, Vinv))}
However, this is still not working.
The code you were using was fine, the only problem is with the plotting: the contours all appear on the back of the sphere, so you can't see them. Setting alpha = 0.5
when you draw the sphere makes them available, or you can rotate the sphere until they show up. Here the lines are showing through from the back of the sphere:
lat <- matrix(seq(90, -90, len = 50)*pi/180, 50, 50, byrow = TRUE)
long <- matrix(seq(-180, 180, len = 50)*pi/180, 50, 50)
r <- 6378.1 # radius of Earth in km
x <- r*cos(lat)*cos(long)
y <- r*cos(lat)*sin(long)
z <- r*sin(lat)
library(rgl)
open3d()
#> glX
#> 1
ids <- persp3d(x, y, z, col = "white",
specular = "black", axes = FALSE, box = FALSE, xlab = "", ylab = "", zlab = "",
normal_x = x, normal_y = y, normal_z = z, polygon_offset = 1, alpha = 0.5)
dSphereFunc = function(y, mu, Vinv){
d = length(mu)
ytVinvy = t(y)%*%Vinv%*%y
ytmu = t(y)%*%mu
result = ((2*pi)^(-(d-1)/2))/(ytVinvy^(d/2))*exp((ytmu^2/ytVinvy - t(mu)%*%mu)/2)*mdmo(d, ytmu/(ytVinvy^(1/2)))
return(result)
}
mdmo = function(d, t){
t = c(t) # formatting from matrix to constant
integrand = function(x) x^(d-1)*exp(-(x-t)^2/2)
integral = integrate(integrand, lower = 0, upper = Inf)
return((2*pi)^(-1/2)*integral$value)
}
mu=c(2,2,2)
Vinv=solve(matrix(c(1.57,-0.08,-0.5,-0.08,0.74,0.34,-0.5,0.34,1.16),nrow=3))
test=c(1,3,4)
test=test/norm(test,type="2")
dSphereFunc(test,mu,Vinv)
#> [,1]
#> [1,] 1.156622
densFunc = function(xyz){apply(xyz, 1, function(point) dSphereFunc(point, mu, Vinv))}
contourLines3d(ids, densFunc)
Created on 2024-05-09 with reprex v2.1.0