Over the last several months I’ve been building a Shiny app for exploratory analysis and visualization of animal movement data. I want the app to be fully interactive so I decided to use leaflet over other mapping packages. Creating this application is also a good opportunity to write some wrapper functions to make analysis in an interactive session easier for the people that I work with.
The data comes from animals tagged with a GPS collar. The collar works like any other GPS device, it connects to GPS satellites and records locations at intermittent time intervals. Depending on the species the intervals are either 12, 7, 8 or 1 hours. The data that we get are xy timestamped data with a few additional fields specifying the accuracy of the location. The data is downloaded from the manufacturers servers and stored in our own location database.
A lot of the data wrangling occurs prior to import to the database. The little bit of data cleaning that occurs in the application is converting the timestamp from a character to class
POSIXct and adding UTM coordinates to the dataframe.
- You can use any of the
spclasses to create the map.
- If there are NA values in the dataframe, leaflet will stop plotting.
The first function really only useful for the displaying a map of the data on the front page of the application. The function takes a dataframe as input loops through each unique animal ID (ndowid) to plot each animal’s locations and returns a leaflet map.
In my application the data is read using the
data.table package. The
as.data.table function is used to ensure that the data is in the proper format. As stated above, I remove all NA from the dataframe using the
complete.cases function call.
The procedure on line 4 uses the
data.table syntax to subset the data.table and return every 20 records. I do this to simplify the polylines on the leaflet map. Some of these animals have over 10,000 GPS locations, trying to plot all those makes for a messy map. I use the same method to plot only the 1st and last GPS location as circles on line 9. The resulting map looks like this.
The second function
DeviceMapping is similar to the first function, and honestly, should be combined with the first function. I’ve just been to lazy to do that at this point. The major difference is that this function plots every point and gives each animal a unique color. The function also creates a layer control box that allows user to click layers on and off.
The output of this function looks like this.
The last function is used to add polygon layers to the DeviceMap from above. The web application estimates several homeranges for each animal. The home range polygons are saved as a GeoJSON and displayed on the map with the
addGeoJSON function of leaflet. Like the above functions, it loops through all the unique animals in the GeoJSON and maps them to the correct color. Creating the GeoJSON is a whole other process I’ll go over some other time.
The result of the DeviceMapping functions produce the following map. The
DeviceMapping function plots the points and the
DeviceMapping_geojson functions maps the polygons.
All these functions are used in the web application. They’ve also proven useful for everyday use in the office. To generalize or modify them for your specific use case, the major things that need to be changed are the name of the ID field. Our animals ID is called ndowid. Other places are the values used for the labels in the popups when the circle markers are clicked.