In a previous post, I discussed how to plot occurrence data from GBIF on a map. In this post, I will discuss how to plot a bird migration by producing an occurrence map for each month of the year. I will use the migration of the stork (Ciconia ciconia) as an example.

These are the libraries I will use for drawing the maps using OpenStreetMap data:
library(OpenStreetMap)
library(ggplot2)
library(sp)
library (rgdal)
library(readr)
Storks migrate from Europe where most of them breed, to southern Africa and India. The area of the map is therefore delimited by these coordinates:
LAT1 = 70 ; LAT2 = -50
LON1 = -20 ; LON2 = 90
Get the map from OpenstreetMap. A more detailed explanation of the parameters can be found here.
map <- openmap(
c(LAT2,LON1), c(LAT1,LON2),
zoom = 2,
type = "esri-topo",
mergeTiles = TRUE)
print("done loading map")
The map can now be drawn. Here, I used a longitude / latitude projection for simplicity. If you were to plot a distribution instead of occurrences, an equal area projection would be a better pick. Projection definitions can be found on the EPGS website.
# the definition of the projection proj <- "+proj=longlat +ellps=clrk66" # project the map map.proj <- openproj(map, projection = proj) # plot the map autoplot(map.proj) + theme_classic() + labs(x="longitude", y="latitude")
Get occurrence data from GBIF, as discussed here. Save the data, without unpacking it, to file. I stored it in a file called “data/GBIF_Ciconia_ciconia.zip”. The occurrence data can now be loaded into a variable. Note that it is not necessary to unzip the file before reading it, and that the data is in tab-separated format (TSV).
The data can now be loaded and cleaned. Optionally, as the data set has 600.000 entries, I chose to sub-sample it to speed things up.
# load the file into a variable
occurrences <- read_tsv("data/GBIF_Ciconia_ciconia.zip")
# Cleanup the data
occurrences.clean <- occurrences[
!is.na(occurrences$decimalLatitude) &
!is.na(occurrences$decimalLongitude) &
!is.na(occurrences$month),]
# randomly sample 25.000 entries
occurrences.sampled <-
occurrences.clean[sample(nrow(occurrences.clean), 25000),]
Now project the data using the same projection definition as for the map
coordinates(occurrences.sampled) <-
c("decimalLongitude", "decimalLatitude")
proj4string(occurrences.sampled) <- CRS(proj)
occurrences.proj <-
spTransform(occurrences.sampled, CRS=CRS(proj))
The map and the data can now be plotted, using facets to create a map for each month of the year
# return the name of the month
month_labeller <- function(variable,value){
return(month.name[value])
}
# plot the map and the data
autoplot(map.proj) +
geom_count(
data = data.frame(occurrences.proj),
aes(x = decimalLongitude, y = decimalLatitude,
color = ..prop..
), alpha=0.1, size=0.5, show.legend = FALSE
) +
scale_fill_gradient(low="red", high="yellow") +
facet_wrap(~month, labeller = month_labeller) +
theme_classic() +
labs(x="Longitude", y="Latitude")
References
GBIF.org (07 September 2020) GBIF Occurrence Download https://doi.org/10.15468/dl.vm74xx