- Notifications
You must be signed in to change notification settings - Fork8
Ultra lightweight, ultra fast calculation of geo distances
License
hypertidy/geodist
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
An ultra-lightweight, zero-dependency package for very fast calculationof geodesic distances. Main eponymous function,geodist(), acceptsonly one or two primary arguments, which must be rectangular objectswith unambiguously labelled longitude and latitude columns (that is,some variant oflon/lat, orx/y).
n<-50x<- cbind (-10+20* runif (n),-10+20* runif (n))y<- cbind (-10+20* runif (2*n),-10+20* runif (2*n))colnames (x)<- colnames (y)<- c ("x","y")d0<- geodist (x)# A 50-by-50 matrixd1<- geodist (x,y)# A 50-by-100 matrixd2<- geodist (x,sequential=TRUE)# Vector of length 49d2<- geodist (x,sequential=TRUE,pad=TRUE)# Vector of length 50
You can install latest stable version ofgeodist from CRAN with:
install.packages("geodist")# current CRAN version
Alternatively, current development versions can be installed using anyof the following options:
# install.packages("remotes")remotes::install_git("https://git.sr.ht/~mpadge/geodist")remotes::install_git("https://codeberg.org/hypertidy/geodist")remotes::install_bitbucket("hypertidy/geodist")remotes::install_gitlab("hypertidy/geodist")remotes::install_github("hypertidy/geodist")
Then load with
library (geodist)packageVersion ("geodist")#> [1] '0.0.7.30'
Input(s) to thegeodist() function can be in arbitrary rectangularformat.
n<-1e1x<-tibble::tibble (x=-180+360* runif (n),y=-90+180* runif (n))dim (geodist (x))#> Maximum distance is > 100km. The 'cheap' measure is inaccurate over such#> large distances, you'd likely be better using a different 'measure'.#> [1] 10 10y<-tibble::tibble (x=-180+360* runif (2*n),y=-90+180* runif (2*n))dim (geodist (x,y))#> Maximum distance is > 100km. The 'cheap' measure is inaccurate over such#> large distances, you'd likely be better using a different 'measure'.#> [1] 10 20x<- cbind (-180+360* runif (n),-90+100* runif (n), seq (n), runif (n))colnames (x)<- c ("lon","lat","a","b")dim (geodist (x))#> Maximum distance is > 100km. The 'cheap' measure is inaccurate over such#> large distances, you'd likely be better using a different 'measure'.#> [1] 10 10
All outputs are distances in metres, calculated with a variety ofspherical and elliptical distance measures. Distance measures currentlyimplemented are Haversine, Vincenty (spherical and elliptical)), thevery fastmapbox cheapruler(see theirblogpost),and the “reference” implementation ofKarney(2013),as implemented in the packagesf. (Note thatgeodist doesnot acceptsf-format objects;thesf package itself shouldbe used for that.) Themapbox cheap ruleralgorithm is intended toprovide approximate yet very fast distance calculations within smallareas (tens to a few hundred kilometres across).
Thegeodist_benchmark() function - the only other function provided bythegeodist package - compares the accuracy of the different metricsto the nanometre-accuracy standard ofKarney(2013).
geodist_benchmark (lat=30,d=1000)#> haversine vincenty cheap#> absolute 0.748852400 0.748852400 0.576849087#> relative 0.002042704 0.002042704 0.001598258
All distances (d) are in metres, and all measures are accurate towithin 1m over distances out to several km (at the chosen latitude of 30degrees). The following plots compare the absolute and relativeaccuracies of the different distance measures implemented here. Themapbox cheap ruler algorithm is the most accurate for distances out toaround 100km, beyond which it becomes extremely inaccurate. Averagerelative errors of Vincenty distances remain generally constant ataround 0.2%, while relative errors of cheap-ruler distances out to 100kmare around 0.16%.
The following code demonstrates the relative speed advantages of thedifferent distance measures implemented in thegeodist package.
n<-1e3dx<-dy<-0.01x<- cbind (-100+dx* runif (n),20+dy* runif (n))y<- cbind (-100+dx* runif (2*n),20+dy* runif (2*n))colnames (x)<- colnames (y)<- c ("x","y")rbenchmark::benchmark (replications=10,order="test",d1<- geodist (x,measure="cheap"),d2<- geodist (x,measure="haversine"),d3<- geodist (x,measure="vincenty"),d4<- geodist (x,measure="geodesic")) [,1:4]#> test replications elapsed relative#> 1 d1 <- geodist(x, measure = "cheap") 10 0.068 1.000#> 2 d2 <- geodist(x, measure = "haversine") 10 0.137 2.015#> 3 d3 <- geodist(x, measure = "vincenty") 10 0.223 3.279#> 4 d4 <- geodist(x, measure = "geodesic") 10 3.038 44.676
Geodesic distance calculation is available in thesfpackage. Comparing computationspeeds requires conversion of sets of numeric lon-lat points tosfform with the following code:
require (magrittr)x_to_sf<-function (x){ sapply (seq (nrow (x)),function (i)sf::st_point (x [i, ]) %>%sf::st_sfc ()) %>%sf::st_sfc (crs=4326)}
n<-1e2x<- cbind (-180+360* runif (n),-90+180* runif (n))colnames (x)<- c ("x","y")xsf<- x_to_sf (x)sf_dist<-function (xsf)sf::st_distance (xsf,xsf)geo_dist<-function (x) geodist (x,measure="geodesic")rbenchmark::benchmark (replications=10,order="test", sf_dist (xsf), geo_dist (x)) [,1:4]#> test replications elapsed relative#> 2 geo_dist(x) 10 0.061 1.000#> 1 sf_dist(xsf) 10 0.146 2.393
Confirm that the two give almost identical results:
ds<-matrix (as.numeric (sf_dist (xsf)),nrow= length (xsf))dg<- geodist (x,measure="geodesic")formatC (max (abs (ds-dg)),format="e")#> [1] "3.7708e+04"
All results are in metres, so the two differ by only around 10nanometres.
Thegeosphere packagealso offers sequential calculation which is benchmarked with thefollowing code:
fgeodist<-function () geodist (x,measure="vincenty",sequential=TRUE)fgeosph<-function ()geosphere::distVincentySphere (x)rbenchmark::benchmark (replications=10,order="test", fgeodist (), fgeosph ()) [,1:4]#> test replications elapsed relative#> 1 fgeodist() 10 0.016 1.00#> 2 fgeosph() 10 0.036 2.25
geodist is thus around 3 times faster thansf for highly accurategeodesic distance calculations, and around twice as fast asgeospherefor calculation of sequential distances.
require (devtools)require (testthat)
date()#> [1] "Wed Oct 19 12:10:55 2022"devtools::test("tests/")#> ℹ Testing geodist#> ══ Results ════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════#> Duration: 1.3 s#>#> [ FAIL 0 | WARN 0 | SKIP 0 | PASS 125 ]
All contributions to this project are gratefully acknowledged using theallcontributorspackage following theall-contributors specification.Contributions of any kind are welcome!
mpadge | daniellemccool |
mdsumner | edzer | njtierney | mkuehn10 | asardaes | marcosci | mem48 |
dcooley | Robinlovelace | espinielli | Maschette |
About
Ultra lightweight, ultra fast calculation of geo distances
Resources
License
Contributing
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Contributors6
Uh oh!
There was an error while loading.Please reload this page.
