library(SeaDens) #updated
using arrows
This post is on how to use arrows in a plot.
This post is to create a plot that has arrows indicating the direction
In the example, we use sample data, but if you know where you event starts and ends you can skip these steps and go directly to the final section.
Data
Load data from package SeaDens
This data is from a simulated survey.
<-survey_4326 survey_data
Calculate gaps
Using the information from time, we will check when there were gaps and based on where a gap was, identify different events
Load tidyverse package to use some functions
library(tidyverse)
Check that your time data is in the correct format
$dt <- as.POSIXct(strptime(survey_data$timestamps, "%Y-%m-%d %H:%M:%S")) survey_data
This function uses the times to identify where there was a gap, assuming that the data is sorted
<-function(my_data=my_data){
calculate_gaps<-my_data$dt
time1<-lag(time1)
time2<-as.numeric(difftime(time1,time2, units="mins"))
time_dif$time_dif<-as.numeric(time_dif)
my_datareturn(my_data)
}
After running the function, a new data frame will be created which includes a column named time_dif
<-calculate_gaps(survey_data) survey_data_gaps
Here we will define, how many minutes should be considered a gap
<-survey_data_gaps %>%
survey_data_gapsmutate(gap_event = case_when(is.na(time_dif) ~ 'N',
>= 2 ~ 'Y',
time_dif TRUE ~ 'N'))
Identify events
To add a number to each event in order to be able to identify them separately, we use the following function
<-function(my_data=my_data){
identify_events<-nrow(my_data)
num_seq<-as.numeric(num_seq)
num_seq$num_seq<-as.numeric(paste(seq(1:num_seq)))
my_data<-subset(my_data,my_data$gap_event != "Y")
subset_data$num_seq<-as.integer(subset_data$num_seq)
subset_data$event_number<-(cumsum(c(1L, diff(subset_data$num_seq)) != 1L))
subset_data$event_number<-subset_data$event_number+1
subset_data$event_number<-stringr::str_pad(subset_data$event_number, 3, pad = "0")
subset_data$event_number<-paste0("event_",subset_data$event_number)
subset_data<-subset_data%>%select(num_seq,event_number)
subset_data<-full_join(my_data,subset_data,by='num_seq')
my_data_eventsreturn(my_data_events)
}
The function will return a data frame with a new column called event_number
<-identify_events(my_data=survey_data_gaps) survey_data_events
Start and time of the events
Using the classification of the events, we will extract the first and the last location per event, which would become the start and end of the arrow on the plot
<-survey_data_events %>%
survey_time_eventsgroup_by(event_number)%>%
summarise(first_lat=first(latitude),
last_lat=last(latitude),
first_lon=first(longitude),
last_lon=last(longitude))%>%
drop_na()
Plot
To plot we use the function geom_segment and the additional argument arrow
ggplot(survey_time_events,
aes(x = first_lon, y = first_lat)) +
geom_segment(aes(xend = last_lon, yend = last_lat), arrow = arrow())
Re-scale
If you were need more frequent arrows, you can re-scale the events
Here I create a for loop to re-scale separately per event
<-function(my_data=my_data,each_num=each_num){
rescale_events<-split(my_data,my_data$event_number)
events_list<-list()
new_events_listfor( i in seq_along(events_list)){
<-events_list[[i]]
events_df<-rep(1:100, each=each_num)
rep_secuence<-rep_secuence[1:nrow(events_df)]
replicate_numbers$rep_number<-replicate_numbers
events_df$event_number2<-paste0(events_df$event_number,'-',events_df$rep_number)
events_df<-events_df
new_events_list[[i]]
}<- do.call("rbind",new_events_list)
new_events_dfreturn(new_events_df)
}
We define how often do we want the arrow to occur, here I selected 40, which represents the number of locations
<-rescale_events(my_data=survey_data_events,each_num=40) survey_rescale
Group by event
Use the function group_by and summarise to identify the start and end of the events, now with the rescaling there would be more events
<-survey_rescale %>%
survey_rescale_arrowsgroup_by(event_number2)%>%
summarise(first_lat=first(latitude),
last_lat=last(latitude),
first_lon=first(longitude),
last_lon=last(longitude))%>%
drop_na()
Similarly to above we use the function geom_segment and the argument arrow
ggplot(survey_rescale_arrows,
aes(x = first_lon, y = first_lat)) +
geom_segment(aes(xend = last_lon, yend = last_lat),
arrow = arrow())
In a map
Finally, to see how it would look in a map we will include a base map from Germany.
Load the shapefiles and the package to plot
library(GermanNorthSea)
library(sf)
Transform to the corresponding CRS
<-st_transform(GermanNorthSea::German_land, 4326)
German_land<-st_transform(GermanNorthSea::German_EEZ, 4326)
German_EEZ<-st_transform(GermanNorthSea::German_coast, 4326) German_coast
Add the corresponding arguments and voilà
ggplot() +
geom_sf(data = German_EEZ, colour = 'black')+
geom_sf(data = German_land, colour = 'black', fill = 'grey')+
coord_sf(xlim = c(3, 9),ylim = c(53, 56))+
theme_bw()+
xlab('Longitude')+ylab('Latitude')+
geom_segment(data=survey_rescale_arrows,aes(x = first_lon, y = first_lat,xend = last_lon, yend = last_lat),
arrow = arrow(length=unit(0.10,"cm"), type = "closed"),
color='#c1121f')
Further reading
To change the shape, size and form of the arrow visit geom_segment