Skip to content

Instantly share code, notes, and snippets.

@CarlBoneri
Created July 30, 2019 12:01
Show Gist options
  • Save CarlBoneri/550d8ddec068064c4a4937e02b3a6d38 to your computer and use it in GitHub Desktop.
Save CarlBoneri/550d8ddec068064c4a4937e02b3a6d38 to your computer and use it in GitHub Desktop.
#' Convert degrees to radians
deg2rad <- function(deg){
rads <- deg*pi/180
return(rads)
}
#' Calculates the geodesic distance between two points specified by
#' radian latitude/longitude using the
#' Spherical Law of Cosines (slc)
#'
#' Many of the formulas calculating the distance between longitude/latitude
#' points assume a spherical earth, which, as we will see, simplifies things
#' greatly and for most purposes is quite an adequate approximation.
#' Perhaps the simples formula for calculating the great-circle distance is
#' the Spherical Law of Cosines
#'
#'
gcd.slc <- function(long1, lat1, long2, lat2) {
R <- 6371 # Earth mean radius [km]
d <- acos(sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2) * cos(long2-long1)) * R
return(d) # Distance in km
}
#' The Spherical Law of Cosines performs well as long as the distance is not
#' to small (some sources claim it’s accuracy deteriorates at about the
#' 1 metre scale). At very small distances the inverting of the cosine
#' magnifies rounding errors. An alternative formulation that is more robust
#' at small distances is the Haversine formula which in R looks like
#' so (assuming the latitudes and longitudes already have been converted
#' from degrees to radians)
# Calculates the geodesic distance between two points specified by radian latitude/longitude using the
# Haversine formula (hf)
gcd.hf <- function(long1, lat1, long2, lat2){
R <- 6371 # Earth mean radius [km]
delta.long <- (long2 - long1)
delta.lat <- (lat2 - lat1)
a <- sin(delta.lat/2)^2 + cos(lat1) * cos(lat2) * sin(delta.long/2)^2
c <- 2 * asin(min(1,sqrt(a)))
d = R * c
return(d) # Distance in km
}
#' While both the Spherical Law of Cosines and the Haversine formula are
#' simple they both assume a spherical earth. Taking into account that Earth
#' is not perfectly spherical makes things a bit more messy. Vincenty inverse
#' formula for ellipsoids is an iterative methods used to calculate the
#' ellipsoidal distance between two points on the surface of a spheroid.
# Calculates the geodesic distance between two points specified by radian latitude/longitude using
# Vincenty inverse formula for ellipsoids (vif)
gcd.vif <- function(long1, lat1, long2, lat2) {
# WGS-84 ellipsoid parameters
a <- 6378137 # length of major axis of the ellipsoid (radius at equator)
b <- 6356752.314245 # ength of minor axis of the ellipsoid (radius at the poles)
f <- 1/298.257223563 # flattening of the ellipsoid
L <- long2-long1 # difference in longitude
U1 <- atan((1-f) * tan(lat1)) # reduced latitude
U2 <- atan((1-f) * tan(lat2)) # reduced latitude
sinU1 <- sin(U1)
cosU1 <- cos(U1)
sinU2 <- sin(U2)
cosU2 <- cos(U2)
cosSqAlpha <- NULL
sinSigma <- NULL
cosSigma <- NULL
cos2SigmaM <- NULL
sigma <- NULL
lambda <- L
lambdaP <- 0
iterLimit <- 100
while (abs(lambda-lambdaP) > 1e-12 & iterLimit>0) {
sinLambda <- sin(lambda)
cosLambda <- cos(lambda)
sinSigma <- sqrt(
(cosU2*sinLambda) * (cosU2*sinLambda) +
(cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda))
if (sinSigma==0) return(0) # Co-incident points
cosSigma <- sinU1*sinU2 + cosU1*cosU2*cosLambda
sigma <- atan2(sinSigma, cosSigma)
sinAlpha <- cosU1 * cosU2 * sinLambda / sinSigma
cosSqAlpha <- 1 - sinAlpha*sinAlpha
cos2SigmaM <- cosSigma - 2*sinU1*sinU2/cosSqAlpha
if (is.na(cos2SigmaM)) cos2SigmaM <- 0 # Equatorial line: cosSqAlpha=0
C <- f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha))
lambdaP <- lambda
lambda <- L + (1-C) * f * sinAlpha *
(sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)))
iterLimit <- iterLimit - 1
}
if (iterLimit==0) return(NA) # formula failed to converge
uSq <- cosSqAlpha * (a*a - b*b) / (b*b)
A <- 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
B <- uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)))
deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM^2) -
B/6*cos2SigmaM*(-3+4*sinSigma^2)*(-3+4*cos2SigmaM^2)))
s <- b*A*(sigma-deltaSigma) / 1000
return(s) # Distance in km
}
@CarlBoneri
Copy link
Author

Fritz have you tried googling "how to calculate geospatial distances in R?"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment