Animated geolocator and tracking data using ggplot in R

Eastern Curlew

fade trax~1

As part of my work I’ve been trying to animate shorebird migration routes. I’m using Far Eastern Curlew (Numenius madagascariensis) as a case study. Not only because there is a lot of data, but also because they are becoming of increasing conservation concern, particularly in Moreton Bay, Australia, where  20% of the world population spends the non-breeding season, and is undergoing substantial declines. The threatened species commissioner has just put forward a proposal to have them listed as threatened, so watch this space. Last, but not least, curlews have their own song/irish jig.

The data

I use tracking data from this paper by Driscoll and Ueta The migration route and behaviour of Eastern Curlews Numenius madagascariensis for my maps.

IBI_081_f1

Here’s the data to load into R done by creating a matrix and populating it:

#Load data
speciestrax<-matrix(0,55,5)
colnames(speciestrax)<-c("BirdID","slong","slat","elong","elat")
speciestrax<-data.frame(speciestrax)
speciestrax[,1]<-c("ID16091","ID16091","ID16091","ID16091","ID16091","ID16091","ID16091","ID16091","ID16091","ID16091","ID16091","ID16090","ID16090","ID16090","ID16090","ID12207","ID12207","ID12207","ID12207","ID12207","ID04548","ID04548","ID04548","ID04548","ID04548","ID04548","ID10024","ID10024","ID10024","ID10024","ID10024","ID10024","ID10024","ID10024","ID16089","ID16089","ID16089","ID16089","ID16089","ID16089","ID29979","ID29979","ID29979","ID29979","ID10280","ID10280","ID10280","ID10280","ID10280","ID10280","ID09916","ID09916","ID09916","ID09916","ID09916")
speciestrax[,2]<-c(153.1590,134.4637,120.7381,119.6376,120.2324,130.1927,130.2522,124.3630,121.7456,120.6749,141.5546,153.1590,141.5546,134.4637,120.2919,153.1590,150.9144,146.9597,127.6623,134.5786,153.1590,145.7811,120.8598,120.3530,119.4488,128.7977,153.1590,143.7280,120.9447,119.3386,124.0975,138.0590,127.3514,125.4954,153.3637,143.9410,147.0105,148.1200,153.2209,150.7225,153.3637,150.7225,143.8696,147.3674,153.3637,150.5797,140.5860,138.3731,141.8976,150.5797,146.2092,140.6413,144.2105,152.9193,151.9199)
speciestrax[,3]<-c(-27.178338,7.485653,18.468052,26.189403,36.111755,46.200918,49.591650,46.914750,40.906617,33.232855,-2.815979,-27.178338,-2.815979,7.485653,23.912308,-27.178338,-22.587119,-2.135652,26.334599,33.996464,-27.178338,16.972186,22.081908,24.014030,35.335506,45.700438,-27.178338,-7.505500,22.267506,25.509521,39.839825,51.808514,39.673263,39.673263,-27.338308,-14.060915,-2.068432,7.631699,-4.324161,-10.391786,-27.338308,-10.391786,-14.032362,-19.314765,-27.338308,-22.469930,-8.692851,-5.766114,-12.707200,-22.469930,-38.797990,-8.531246,-13.813650,-25.520598,-32.730365)
speciestrax[,4]<-c(134.4637,120.7381,119.6376,120.2324,130.1927,130.2522,124.3630,121.7456,120.6749,141.5546,143.9936,145.7811,134.4637,120.2919,121.7708,150.9144,146.9597,127.6623,134.5786,136.2918,145.7811,120.8598,120.3530,119.4488,128.7977,132.4383,143.7280,120.9447,119.3386,124.0975,138.0590,127.3514,125.4954,133.4904,143.9410,147.0105,148.1200,153.2209,150.7225,153.3637,150.7225,143.8696,147.3674,153.3637,150.5797,140.5860,138.3731,141.8976,150.5797,153.3637,140.6413,144.2105,152.9193,151.9199,146.2806)
speciestrax[,5]<-c(7.485653,18.468052,26.189403,36.111755,46.200918,49.591650,46.914750,40.906617,33.232855,-2.815979,-13.939959,-16.972186, 7.485653,23.912308,40.915936,-22.587119,-2.135652,26.334599,33.996464,36.471103,-16.972186,22.081908,24.014030,35.335506,45.700438,45.272135,-7.505500,22.267506,25.509521,39.839825,51.808514,39.673263,39.673263,-3.870873,-14.060915,-2.068432,7.631699,-4.324161,-10.391786,-27.338308,-10.391786,-14.032362,-19.314765,-27.338308,-22.469930,-8.692851,-5.766114,-12.707200,-22.469930,-27.338308,-8.531246,-13.813650,-25.520598,-32.730365,-38.583839)
speciestrax[,2:5]<-as.numeric(speciestrax[,2:5])

Far-eastern-curlew-in-flight

Pre-requisites for running the code

Once the data has been loaded, it is important to load the necessary packages into R. Because the function fortify in the package sp no longer works, this code must first be loaded as a bug fix.

###load libraries
library(maps)
library(geosphere)
library(plyr)
library(ggplot2)
library(sp)

Creating a Pacific-centered map

I used some code from here: Great circles on a recentered worldmap in ggplot to re-center the world map over the pacific. I’ve changed some of the coulours. Please not that if you don’t use this code you get this problem.

### Function to regroup split lines and polygons
# takes dataframe, column with long and unique group variable,
#returns df with #added column named group.regroup
RegroupElements <- function(df, longcol, idcol){
g <- rep(1, length(df[,longcol]))
if (diff(range(df[,longcol])) > 300) {
d <- df[,longcol] > mean(range(df[,longcol]))
g[!d] <- 1
g[d] <- 2
}
g <- paste(df[, idcol], g, sep=".")
df$group.regroup <- g
df
}

### Function to close regrouped polygons
# takes dataframe, checks if 1st and last longitude value are the same,
#if not, inserts first as last and reassigns order variable
ClosePolygons <- function(df, longcol, ordercol){
if (df[1,longcol] != df[nrow(df),longcol]) {
tmp <- df[1,]
df <- rbind(df,tmp)
}
o <- c(1: nrow(df)) # rassign the order variable
df[,ordercol] <- o
df
}

PEK_Flights

Adding tracks to map in ggplot2

To add the bird tracks to the map, I create 2 loops for the code. The first loop generates the first four bird tracks. The second loop allows the tracks to sequentially fade after the fourth track has been drawn.

center=160 #describe where you want the centre of the map to be

### loop 1
for (i in 1:4) {
rts <- gcIntermediate(speciestrax[1:i,2:3],speciestrax[1:i,4:5], 100, breakAtDateLine=FALSE, addStartEnd=TRUE, sp=TRUE)
rts.ff <- fortify.SpatialLinesDataFrame(rts)
trax.ll<-speciestrax[1:i,]
trax.ll$id <-as.character(c(1:nrow(speciestrax[1:i,]))) # that rts.ff$id is a char
gcircles <- merge(rts.ff, trax.ll, all.x=T, by="id") # join attributes, we keep them all, just in case

# shift coordinates to recenter great circles
gcircles$long.recenter <- ifelse(gcircles$long < center - 180 , gcircles$long + 360, gcircles$long)

# shift coordinates to recenter worldmap
worldmap <- map_data ("world")
worldmap$long.recenter <- ifelse(worldmap$long < center - 180 , worldmap$long + 360, worldmap$long)

# now regroup
gcircles.rg <- ddply(gcircles, .(id), RegroupElements, "long.recenter", "id")
worldmap.rg <- ddply(worldmap, .(group), RegroupElements, "long.recenter", "group")

# close polys
worldmap.cp <- ddply(worldmap.rg, .(group.regroup), ClosePolygons, "long.recenter", "order") # use the new grouping var

# plot
ggplot() +
geom_polygon(aes(long.recenter,lat,group=group.regroup), size = 0.2, fill="#f9f9f9", colour = "#191919", data=worldmap.cp) +
geom_line(aes(long.recenter,lat,group=group.regroup,color=100), size=1.8, data= gcircles.rg) + # set transparency here
scale_colour_gradient(low="#fafafa", high="#EE0000") + # set color gradient here
theme(panel.background = element_rect(fill = "black"), panel.grid.minor = element_blank(), panel.grid.major = element_blank(), axis.ticks = element_blank(), axis.title.x = element_blank(), axis.title.y = element_blank(), axis.text.x = element_blank(), axis.text.y = element_blank(), legend.position = "none") +
ylim(-60, 90) +
coord_equal()
ggsave(file=paste("eastern_curlew_mov_fade",i, ".png", sep=""),height=4)
}

Far-eastern-curlew-flock-in-flight

 ### loop 2
for (i in 5:length(speciestrax$X)) {
rts <- gcIntermediate(speciestrax[(i-3):i,2:3],speciestrax[(i-3):i,4:5], 100, breakAtDateLine=FALSE, addStartEnd=TRUE, sp=TRUE)
rts.ff <- fortify.SpatialLinesDataFrame(rts)
trax.ll<-speciestrax[(i-3):i,]
trax.ll$id <-as.character(c(1:nrow(speciestrax[(i-3):i,]))) # that rts.ff$id is a char
gcircles <- merge(rts.ff, trax.ll, all.x=T, by="id") # join attributes, we keep them all, just in case

# shift coordinates to recenter great circles
gcircles$long.recenter <- ifelse(gcircles$long < center - 180 , gcircles$long + 360, gcircles$long)

# shift coordinates to recenter worldmap
worldmap <- map_data ("world")
worldmap$long.recenter <- ifelse(worldmap$long < center - 180 , worldmap$long + 360, worldmap$long)

# now regroup
gcircles.rg <- ddply(gcircles, .(id), RegroupElements, "long.recenter", "id")
worldmap.rg <- ddply(worldmap, .(group), RegroupElements, "long.recenter", "group")

# close polys
worldmap.cp <- ddply(worldmap.rg, .(group.regroup), ClosePolygons, "long.recenter", "order") # use the new grouping var

# plot
ggplot() +
geom_polygon(aes(long.recenter,lat,group=group.regroup), size = 0.2, fill="#f9f9f9", colour = "#191919", data=worldmap.cp) +
geom_line(aes(long.recenter,lat,group=group.regroup,color=100), size=1.8, data= gcircles.rg) + # set transparency here
scale_colour_gradient(low="#fafafa", high="#EE0000") + # set color gradient here
theme(panel.background = element_rect(fill = "black"), panel.grid.minor = element_blank(), panel.grid.major = element_blank(), axis.ticks = element_blank(), axis.title.x = element_blank(), axis.title.y = element_blank(), axis.text.x = element_blank(), axis.text.y = element_blank(), legend.position = "none") +
ylim(-60, 90) +
coord_equal()
#save
ggsave(file=paste("eastern_curlew_mov_fade",i, ".png", sep=""),height=4)
}

Creating the animation

I used windows movie maker to collate all the png files created into a short film. To do so, I simply dragged all the png files in and saved it as an mpg4 file under save movie>for computer.

fade trax~1

I then used format factory to transform the video into a gif file. You can do this by opening format factory>videos>gif>output settings. You can then change your video size to standard or HD. I also changed FPS to 15. Then clicked on ok. Add file. Choose file. ok. And then clicked on the start button. Voila!

The R package animation can also do this via VisualMagick, but it’s a pain to install.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s