6 Exploring data with the Motus R package

Once you have clarified any possible ambiguous tags, and removed false positives, you are ready to start analyzing your clean data set. This chapter will walk you through some simple procedures to start working with and visualizing the clean sample data set; you can modify these scripts to work with your own data. For a more in-depth R tutorial we strongly recommend working through R for Data Science by Garrett Grolemund and Hadley Wickham (http://r4ds.had.co.nz/).

6.1 Load required packages

Follow the instructions in Chapter 2 to install the following packages before loading, if you haven’t already done so.

library(motus)
library(tidyverse)
library(ggmap)
Sys.setenv(TZ = "GMT")

6.2 Load data

If you followed along with the the previous Chapter (Chapter 5) and are working with the cleaned ‘df.alltags.sub’ file, you can skip this step and move to section 6.3.

Otherwise, if you saved your data as an RDS file, you can load it using:

df.alltags.sub <- readRDS("./data/dfAlltagsSub.rds")  # change dir to local directory

Or, if you’ve applied a custom filter to your .motus file, you can load the previously downloaded sample .motus data (see Chapter 3) and clean it now. Currently the main benefit of using the custom filter is that you apply the filter to the .motus file, which allows you more flexibility in applying dplyr functions to manage and filter the data (e.g., you can select different variables to include in the data than we included in the RDS file in Chapter 5). This approach also allows you to more readily integrate new data added to your database with the tagme function. Because we are selecting the same variables and filtering the same records, the following gives you the same dataset as the readRDS statement above:

# load the .motus file
proj.num = 176
sql.motus <- tagme(proj.num, update = TRUE, dir = "./data/")
tbl.alltags <- tbl(sql.motus, "alltags")

# obtain a table object of the filter
tbl.filter = getRunsFilters(sql.motus, "filtAmbigFalsePos")

# filter and convert the table into a dataframe, with a few modications
df.alltags.sub <- left_join(tbl.alltags, tbl.filter, by = c("runID", "motusTagID")) %>%
  mutate(probability = ifelse(is.na(probability), 1, probability),
         recvLat = if_else((is.na(gpsLat)|gpsLat == 0), 
                           recvDeployLat, 
                           gpsLat),
         recvLon = if_else((is.na(gpsLon)|gpsLon == 0), 
                           recvDeployLon, 
                           gpsLon),
         recvAlt = if_else(is.na(gpsAlt), 
                           recvDeployAlt, 
                           gpsAlt)) %>%
  filter(probability > 0) %>%
  select(-noise, -slop, -burstSlop, -done, -bootnum, -codeSet, 
         -mfg, -nomFreq,-markerNumber, -markerType, -tagDeployComments, 
         -fullID, -deviceID,-recvDeployLat, -recvDeployLon, -recvDeployAlt, 
         -speciesGroup, -gpsLat,-gpsLon, - recvAlt, - recvSiteName) %>%
  collect() %>%
  as.data.frame() %>%
  mutate(ts = as_datetime(ts),  # work with dates AFTER transforming to flat file
         tagDeployStart = as_datetime(tagDeployStart),
         tagDeployEnd = as_datetime(tagDeployEnd))

Note that if your project is very large, you may want to convert only a portion of it to the dataframe, to avoid memory issues. Details on filtering the tbl prior to collecting as a dataframe are available in section ??.

Here we do so by adding a filter to the above command, in this case, only creating a dataframe for motusTagID 16047, but you can decide how to best subset your data based on your need (e.g. by species or year):

# create a subset for a single tag, to keep the
# dataframe small
df.alltags.16047 <- df.alltags.sub %>% filter(motusTagID == 
    16047)

6.3 Summarizing your data

Here we will run through some basic commands, starting with the summary() function to view a selection of variables in a data frame:

sql.motus %>% tbl("alltags") %>% select(ts, motusTagID, 
    runLen, speciesEN, tagDeployLat, tagDeployLon, 
    recvDeployLat, recvDeployLon) %>% collect() %>% 
    summary()
##        ts              motusTagID        runLen        speciesEN        
##  Min.   :1.438e+09   Min.   :10811   Min.   :   2.0   Length:108826     
##  1st Qu.:1.476e+09   1st Qu.:22897   1st Qu.:  30.0   Class :character  
##  Median :1.477e+09   Median :22905   Median : 122.0   Mode  :character  
##  Mean   :1.476e+09   Mean   :22660   Mean   : 355.9                     
##  3rd Qu.:1.477e+09   3rd Qu.:23316   3rd Qu.: 404.0                     
##  Max.   :1.498e+09   Max.   :24303   Max.   :2474.0                     
##                                                                         
##   tagDeployLat    tagDeployLon    recvDeployLat    recvDeployLon    
##  Min.   :11.12   Min.   :-80.69   Min.   :-42.50   Min.   :-143.68  
##  1st Qu.:50.19   1st Qu.:-63.75   1st Qu.: 50.20   1st Qu.: -63.75  
##  Median :50.19   Median :-63.75   Median : 50.20   Median : -63.75  
##  Mean   :50.14   Mean   :-65.77   Mean   : 49.05   Mean   : -65.64  
##  3rd Qu.:50.19   3rd Qu.:-63.75   3rd Qu.: 50.20   3rd Qu.: -63.75  
##  Max.   :51.80   Max.   :-63.75   Max.   : 62.89   Max.   : -60.02  
##  NA's   :2025    NA's   :2025     NA's   :173      NA's   :173
# same summary for the filtered sql data
df.alltags.sub %>% select(ts, motusTagID, runLen, speciesEN, 
    tagDeployLat, tagDeployLon, recvLat, recvLon) %>% 
    summary()
##        ts                        motusTagID        runLen      
##  Min.   :2015-08-03 06:37:11   Min.   :16011   Min.   :   3.0  
##  1st Qu.:2016-10-06 10:30:26   1st Qu.:22897   1st Qu.:  25.0  
##  Median :2016-10-09 21:49:41   Median :22897   Median :  92.0  
##  Mean   :2016-09-04 20:32:07   Mean   :22247   Mean   : 230.7  
##  3rd Qu.:2016-10-19 10:41:54   3rd Qu.:22897   3rd Qu.: 286.0  
##  Max.   :2017-04-20 22:33:19   Max.   :23316   Max.   :1371.0  
##                                                                
##   speciesEN          tagDeployLat    tagDeployLon       recvLat      
##  Length:49076       Min.   :50.19   Min.   :-80.69   Min.   :-42.50  
##  Class :character   1st Qu.:50.19   1st Qu.:-63.75   1st Qu.: 50.20  
##  Mode  :character   Median :50.19   Median :-63.75   Median : 50.20  
##                     Mean   :50.34   Mean   :-65.63   Mean   : 49.97  
##                     3rd Qu.:50.19   3rd Qu.:-63.75   3rd Qu.: 50.20  
##                     Max.   :51.80   Max.   :-63.75   Max.   : 51.82  
##                                                      NA's   :167     
##     recvLon      
##  Min.   :-80.69  
##  1st Qu.:-63.75  
##  Median :-63.75  
##  Mean   :-65.32  
##  3rd Qu.:-63.75  
##  Max.   :-62.99  
##  NA's   :167

The dplyr package allows you to easily summarize data by group, manipulate variables, or create new variables based on your data.

We can manipulate existing variables or create new ones with dplyr’s mutate function, here we’ll convert ts to a POSIXct format, then make a new variable for year and day of year (doy).

We’ll also remove the set of points with missing receiver latitude and longitudes. These may be useful in some contexts (for example if the approximate location of the receiver is known) but can cause warnings or errors when plotting.

df.alltags.sub <- df.alltags.sub %>%
  mutate(ts = as_datetime(ts, tz = "UTC"), # convert ts to POSIXct format
         year = year(ts), # extract year from ts
         doy = yday(ts)) %>% # extract numeric day of year from ts
  filter(!is.na(recvLat))
head(df.alltags.sub)
##    hitID runID batchID                  ts sig sigsd freq freqsd
## 1  45107  8886      53 2015-10-26 11:19:49  52     0    4      0
## 2  45108  8886      53 2015-10-26 11:20:28  54     0    4      0
## 3  45109  8886      53 2015-10-26 11:21:17  55     0    4      0
## 4  45110  8886      53 2015-10-26 11:21:55  52     0    4      0
## 5  45111  8886      53 2015-10-26 11:22:44  49     0    4      0
## 6 199885 23305      64 2015-10-26 11:12:04  33     0    4      0
##   motusTagID ambigID port runLen tagProjID mfgID tagType tagModel
## 1      16047      NA    3      5       176   378      ID NTQB-3-2
## 2      16047      NA    3      5       176   378      ID NTQB-3-2
## 3      16047      NA    3      5       176   378      ID NTQB-3-2
## 4      16047      NA    3      5       176   378      ID NTQB-3-2
## 5      16047      NA    3      5       176   378      ID NTQB-3-2
## 6      16047      NA    1     11       176   378      ID NTQB-3-2
##   tagLifespan  tagBI pulseLen tagDeployID speciesID      tagDeployStart
## 1          NA 9.6971      2.5        1839      4670 2015-09-10 18:00:00
## 2          NA 9.6971      2.5        1839      4670 2015-09-10 18:00:00
## 3          NA 9.6971      2.5        1839      4670 2015-09-10 18:00:00
## 4          NA 9.6971      2.5        1839      4670 2015-09-10 18:00:00
## 5          NA 9.6971      2.5        1839      4670 2015-09-10 18:00:00
## 6          NA 9.6971      2.5        1839      4670 2015-09-10 18:00:00
##          tagDeployEnd tagDeployLat tagDeployLon tagDeployAlt recvDeployID
## 1 2016-03-10 18:00:00      51.4839       -80.45           NA         2510
## 2 2016-03-10 18:00:00      51.4839       -80.45           NA         2510
## 3 2016-03-10 18:00:00      51.4839       -80.45           NA         2510
## 4 2016-03-10 18:00:00      51.4839       -80.45           NA         2510
## 5 2016-03-10 18:00:00      51.4839       -80.45           NA         2510
## 6 2016-03-10 18:00:00      51.4839       -80.45           NA         2512
##        recv recvDeployName isRecvMobile recvProjID antType antBearing
## 1 Lotek-159      Shelburne            0         74  yagi-9        127
## 2 Lotek-159      Shelburne            0         74  yagi-9        127
## 3 Lotek-159      Shelburne            0         74  yagi-9        127
## 4 Lotek-159      Shelburne            0         74  yagi-9        127
## 5 Lotek-159      Shelburne            0         74  yagi-9        127
## 6 Lotek-164  BennettMeadow            0         74  yagi-9        243
##   antHeight speciesEN          speciesFR       speciesSci tagProjName
## 1        NA  Red Knot Bécasseau maubèche Calidris canutus  SampleData
## 2        NA  Red Knot Bécasseau maubèche Calidris canutus  SampleData
## 3        NA  Red Knot Bécasseau maubèche Calidris canutus  SampleData
## 4        NA  Red Knot Bécasseau maubèche Calidris canutus  SampleData
## 5        NA  Red Knot Bécasseau maubèche Calidris canutus  SampleData
## 6        NA  Red Knot Bécasseau maubèche Calidris canutus  SampleData
##   recvProjName gpsAlt filterID probability  recvLat   recvLon year doy
## 1         <NA>     NA       NA           1 42.60699 -72.71657 2015 299
## 2         <NA>     NA       NA           1 42.60699 -72.71657 2015 299
## 3         <NA>     NA       NA           1 42.60699 -72.71657 2015 299
## 4         <NA>     NA       NA           1 42.60699 -72.71657 2015 299
## 5         <NA>     NA       NA           1 42.60699 -72.71657 2015 299
## 6         <NA>     NA       NA           1 42.68067 -72.47392 2015 299

We can also summarize information by group, in this case motusTagID, and apply various functions to these groups such as getting the total number of detections (n) for each tag, the number of receivers each tag was detected on, the first and last detection date, and the total number of days there was at least one detection:

tagSummary <- df.alltags.sub %>% group_by(motusTagID) %>% 
    summarize(nDet = n(), nRecv = length(unique(recvDeployName)), 
        tsMin = min(ts), tsMax = max(ts), totDay = length(unique(doy)))

head(tagSummary)
## # A tibble: 6 x 6
##   motusTagID  nDet nRecv tsMin               tsMax               totDay
##        <int> <int> <int> <dttm>              <dttm>               <int>
## 1      16011   122     1 2015-08-03 06:37:11 2015-08-05 20:41:12      3
## 2      16035   430     5 2015-08-14 17:53:49 2015-09-02 14:06:09      6
## 3      16036    62     1 2015-08-17 21:56:44 2015-08-17 21:58:52      1
## 4      16037  1296     3 2015-08-23 15:13:57 2015-09-08 18:37:16     14
## 5      16038    73     1 2015-08-20 18:42:33 2015-08-22 22:19:37      3
## 6      16039  1050    10 2015-08-23 02:28:45 2015-09-19 06:08:31      8

We can also group by multiple variables; applying the same function as above but now grouping by motusTagID and recvDeployName, we will get information for each tag detected on each receiver. Since we are grouping by recvDeployName, there will be by default only one recvDeployName in each group, thus the variable nRecv will be 1 for each row. This in not very informative, however we include this to help illustrate how grouping works:

tagRecvSummary <- df.alltags.sub %>% group_by(motusTagID, 
    recvDeployName) %>% summarize(nDet = n(), nRecv = length(unique(recvDeployName)), 
    tsMin = min(ts), tsMax = max(ts), totDay = length(unique(doy)))

head(tagRecvSummary)
## # A tibble: 6 x 7
## # Groups:   motusTagID [2]
##   motusTagID recvDeployName  nDet nRecv tsMin              
##        <int> <chr>          <int> <int> <dttm>             
## 1      16011 North Bluff      122     1 2015-08-03 06:37:11
## 2      16035 Brier2            41     1 2015-09-02 14:03:19
## 3      16035 D'Estimauville    32     1 2015-09-02 07:58:43
## 4      16035 Netitishi        286     1 2015-08-14 17:53:49
## 5      16035 Southwest Head    65     1 2015-09-02 13:06:13
## 6      16035 Swallowtail        6     1 2015-09-02 13:21:27
## # ... with 2 more variables: tsMax <dttm>, totDay <int>

6.4 Plotting your data

Plotting your data is a powerful way to visualize broad and fine-scale detection patterns. This section will give you a brief introduction to plotting using ggplot2. For more in depth information on the uses of ggplot2, we recommend the Cookbook for R, and the rstudio ggplot2 cheatsheet.

To make coarse-scale plots with large files, we suggest first rounding the detection time to the nearest hour or day so that processing time is faster. Here we round detection times to the nearest hour, then make a basic plot of hourly detections by motusTagID:

df.alltags.sub.2 <- mutate(df.alltags.sub, hour = as.POSIXct(round(ts, 
    "hour"))) %>% select(motusTagID, port, tagDeployStart, 
    tagDeployLat, tagDeployLon, recvLat, recvLon, recvDeployName, 
    antBearing, speciesEN, year, doy, hour) %>% distinct()

p <- ggplot(data = df.alltags.sub.2, aes(hour, as.factor(motusTagID)))
p + geom_point() + ylab("MotusTagID") + xlab("Time (rounded to hour)") + 
    theme_bw()

Let’s focus only on tags deployed in 2016, and we can colour the tags by species:

p <- ggplot(data = filter(df.alltags.sub.2, year(tagDeployStart) == 
    2016), aes(hour, as.factor(motusTagID), col = speciesEN))
p + geom_point() + ylab("MotusTagID") + xlab("Time (rounded to hour)") + 
    scale_colour_discrete(name = "Species") + theme_bw()

We can see how tags moved latitudinally by first ordering by hour, and colouring by motusTagID:

df.alltags.sub.2 <- arrange(df.alltags.sub.2, hour)

p <- ggplot(data = filter(df.alltags.sub.2, year(tagDeployStart) == 
    2016), aes(hour, recvLat, col = as.factor(motusTagID), 
    group = as.factor(motusTagID)))
p + geom_point() + geom_path() + theme_bw() + xlab("Time (rounded to hour)") + 
    ylab("Receiver latitude") + scale_colour_discrete(name = "MotusTagID")

Now lets look at more detailed plots of signal variation. We use the full df.alltags.sub dataframe so that we can get signal strength for each detection of a specific tag. Let’s examine fall 2016 detections of tag 22897 at Niapiskau; we facet the plot by deployment name, ordered by decreasing latitude:

p <- ggplot(filter(df.alltags.sub, motusTagID == 22897, 
    recvDeployName == "Niapiskau"), aes(ts, sig))
p + theme_bw() + geom_point() + xlab("Time") + ylab("Signal strength") + 
    facet_grid(recvDeployName ~ .)

We use the sunRiseSet function available in the motus R package (see C.2) to get sunrise and sunset times for all detections. We then zoom in on a certain timeframe and add that information to the above plot by adding a geom_vline() statement to the code, which adds a yellow line for sunrise time, and a blue line for sunset time:

# add sunrise and sunset times to the dataframe
df.alltags.sub <- sunRiseSet(df.alltags.sub, lat = "recvLat", 
    lon = "recvLon")

p <- ggplot(filter(df.alltags.sub, motusTagID == 22897 & 
    ts > ymd("2016-10-11") & ts < ymd("2016-10-17") & 
    recvDeployName == "Niapiskau"), aes(ts, sig))

p + theme_bw() + geom_point() + xlab("Time of year") + 
    ylab("Signal strength") + geom_vline(xintercept = df.alltags.sub$sunrise, 
    col = "orange") + geom_vline(xintercept = df.alltags.sub$sunset, 
    col = "blue")

We can see that during this period, the tag was most often detected during the day, suggesting it may be actively foraging in this area during this time.

The same plots can provide valuable movement information when the receivers are ordered geographically. We do this for motusTagID 16039:

# We'll first order sitelat by latitude (for plots)
df.alltags.sub <- mutate(df.alltags.sub, recvDeployName = as.factor(as.character(reorder(recvDeployName, 
    recvLat))))

p <- ggplot(filter(df.alltags.sub, motusTagID == 16039 & 
    ts < ymd("2015-10-01")), aes(ts, recvDeployName))

p + theme_bw() + geom_point() + xlab("Time of year") + 
    ylab("Receiver name (ordered by latitude)")

We zoom in on a section of this plot and look at antenna bearings to see directional movement past stations:

p <- ggplot(filter(df.alltags.sub, motusTagID == 16039, 
    ts > ymd("2015-09-14"), ts < ymd("2015-10-01")), 
    aes(ts, sig, col = as.factor(antBearing)))
p + theme_bw() + geom_point() + xlab("Time of day") + 
    ylab("Signal strength") + scale_color_discrete(name = "Antenna bearing") + 
    facet_grid(recvDeployName ~ .)

This plot shows the typical flyby pattern of a migrating animal, with signal strength increasing and then decreasing as the tag moves through the beams of the antennas.

6.5 Mapping your data

To generate maps of tag paths, we will once again use summarized data so we can work with a much smaller database for faster processing. Here we’ll summarize detections by day. As we did in Chapter 5, we create a simple function to summarize the data, since we will likely want to do this type of summary over and over again.

# simplify the data by summarizing by the runID. 
# if you want to summarize at a finer (or coarser) scale, you can also create other groups.  
# The simplest alternative is a rounded timestamp variable; for example by using 
# mutate(ts.h = plyr::round_any(ts, 3600) function call. 
# Other options are to just use date (e.g date = as_date(ts))

# 
fun.getpath <- function(df) 
  {
  df %>%
    filter(tagProjID == proj.num, # keep only tags registered to the sample project
                           !is.na(recvLat) | !(recvLat == 0)) %>% 
    group_by(motusTagID, runID, recvDeployName, ambigID, 
             tagDeployLon, tagDeployLat, recvLat, recvLon) %>%
    summarize(max.runLen = max(runLen), ts.h = mean(ts)) %>%
    arrange(motusTagID, ts.h) %>%
    data.frame()
  } # end of function call

df.alltags.path <- fun.getpath(df.alltags.sub)
df.alltags.sub.path <- df.alltags.sub %>%
                filter(tagProjID == proj.num) %>% # keep to tags registered to the sample project
                arrange(motusTagID, ts) %>%       # order data by time stamp for each motus tag ID
                mutate(date = as_date(ts)) %>%    # create date variable
                group_by(motusTagID, date, recvDeployName, ambigID, 
                         tagDeployLon, tagDeployLat, recvLat, recvLon)

df.alltags.path <- fun.getpath(df.alltags.sub.path)

6.5.1 Mapping with Google Maps

Mapping with Google Maps can be a fast way to view flight paths and allows you to select from multiple base layers.

The first step is to create a map with a specified map centre, maptype (“terrain”, “roadmap”, “satellite”, or “hybrid”), and level of zoom (integer for zoom 3-21, 3 being continent level, 10 being city-scale). We then add points for receivers and lines connecting consecutive detections by motusTagID. We can also add points for all receivers that were active during a certain time period if we have already downloaded all metadata.

gmap <-  get_map(location = c(lon = -75, lat = 40), # lon/lat to centre map over
                 maptype = "satellite", # select maptype
                 source = "google",
                 zoom = 4) # zoom, must be a whole number

# just use the tags that we have examined carefully and filtered (in the previous chapter)
df.tmp <- filter(df.alltags.path, 
                           motusTagID %in% c(16011, 16035, 16036, 16037, 16038, 16039))
df.tmp <- arrange(df.tmp, ts.h) # arange by hour
df.tmp <- as.data.frame(df.tmp)

p <- ggmap(gmap)
p + geom_point(data=df.tmp, 
               aes(recvLon, recvLat), pch=21, colour = "black", fill = "yellow") +
  geom_path(data=df.tmp, 
            aes(recvLon, recvLat, group=motusTagID, col = as.factor(motusTagID))) +
  theme_bw() + 
  scale_color_discrete(name="MotusTagID")

We make the same plot, with additional points for all receivers that were active during a specified time:

# get receiver metadata
tbl.recvDeps <- tbl(sql.motus, "recvDeps")
df.recvDeps <- tbl.recvDeps %>% collect %>% as.data.frame() %>% 
    mutate(tsStart = as_datetime(tsStart, tz = "UTC", 
        origin = "1970-01-01"), tsEnd = as_datetime(tsEnd, 
        tz = "UTC", origin = "1970-01-01"))
# for deployments with no end dates, make an end
# date a year from now
df.recvDeps$tsEnd <- as.POSIXct(ifelse(is.na(df.recvDeps$tsEnd), 
    as.POSIXct(format(Sys.time(), "%Y-%m-%d %H:%M:%S")) + 
        lubridate::dyears(1), df.recvDeps$tsEnd), tz = "UTC", 
    origin = "1970-01-01")
# get running intervals for all receiver
# deployments
siteOp <- with(df.recvDeps, lubridate::interval(tsStart, 
    tsEnd))  # get running intervals for each deployment
# set the date range you're interested in
dateRange <- lubridate::interval(as.POSIXct("2015-08-01"), 
    as.POSIXct("2016-01-01"))
# create new variable 'active' which will be set to
# TRUE if the receiver was active at some point
# during your specified date range, and FALSE if
# not
df.recvDeps$active <- lubridate::int_overlaps(siteOp, 
    dateRange)

# create map with receivers active during specified
# date range as red, and receivers with detections
# as yellow
p <- ggmap(gmap)
p + geom_point(data = subset(df.recvDeps, active == 
    TRUE), ggplot2::aes(longitude, latitude), pch = 21, 
    colour = "black", fill = "red") + geom_point(data = df.tmp, 
    aes(recvLon, recvLat), pch = 21, colour = "black", 
    fill = "yellow") + geom_path(data = df.tmp, aes(recvLon, 
    recvLat, group = motusTagID, col = as.factor(motusTagID))) + 
    theme_bw() + scale_color_discrete(name = "MotusTagID")

6.5.2 Creating simple outline maps

We load the base maps.

na.lakes <- map_data(map = "lakes")
na.lakes <- na.lakes %>% mutate(long = long - 360)

# Include all of the Americas to begin
na.map <- map_data(map = "world2")
na.map <- na.map %>% filter(region %in% c("Canada", 
    "USA")) %>% mutate(long = long - 360)

Then, to map the paths, we set the x-axis and y-axis limits based on the location of receivers with detections. Depending on your data, these might need to be modified to encompass the deployment location of the tags, if tags were not deployed near towers with detections. We then use ggplot to plot the map and tag paths. Here we use the Mercator projection and are colouring the paths by motusTagID, including a point for where the tag was deployed:

# set limits to map based on locations of
# detections, ensuring they include the deployment
# locations
xmin <- min(df.tmp$recvLon, na.rm = TRUE) - 2
xmax <- max(df.tmp$recvLon, na.rm = TRUE) + 2
ymin <- min(df.tmp$recvLat, na.rm = TRUE) - 1
ymax <- max(df.tmp$recvLat, na.rm = TRUE) + 1

# map
ggplot(na.lakes, aes(long, lat)) + geom_polygon(data = na.map, 
    aes(long, lat, group = group), colour = "grey", 
    fill = "grey98") + geom_polygon(aes(group = group), 
    colour = "grey", fill = "white") + coord_map(projection = "mercator", 
    xlim = c(xmin, xmax), ylim = c(ymin, ymax)) + xlab("") + 
    ylab("") + theme_bw() + geom_path(data = df.tmp, 
    aes(recvLon, recvLat, group = as.factor(motusTagID), 
        colour = as.factor(motusTagID))) + geom_point(data = df.tmp, 
    aes(tagDeployLon, tagDeployLat), colour = "black", 
    shape = 4) + scale_colour_discrete("motusTagID")

The functions above provide examples of how you can begin exploring your data and are by no means exhaustive. The next chapter will cover some common errors and troubleshooting you may encounter while trying to download and use the .motus sql files.