Calculate Total Miles Traveled from Vectors of Lat/Lon

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



Leave a reply



Submit