Created
July 30, 2019 12:01
-
-
Save CarlBoneri/550d8ddec068064c4a4937e02b3a6d38 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#' 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 | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Fritz have you tried googling "how to calculate geospatial distances in R?"