Identify trips with large gaps and classify the trip as complete or incomplete.
When tracking animals, there may be times when they enter areas with no reception.
If a large part of a trip takes place in such areas, you should consider whether to include this data in your calculations.
For instance, long gaps could lead to an underestimation of the actual distance traveled by the animal.
In this post, I’ll walk you through the steps I used to determine whether trips are complete or incomplete.
This post has three parts:
- Create a gap
- Calculate gaps
- Identify if our trips are complete or incomplete
For the exercises, test data is from masked boobies.
To access the data you have to install the package sula: devtools::install_github(“MiriamLL/sula”)
library(sula)
Use the function select from tidyverse to keep the columns ID, latitude, longitude, date, time, and trip number.
In this part we would eliminate some locations to create an artificial gap
Select a location with “low reception” and remove all the locations to create the gaps
This_area<-data.frame(Longitude=-109.4,Latitude=-27.6)
create_buffer<-function(central_point=central_point, buffer_km=buffer_km){
central_spatial<- sp::SpatialPoints(cbind(central_point$Longitude,central_point$Latitude))
sp::proj4string(central_spatial)= sp::CRS("+init=epsg:4326")
central_spatial <- sp::spTransform(central_spatial, sp::CRS("+init=epsg:4326"))
central_spatial<-sf::st_as_sf(central_spatial)
buffer_dist<-buffer_km*1000
central_buffer<-sf::st_buffer(central_spatial, buffer_dist)
return(central_buffer)
}
This_buffer<-create_buffer(central_point=This_area,buffer_km=20)
Data_2spatial <- Data_1original
sp::coordinates(Data_2spatial) <- ~Longitude + Latitude
sp::proj4string(Data_2spatial) = sp::CRS("+init=epsg:4326")
Data_2spatial <-sf::st_as_sf(Data_2spatial)
Data_3creategap<-sapply(sf::st_intersects(Data_2spatial ,This_buffer), function(z) if (length(z)==0) NA_integer_ else z[1])
Data_1original$trip <- as.numeric(Data_3creategap)
Data_4withgap <- Data_1original %>%
filter(is.na(trip)==TRUE)
Plot the area with low reception
Plot_locations<-ggplot()+
geom_point(data = Data_4withgap,aes(x=Longitude,y = Latitude),color="#6a994e",size=0.5)+
scale_x_continuous(labels = function(x) paste0(-x, '\u00B0')) +
scale_y_continuous(labels = function(x) paste0(-x, '\u00B0')) +
xlab('Longitude')+ylab('Latitude')+
theme(
panel.background = element_rect(fill = '#edf2f4'),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),legend.position='none',
panel.border = element_rect(colour = "black", fill=NA, size=1.5))+
geom_sf(data=This_buffer,colour='blue', fill='transparent', linetype='dashed')+
geom_text(data=This_area, aes(x=Longitude,y=Latitude,label="No reception"),hjust=+0.46, vjust=0, size=3)
Plot_locations
A column in the format POSIXct is needed for this example.
Data_4withgap <- Data_4withgap %>%
mutate(dt= as.POSIXct(strptime(paste0(DateGMT," ",TimeGMT), format="%d/%m/%Y %H:%M:%S")))
Checking gaps in time on one individual trip.
This trip has a large gap.
range(Data_5oneindividual$time_dif_mins,na.rm=TRUE)
[1] 4.07 57.70
Create a loop to calculate gaps separated by individuals and trips.
calculate_gaps<-function (this_data = this_data,
column_datetime = column_datetime,
column_separator = column_separator){
# to standardize the column names
this_data$column_separator<- this_data[[column_separator]]
this_data$column_datetime <- this_data[[column_datetime]]
trips_list <- split(this_data, this_data$column_separator)
gaps_list <- list()
for (i in seq_along(trips_list)) {
trip_df <- trips_list[[i]]
trip_df<-trip_df %>%
mutate(times_lag=lag(dt))%>%
mutate(time_dif=as.numeric(difftime(dt, times_lag, units = "mins")))%>%
mutate(time_dif_mins = round(time_dif,2))
gaps_list[[i]] <- trip_df}
gaps_df <- do.call("rbind", gaps_list)
return(gaps_df)
}
For this, would help to have an unique ID, which helps separate the individual and their trips.
To run the function, provide the data frame, the information for separating, and the name of the column where the date and time are provided.
Data_6uniqueIds<-calculate_gaps(this_data=Data_5uniqueIds,
column_separator='unique_id',
column_datetime='dt')
By using the function summarise, we can see which trips have large gaps.
Based on the gaps during the trips, the trips could be classified as complete or incomplete.
For example, here an incomplete trip would be any trip that had a gap of more than 30 minutes.
Use this table to identify those trips that were incomplete.
Use the information above, to add the classification on the tracking locations.
Plot incomplete trips.
Plot_trips<-ggplot()+
geom_point(data = Data_8gaps,aes(x=Longitude,y = Latitude,color=trip_class),size=0.5)+
scale_x_continuous(labels = function(x) paste0(-x, '\u00B0')) +
scale_y_continuous(labels = function(x) paste0(-x, '\u00B0')) +
xlab('Longitude')+ylab('Latitude')+
theme(
panel.background = element_rect(fill = '#edf2f4'),
panel.grid.major = element_blank(),
panel.grid.minor = element_blank(),legend.position='none',
panel.border = element_rect(colour = "black", fill=NA, size=1.5)
)+
geom_sf(data=This_buffer,colour='blue', fill='transparent', linetype='dashed')+
geom_text(data=This_area, aes(x=Longitude,y=Latitude,label="No reception"),hjust=+0.46, vjust=0, size=3)+
theme(
legend.background = element_rect(colour = "transparent", fill = "transparent"),
legend.position = c(0.22,0.15))+
scale_colour_manual(name='trip',values = c("#6a994e", "#fca311"))+
guides(color = guide_legend(override.aes = list(size = 2)))
Plot_trips
Another way to check for gaps, using only time in this post.