Reputation: 493
I have the following data frames:
library(dplyr)
d1 <- data_frame(
title = c("base1", "base2", "base3", "base4"),
lat = c(57.3, 58.8, 47.2, 57.8),
long = c(0.4, 3.4, 3.5, 1.2))
d2 <- data_frame(
tas = c("tas1", "tas2", "tas3", "tas4"),
Base= c ("base1", "base2", "base3", "base4"),
lat=c(54.6, 56.4, 54.2, 54.6),
long = c(1.2, 3.4, 3.5, 56.6))
What I would like to do is calculate the distance in miles between tas in d2 and title in d1. So in d2 tas1 has the coordinates 54.6 lat and 1.2 long, and has 'base1' in the 'Base' column. So I would like to calculate the distance between 54.6lat by 1.2long and 57.3lat and 0.4lon.
I've tried to do this using the GeoDistanceInMetresMatrix
function as detailed below but the function doesn't quite give me the structure that I want.
The below article gives some information on GeoDistanceInMetresMatrix
http://eurekastatistics.com/calculating-a-distance-matrix-for-geographic-points-using-r/
This is what I would like the data to look like:
df <- data_frame(
tas = c("tas1", "tas2", "tas3", "tas4"),
Base= c ("base1", "base2", "base3", "base4"),
lat=c(54.6, 56.4, 54.2, 54.6),
long = c(1.2, 3.4, 3.5, 56.6),
difference_miles = c(23, 35, 56, 23))
I've been looking at this all afternoon and can't quite get it right so any help would be appreciated!
Upvotes: 5
Views: 3932
Reputation: 6073
One method might be to use the geosphere
package:
# slightly modify your data because I want to merge it
df1 <- data.frame(
title = c("base1", "base2", "base3", "base4"),
lat1 = c(57.3, 58.8, 47.2, 57.8),
long1 = c(0.4, 3.4, 3.5, 1.2),
stringsAsFactors = FALSE)
df2 <- data.frame(
title = c ("base1", "base2", "base3", "base4"),
lat2 = c(54.6, 56.4, 54.2, 54.6),
long2 = c(1.2, 3.4, 3.5, 56.6),
stringsAsFactors = FALSE)
# merge your data so you're sure your lat/long pairs make sense
df <- merge(df1, df2, by="title")
# calculate distance according to the Haversine method (shortest dist around sphere)
df$dist_meters <- geosphere::distHaversine(
p1=df[ , c("long1", "lat1")],
p2=df[ , c("long2", "lat2")] )
# convert meters to miles
df$dist_miles = df$dist_meters / 1609.34
Upvotes: 5
Reputation: 16832
Since you're already using dplyr
, you can easily add sf
into your workflow. Here I make both your data frames into data frames with sf
columns using the long/lat coordinates and a long/lat projection. Then I transform them each into a US foot-based projection and take the distances. You can add that distance vector to a joined version of your two initial data frames if you need to.
One note to be careful of is the order--I arranged d1_sf
and d2_sf
by the base labels, but if this doesn't work well in a larger or more complex dataset, or there are missing bases, you could use a join here to check.
library(tidyverse)
library(sf)
...
d1_sf <- st_as_sf(d1, coords = c("long", "lat"), crs = 4326) %>%
arrange(title)
d2_sf <- st_as_sf(d2, coords = c("long", "lat"), crs = 4326) %>%
arrange(Base)
distances <- st_distance(
st_transform(d1_sf, crs = 2234),
st_transform(d2_sf, crs = 2234),
by_element = T
)
distances
#> Units: US_survey_foot
#> [1] 1035387.8 916425.4 2591457.0 11553291.3
inner_join(d1, d2, by = c("title" = "Base"), suffix = c("1", "2")) %>%
mutate(dist = distances) %>%
mutate(dist_mi = dist / 5280)
#> # A tibble: 4 x 8
#> title lat1 long1 tas lat2 long2 dist dist_mi
#> <chr> <dbl> <dbl> <chr> <dbl> <dbl> <S3: units> <S3: units>
#> 1 base1 57.3 0.4 tas1 54.6 1.2 " 1035387.8 US_su… " 196.0962 US_su…
#> 2 base2 58.8 3.4 tas2 56.4 3.4 " 916425.4 US_su… " 173.5654 US_su…
#> 3 base3 47.2 3.5 tas3 54.2 3.5 " 2591457.0 US_su… " 490.8062 US_su…
#> 4 base4 57.8 1.2 tas4 54.6 56.6 11553291.3 US_sur… 2188.1234 US_sur…
Created on 2018-08-13 by the reprex package (v0.2.0).
Upvotes: 1
Reputation: 13581
You should also check out sp
library(sp)
p1 <- SpatialPoints(select(d1, long, lat))
p2 <- SpatialPoints(select(d2, long, lat))
spDists(p1, p2, longlat=TRUE, diagonal=TRUE)
# [1] 304.7427 267.2908 778.7028 3359.7988 (output is km)
Upvotes: 2
Reputation: 24069
This is easily accomplished using the geosphere library:
d1 <- data.frame(
title = c("base1", "base2", "base3", "base4"),
lat = c(57.3, 58.8, 47.2, 57.8),
long = c(0.4, 3.4, 3.5, 1.2))
d2 <- data.frame(
tas = c("tas1", "tas2", "tas3", "tas4"),
Base= c ("base1", "base2", "base3", "base4"),
lat=c(54.6, 56.4, 54.2, 54.6),
long = c(1.2, 3.4, 3.5, 56.6))
library(geosphere)
#1609.35 is the conversion from miles to meters
dist<-distGeo(d1[, c("long", "lat")], d2[, c("long", "lat")])/1609.35
df<-cbind(d2, difference_miles=dist)
Upvotes: 6