[R-sig-Geo] function to convert 3D geographical coordinates to Cartesian?
Tom Roche
Tom_Roche at pobox.com
Mon Dec 31 06:48:49 CET 2012
https://stat.ethz.ch/pipermail/r-sig-geo/2012-December/017102.html
> Is there packaged code to convert geographical coordinates (e.g.,
> longitude, latitude, elevation) to [simple] Cartesian coordinates
> in 3-space?
Just to clarify: I'm looking for code like that which follows my .sig
(to end of post), but which
* is much better tested/known/used (I just hacked the following this
evening)
* _properly_ converts elevation to radial distance (from earth center),
i.e., using a user-specified CRS. My function elev2rdist below is a
blatant kludge, though it should work for the model for which I'm
assimilating data.
This is such low-level functionality that I can't believe it's not API
somewhere, but I have looked at several packages and am not seeing it.
Is it so low-level that it's just not exposed as API? or am I just not
seeing it in package docs? or did I fail a spatial-intelligence test?
TIA, Tom Roche <Tom_Roche at pobox.com>----sample code follows to EOP----
# Convert geographic coordinates (lon, lat, elevation) to
# Cartesian coordinates (x, y, z) relative to earth center.
geo2cart <- function(lon.lat.elev) {
library(pracma) # for sph2cart
stopifnot(is.numeric(lon.lat.elev))
return(sph2cart(geo2sph(lat.lon.elev)))
} # end function geo2cart(lon.lat.elev)
# Convert geographic coordinates (lon, lat, elevation) to
# spherical coordinates (azimuth/Θ, polar angle/φ, radial distance/r),
# relative to earth center
geo2sph <- function(lon.lat.elev) {
stopifnot(is.numeric(lon.lat.elev))
# Use convention of package=pracma: see http://cran.r-project.org/web/packages/pracma/
# same as described @ http://en.wikipedia.org/wiki/File:3D_Spherical_2.svg
# convert
# * longitude -> azimuth (Θ)
# * latitude -> polar angle (φ)
# * elevation -> radial distance from earth center (r)
if (is.vector(lon.lat.elev) && length(lon.lat.elev) == 3) {
theta <- lon2azi(lon.lat.elev[1])
phi <- lat2pol(lon.lat.elev[2])
r <- elev2rdist(lon.lat.elev[3])
m <- 1
} else if (is.matrix(lon.lat.elev) && ncol(lon.lat.elev) == 3) {
theta <- lon2azi(lon.lat.elev[,1])
phi <- lat2pol(lon.lat.elev[,2])
r <- elev2rdist(lon.lat.elev[,3])
m <- nrow(lon.lat.elev)
} else {
stop('geo2sph: ERROR: input must be a vector of length 3 or a matrix with 3 columns.')
}
if (m == 1) {
tpr <- c(theta, phi, r)
} else {
tpr <- cbind(theta, phi, r)
}
return(tpr)
} # end function geo2sph(lon.lat.elev)
# Filter and convert longitudes to radians.
# Much-too-simple tests:
# > lon2azi(c(seq(0, 180), seq(-179, -1)))/pi
# > lon2azi(c(0:359))
lon2azi <- function(lon) {
library(pracma) # for deg2rad
stopifnot(is.numeric(lon))
# only handle vectors
if (!is.vector(lon)) {
lon.vec <- c(lon)
} else {
lon.vec <- lon
}
# only take -180 <= lon.vec <= 180
# TODO: better error messages
stopifnot(length(lon.vec[lon.vec < -180]) == 0)
stopifnot(length(lon.vec[lon.vec > 180]) == 0)
# Convert to azimuth=Θ in radians.
# Convert to 0 <= lon.vec <= 360 (hmm ...) e.g., -179 -> 181.
lon.vec[lon.vec < 0] <- 360.0 + lon.vec[lon.vec < 0]
theta.vec <- deg2rad(lon.vec)
if (!is.vector(lon)) {
return(theta.vec[1])
} else {
return(theta.vec)
}
} # end function lon2azi(lon)
# Filter and convert latitudes to radians.
# Much-too-simple tests:
# > lat2pol(c(seq(90, 0), seq(-1, -90)))/pi
# > lat2pol(c(-100:100))
lat2pol <- function(lat) {
library(pracma) # for deg2rad
stopifnot(is.numeric(lat))
# only handle vectors
if (!is.vector(lat)) {
lat.vec <- c(lat)
} else {
lat.vec <- lat
}
# only take -90 <= lat.vec <= 90
# TODO: better error messages
stopifnot(length(lat.vec[lat.vec < -90]) == 0)
stopifnot(length(lat.vec[lat.vec > 90]) == 0)
# Convert to polar angle=φ in radians.
# northern hemisphere + equator:
# lat.vec[lat.vec >= 0] <- 90.0 - lat.vec[lat.vec >= 0]
# southern hemisphere:
# lat.vec[lat.vec < 0] <- 90.0 - lat.vec[lat.vec < 0]
phi.vec <- deg2rad(90.0 - lat.vec)
if (!is.vector(lat)) {
return(phi.vec[1])
} else {
return(phi.vec)
}
} # end function lat2pol(lat)
# Convert elevation to radial distance
# Much-too-simple tests:
# > elev2rdist(c(seq(1000, by=1000, length=10)))
# > elev2rdist(c(seq(-1000, by=1000, length=10)))
elev2rdist <- function(elev) {
r.earth.meters <- 6370000.0 # constant, kludge, see below
stopifnot(is.numeric(elev))
# only handle vectors
if (!is.vector(elev)) {
elev.vec <- c(elev)
} else {
elev.vec <- elev
}
# Only take non-negative elevations.
stopifnot(length(elev.vec[elev.vec < 0]) == 0)
# Convert elevation to radial distance (from earth center).
# TODO: use coordinate reference system.
# Kludge: spherical earth with radius defined above.
r.vec <- r.earth.meters + elev
if (!is.vector(elev)) {
return(r.vec[1])
} else {
return(r.vec)
}
} # end function elev2rdist(elev)
More information about the R-sig-Geo
mailing list