Search code examples
rgpsdistance

Calculate the distance between animals at different time points


I have the GPS coordinates in Easting & Northing for a year for three animals. I want to calculate the distance between each of the animals at each of the time points.

I have no idea how to do this and any help would be very appreciated.

I've included an example of my data in case that helps.

     ID     GPS.E  GPS.S  Date       Time
1  Animal 1 417547 393907 2017-01-01 06:19
2  Animal 1 417769 395000 2017-01-01 16:34
3  Animal 1 418418 394985 2017-01-02 08:18
4  Animal 1 419448 395405 2017-01-02 15:57
5  Animal 1 418249 396145 2017-01-03 07:24
6  Animal 1 417399 396238 2017-01-03 17:44
7  Animal 1 417320 396119 2017-01-04 06:33
8  Animal 1 417770 396080 2017-01-04 17:01
9  Animal 1 417232 396812 2017-01-05 08:43
10 Animal 1 417716 396745 2017-01-05 16:43
11 Animal 2 416571 396099 2017-01-01 06:24
12 Animal 2 416423 395996 2017-01-01 16:15
13 Animal 2 416184 395916 2017-01-02 08:28
14 Animal 2 416002 395858 2017-01-02 15:34
15 Animal 2 415454 395993 2017-01-03 07:14
16 Animal 2 415450 397175 2017-01-03 17:27
17 Animal 2 415781 396949 2017-01-04 06:12
18 Animal 2 415702 396949 2017-01-04 17:23
19 Animal 2 415017 397185 2017-01-05 08:12
20 Animal 2 414516 396990 2017-01-05 16:18
21 Animal 3 418971 394300 2017-01-01 05:59
22 Animal 3 418275 394558 2017-01-01 16:45
23 Animal 3 419881 394940 2017-01-02 08:20
24 Animal 3 420304 394669 2017-01-02 15:25
25 Animal 3 419585 394825 2017-01-03 07:20
26 Animal 3 421528 396153 2017-01-03 17:03
27 Animal 3 420045 396510 2017-01-04 06:27
28 Animal 3 419636 396349 2017-01-04 17:17
29 Animal 3 419499 396212 2017-01-05 08:22
30 Animal 3 420515 395898 2017-01-05 16:14 ````


The desired output is 

ID      Distance to Animal 1  Distance to Animal 2  Distance to Animal 3 
Animal 1
Animal 1
Animal 1
Animal 1
Animal 1
Animal 1
Animal 1
Animal 1
Animal 1
Animal 1
Animal 2
Animal 2
Animal 2
Animal 2
etc. 


   

Solution

  • Looks like a simple question, but surprisingly hard to tackle (for me at least)..

    Please check some answers by hand, to make sure i understood the coordinates correctly. Distance is (should be) in metres..

    library( data.table )
    library( sf )
    library( geosphere )
    library( tidyverse )
    
    #sample data
    DT <- data.table::fread("rowid ID     GPS.E  GPS.S  Date       Time
    1  Animal1 417547 393907 2017-01-01 06:19
    2  Animal1 417769 395000 2017-01-01 16:34
    3  Animal1 418418 394985 2017-01-02 08:18
    4  Animal1 419448 395405 2017-01-02 15:57
    5  Animal1 418249 396145 2017-01-03 07:24
    6  Animal1 417399 396238 2017-01-03 17:44
    7  Animal1 417320 396119 2017-01-04 06:33
    8  Animal1 417770 396080 2017-01-04 17:01
    9  Animal1 417232 396812 2017-01-05 08:43
    10 Animal1 417716 396745 2017-01-05 16:43
    11 Animal2 416571 396099 2017-01-01 06:24
    12 Animal2 416423 395996 2017-01-01 16:15
    13 Animal2 416184 395916 2017-01-02 08:28
    14 Animal2 416002 395858 2017-01-02 15:34
    15 Animal2 415454 395993 2017-01-03 07:14
    16 Animal2 415450 397175 2017-01-03 17:27
    17 Animal2 415781 396949 2017-01-04 06:12
    18 Animal2 415702 396949 2017-01-04 17:23
    19 Animal2 415017 397185 2017-01-05 08:12
    20 Animal2 414516 396990 2017-01-05 16:18
    21 Animal3 418971 394300 2017-01-01 05:59
    22 Animal3 418275 394558 2017-01-01 16:45
    23 Animal3 419881 394940 2017-01-02 08:20
    24 Animal3 420304 394669 2017-01-02 15:25
    25 Animal3 419585 394825 2017-01-03 07:20
    26 Animal3 421528 396153 2017-01-03 17:03
    27 Animal3 420045 396510 2017-01-04 06:27
    28 Animal3 419636 396349 2017-01-04 17:17
    29 Animal3 419499 396212 2017-01-05 08:22
    30 Animal3 420515 395898 2017-01-05 16:14")
    
    DT[, datetime := as.POSIXct( paste(Date, Time ), format = "%Y-%m-%d %H:%M" ) ]
    
    #convert cvoordinates to 'normal' wgs84 lon/lat
    library( sf )
    DT[ , c("lon", "lat") := DT %>% 
          sf::st_as_sf( coords = c("GPS.S", "GPS.E"), crs = 32629 ) %>% 
          sf::st_transform( 4326 ) %>% sf::st_coordinates() %>% data.table::as.data.table() ][]
    
    
    #split by animal
    L <- split( DT, by = "ID" )
    #update to get closest rownumbers
    lapply( L, function(x) {
      lapply( L, function(y) {
        #update x with the rowid of the nearest timestamp in y
        x[, unique(y$ID) := y[x, x.rowid, roll = "nearest", on = .(datetime)] ]
      })
    })
    
    #bind together again
    DT <- data.table::rbindlist( L )
    
    #melt to long format
    DT.melt <- data.table::melt(DT, 
                                measure.vars = patterns("^Animal[0-9]+"), 
                                variable.name = "ID2", 
                                variable.factor = FALSE,
                                value.name = "rowid2" )
    #join in the coordinates of the ID2-animal
    DT.melt[ DT, `:=`( lon2 = i.lon, lat2 = i.lat ), on = .(rowid2 = rowid) ][]
    #calculate the distance between c(lon, lat) and c(lon2, lat2)
    # I do no know the data.table approach here, so a small switch to the tidyverse
    DT.melt <- DT.melt %>%
      dplyr::mutate( distance = purrr::pmap( 
        list( a = lon, 
              b = lat, 
              x = lon2,
              y = lat2 ), 
        ~ round( geosphere::distHaversine( c(..1, ..2), c(..3, ..4) ) ) ) )
    #now, cast to wide again
    dcast( DT.melt, rowid + ID + GPS.E + GPS.S + Date + Time ~ ID2, value.var = "distance" )
    
    
    #output
    #     rowid      ID  GPS.E  GPS.S       Date  Time Animal1 Animal2 Animal3
    #  1:     1 Animal1 417547 393907 2017-01-01 06:19       0    2403    1487
    #  2:     2 Animal1 417769 395000 2017-01-01 16:34       0    1682     675
    #  3:     3 Animal1 418418 394985 2017-01-02 08:18       0    2435    1474
    #  4:     4 Animal1 419448 395405 2017-01-02 15:57       0    3499    1134
    #  5:     5 Animal1 418249 396145 2017-01-03 07:24       0    2819    1885
    #  6:     6 Animal1 417399 396238 2017-01-03 17:44       0    2175    4159
    #  7:     7 Animal1 417320 396119 2017-01-04 06:33       0    1758    2772
    #  8:     8 Animal1 417770 396080 2017-01-04 17:01       0    2257    1898
    #  9:     9 Animal1 417232 396812 2017-01-05 08:43       0    2261    2360
    # 10:    10 Animal1 417716 396745 2017-01-05 16:43       0    3232    2943
    # 11:    11 Animal2 416571 396099 2017-01-01 06:24    2403       0    3013
    # 12:    12 Animal2 416423 395996 2017-01-01 16:15    1682       0    2355
    # 13:    13 Animal2 416184 395916 2017-01-02 08:28    2435       0    3849
    # 14:    14 Animal2 416002 395858 2017-01-02 15:34    3499       0    4492
    # 15:    15 Animal2 415454 395993 2017-01-03 07:14    2819       0    4321
    # 16:    16 Animal2 415450 397175 2017-01-03 17:27    2175       0    6205
    # 17:    17 Animal2 415781 396949 2017-01-04 06:12    1758       0    4316
    # 18:    18 Animal2 415702 396949 2017-01-04 17:23    2257       0    4007
    # 19:    19 Animal2 415017 397185 2017-01-05 08:12    2261       0    4617
    # 20:    20 Animal2 414516 396990 2017-01-05 16:18    3232       0    6139
    # 21:    21 Animal3 418971 394300 2017-01-01 05:59    1487    3013       0
    # 22:    22 Animal3 418275 394558 2017-01-01 16:45     675    2355       0
    # 23:    23 Animal3 419881 394940 2017-01-02 08:20    1474    3849       0
    # 24:    24 Animal3 420304 394669 2017-01-02 15:25    1134    4492       0
    # 25:    25 Animal3 419585 394825 2017-01-03 07:20    1885    4321       0
    # 26:    26 Animal3 421528 396153 2017-01-03 17:03    4159    6205       0
    # 27:    27 Animal3 420045 396510 2017-01-04 06:27    2772    4316       0
    # 28:    28 Animal3 419636 396349 2017-01-04 17:17    1898    4007       0
    # 29:    29 Animal3 419499 396212 2017-01-05 08:22    2360    4617       0
    # 30:    30 Animal3 420515 395898 2017-01-05 16:14    2943    6139       0
    #     rowid      ID  GPS.E  GPS.S       Date  Time Animal1 Animal2 Animal3