Calculate total miles traveled from vectors of lat / lon
How about this?
## Setup
library(geosphere)
metersPerMile <- 1609.34
pts <- df1[c("lon", "lat")]
## Pass in two derived data.frames that are lagged by one point
segDists <- distVincentyEllipsoid(p1 = pts[-nrow(df),],
p2 = pts[-1,])
sum(segDists)/metersPerMile
# [1] 1013.919
(To use one of the faster distance calculation algorithms, just substitute distCosine
, distVincentySphere
, or distHaversine
for distVincentyEllipsoid
in the call above.)
Total distance calculation from LatLng List
Try this please. I tested it with Google Maps and works accurately. You can do a loop and find total distance by using 2 points each time. I added some random dummy data to show how it works. Copy this code to https://dartpad.dartlang.org/ and test easily.
import 'dart:math' show cos, sqrt, asin;
void main() {
double calculateDistance(lat1, lon1, lat2, lon2){
var p = 0.017453292519943295;
var c = cos;
var a = 0.5 - c((lat2 - lat1) * p)/2 +
c(lat1 * p) * c(lat2 * p) *
(1 - c((lon2 - lon1) * p))/2;
return 12742 * asin(sqrt(a));
}
List<dynamic> data = [
{
"lat": 44.968046,
"lng": -94.420307
},{
"lat": 44.33328,
"lng": -89.132008
},{
"lat": 33.755787,
"lng": -116.359998
},{
"lat": 33.844843,
"lng": -116.54911
},{
"lat": 44.92057,
"lng": -93.44786
},{
"lat": 44.240309,
"lng": -91.493619
},{
"lat": 44.968041,
"lng": -94.419696
},{
"lat": 44.333304,
"lng": -89.132027
},{
"lat": 33.755783,
"lng": -116.360066
},{
"lat": 33.844847,
"lng": -116.549069
},
];
double totalDistance = 0;
for(var i = 0; i < data.length-1; i++){
totalDistance += calculateDistance(data[i]["lat"], data[i]["lng"], data[i+1]["lat"], data[i+1]["lng"]);
}
print(totalDistance);
}
Getting distance between two points based on latitude/longitude
Edit: Just as a note, if you just need a quick and easy way of finding the distance between two points, I strongly recommend using the approach described in Kurt's answer below instead of re-implementing Haversine -- see his post for rationale.
This answer focuses just on answering the specific bug OP ran into.
It's because in Python, all the trig functions use radians, not degrees.
You can either convert the numbers manually to radians, or use the radians
function from the math module:
from math import sin, cos, sqrt, atan2, radians
# approximate radius of earth in km
R = 6373.0
lat1 = radians(52.2296756)
lon1 = radians(21.0122287)
lat2 = radians(52.406374)
lon2 = radians(16.9251681)
dlon = lon2 - lon1
dlat = lat2 - lat1
a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlon / 2)**2
c = 2 * atan2(sqrt(a), sqrt(1 - a))
distance = R * c
print("Result:", distance)
print("Should be:", 278.546, "km")
The distance is now returning the correct value of 278.545589351
km.
Calculate distance between consecutive rows, by group
Try :
df %>% group_by(fac) %>%
mutate(lat_prev = lag(lat,1), lon_prev = lag(lon,1) ) %>%
mutate(dist = distHaversine(matrix(c(lon_prev, lat_prev), ncol = 2),
matrix(c(lon, lat), ncol = 2))) %>%
summarize(dist = sum(dist,na.rm=T))
# A tibble: 3 x 2
fac dist
<fct> <dbl>
1 A 93708.
2 B 219742.
3 C 347578.
Much better, as suggested by Henrik:
df %>% group_by(fac) %>%
summarize(dist = distHaversine(cbind(lon, lat))) %>%
summarize(dist = sum(dist,na.rm=T))
Distances between consecutive points
Using your data as dat:
sapply(2:nrow(dat),function(i){distm(dat[i-1,],dat[i,])})
# [1] 82072.86 137911.46 80747.82 161492.73
Which is the same result as @JoshObrien if you substitute distHaversine(...)
for distVincentyEllipsoid(...)
in his code snippet.
Function to calculate geospatial distance between two points (lat,long) using R
Loading the geosphere package you can use a number of different functions
library(geosphere)
distm(c(lon1, lat1), c(lon2, lat2), fun = distHaversine)
Also:
distHaversine()
distMeeus()
distRhumb()
distVincentyEllipsoid()
distVincentySphere()
...
Calculating average speed from lon/lat and timestamp using distance (geosphere) and difftime
dplyr::lag
or data.table::shift
and grouping is handy for this, though it can be done manually in base with something like c(NA, variable[-length(variable)])
and aggregate
:
library(dplyr)
df <- structure(list(Timestamp = structure(c(1352704121, 1352704181, 1352704241, 1352708321, 1352708381, 1352708441),
class = c("POSIXct", "POSIXt"), tzone = ""),
ID = c(1L, 1L, 1L, 2L, 2L, 2L),
lat = c(76.57169, 76.44325, 76.90897, 76.11152, 76.29013, 76.15544),
lon = c(-110.807, -110.7525, -110.8613, -110.2037, -110.3838, -110.4506)),
class = "data.frame", .Names = c("Timestamp", "ID", "lat", "lon"), row.names = c(NA, -6L))
df <- df %>%
group_by(ID) %>%
mutate(dist_m = geosphere::distVincentyEllipsoid(cbind(lon, lat),
cbind(lag(lon), lag(lat))),
time_s = difftime(Timestamp, lag(Timestamp), units = 'secs'),
speed_m_per_s = dist_m / as.integer(time_s))
df
#> # A tibble: 6 x 7
#> # Groups: ID [2]
#> Timestamp ID lat lon dist_m time_s speed_m_per_s
#> <dttm> <int> <dbl> <dbl> <dbl> <time> <dbl>
#> 1 2012-11-12 02:08:41 1 76.57169 -110.8070 NA NA secs NA
#> 2 2012-11-12 02:09:41 1 76.44325 -110.7525 14408.23 60 secs 240.1371
#> 3 2012-11-12 02:10:41 1 76.90897 -110.8613 52065.53 60 secs 867.7588
#> 4 2012-11-12 03:18:41 2 76.11152 -110.2037 NA NA secs NA
#> 5 2012-11-12 03:19:41 2 76.29013 -110.3838 20507.15 60 secs 341.7859
#> 6 2012-11-12 03:20:41 2 76.15544 -110.4506 15140.03 60 secs 252.3338
Since the data.frame is already grouped, aggregation only requires summing:
df_avg <- df %>%
summarise(dist_m = sum(dist_m, na.rm = TRUE),
time_s = sum(as.integer(time_s), na.rm = TRUE),
speed_m_per_s = dist_m / time_s)
df_avg
#> # A tibble: 2 x 4
#> ID dist_m time_s speed_m_per_s
#> <int> <dbl> <int> <dbl>
#> 1 1 66473.76 120 553.9480
#> 2 2 35647.18 120 297.0598
Units are in meters per second; convert as you like.
Calculate distance longitude latitude of multiple in dataframe R
Instead of distm
you can use the distHaversine
-function. Further in your mutate
call you should not repeat the dataframe and use the $
operator, mutate
already nows where to look for the columns. The error occurs because you need to use cbind
instead of c
, as c
creates one long vector, simply stacking the columns together, whereas cbind
creates a dataframe with two columns (what you want to have in this case).
library(geosphere)
library(dplyr)
mutate(mydata,
Distance = distHaversine(cbind(Longitude, Latitude),
cbind(lag(Longitude), lag(Latitude))))
# Callsign Altitude Speed Direction Date_Time Latitude Longitude Distance
# 1 A118 18000 110 340 2017-11-06T22:28:09 70.6086 58.2959 NA
# 2 A118 18500 120 339 2017-11-06T22:29:09 72.1508 58.7894 172569.2
# 3 B222 18500 150 350 2017-11-08T07:28:09 71.1689 59.1234 109928.5
# 4 D123 19000 150 110 2018-05-29T15:13:27 69.4523 68.1235 387356.2
With distCosine
it is a little bit more tricky, as it doesn't return NA
if one of the input latitudes or longitudes is missing. Thus I modified the function a little bit and this solves the problem:
modified_distCosine <- function(Longitude1, Latitude1, Longitude2, Latitude2) {
if (any(is.na(c(Longitude1, Latitude1, Longitude2, Latitude2)))) {
NA
} else {
distCosine(c(Longitude1, Latitude1), c(Longitude2, Latitude2))
}
}
mutate(mydata,
Distance = mapply(modified_distCosine,
Longitude, Latitude, lag(Longitude), lag(Latitude)))
# Callsign Altitude Speed Direction Date_Time Latitude Longitude Distance
# 1 A118 18000 110 340 2017-11-06T22:28:09 70.6086 58.2959 NA
# 2 A118 18500 120 339 2017-11-06T22:29:09 72.1508 58.7894 172569.2
# 3 B222 18500 150 350 2017-11-08T07:28:09 71.1689 59.1234 109928.5
# 4 D123 19000 150 110 2018-05-29T15:13:27 69.4523 68.1235 387356.2
Here I use mapply
to apply the modified function with the arguments Longitude, Latitude, lag(Longitude), lag(Latitude)
.
I'm quite sure there has to be a more elegant way, but at least this works.
Data
mydata <- structure(list(Callsign = c("A118", "A118", "B222", "D123"),
Altitude = c(18000L, 18500L, 18500L, 19000L),
Speed = c(110L, 120L, 150L, 150L),
Direction = c(340L, 339L, 350L, 110L),
Date_Time = c("2017-11-06T22:28:09", "2017-11-06T22:29:09", "2017-11-08T07:28:09", "2018-05-29T15:13:27"),
Latitude = c(70.6086, 72.1508, 71.1689, 69.4523),
Longitude = c(58.2959, 58.7894, 59.1234, 68.1235)),
.Names = c("Callsign", "Altitude", "Speed", "Direction", "Date_Time", "Latitude", "Longitude"),
class = "data.frame", row.names = c(NA, -4L))
Calculate distances by trip in dataframe
The best way I found to get all the information from the data.frame was using the adehabitat package.
Thus I used the "SpatialPointDataframe" called locs_utm (same as above that I called locs1_utm, to convert it into coordinates with "x" and "y" names for the columns and made a column called BirdTrips with unique values so I can use that later as burst and split obtain the distances and times for each trip combining the id column with the trip column.
gent$trip[gent$trip<10]=paste(0,gent$trip[gent$trip<10],sep="")#so that trip 1,2,3 becomes 01,02,03...
gent$BirdTrip=paste(gent$id,gent$trip,sep=".") #combine the animal id with the trip so you have 1.01,1.02, 1.03 and soon
gent$BirdTrip=as.factor(gent$BirdTrip)
BirdTrip
1.01
1.01
...
2.01
foo = as.data.frame(locs_utm) #transform locs_utm-->SpatialPointDataframe into data.frame
foo = cbind.data.frame(foo$coords.x1, foo$coords.x2) #convert lon lat, columns in coordinatse
names(foo) = c("x", "y") #convert the columns in "x" and "y" to be able to use the as.ltraj and convert thedata in ltraj forat
tr=as.ltraj(xy=foo,
date=p.1$date,
id=p.1$id,
burst=p.1$BirdTrip)
class(tr) #[1] "ltraj" "list"
tr=ld(tr) #transform the ltraj into data.frame
To gather all the information in one data.frame I created some vectors using as.POSIXct and tappply functions to obtein length of the trip, beggining and end dates of the trip and maximum displacement.
ts = as.data.frame(as.POSIXct(tapply(tr$date, tr$burst, min, na.rm = T), origin = "1970-01-01")) #trip start
te = as.data.frame(as.POSIXct(tapply(tr$date, tr$burst, max, na.rm = T), origin = "1970-01-01")) #trip end
th = as.data.frame(tapply(tr$dt, tr$burst, sum, na.rm = T)/3600) #trip hours
tl = as.data.frame(tapply(tr$dist, list(tr$burst), sum, na.rm = T)/1000) #trip kms-->LENGTH
td = as.data.frame(sqrt(tapply(tr$R2n, list(tr$burst), max, na.rm = T))/1000) #max displacement kms
resumen = cbind.data.frame(ts[,1], te[,1], as.numeric(th[,1]), as.numeric(tl[,1]), as.numeric(td[,1]))
names(resumen) = c("Start", "End", "Hours", "Length", "Displacement")
View(resumen)
Calculate distance between two latitude-longitude points? (Haversine formula)
This link might be helpful to you, as it details the use of the Haversine formula to calculate the distance.
Excerpt:
This script [in Javascript] calculates great-circle distances between the two points –
that is, the shortest distance over the earth’s surface – using the
‘Haversine’ formula.
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2-lat1); // deg2rad below
var dLon = deg2rad(lon2-lon1);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2)
;
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI/180)
}
Related Topics
Converting Nested List (Unequal Length) to Data Frame
How to Create Md5 Hash of a Column in R
Error Creating R Data.Table with Date-Time Posixlt
Return Df with a Columns Values That Occur More Than Once
Geom_Tile and Facet_Grid/Facet_Wrap for Same Height of Tiles
Why the Built-In Lm Function Is So Slow in R
Combining New Lines and Italics in Facet Labels with Ggplot2
Combining Duplicated Rows in R and Adding New Column Containing Ids of Duplicates
Change Path.Expand Location (Win 7)
Get Decision Tree Rule/Path Pattern for Every Row of Predicted Dataset for Rpart/Ctree Package in R
Finding Overlap in Ranges with R