The Breeding Bird Survey is an annual survey conducted by the USGS each breeding season. Highly skilled citizen scientists visit several thousand locations across the United States and Canada to survey breeding birds. At each location, observers conduct 50 3-minute point counts along a 40km route.

Functions

We’ll use a few functions:

unzipBBS <- function(path) {
  
  print("Unzipping folder")
  folderpath <- paste0(path, ".zip")
  unzip(folderpath,
        exdir = path)
  
  
  print("Unzipping 50-StopData.zip")
  unzip(paste0(path, "/50-StopData.zip"), overwrite = T,
        exdir = path)
  
  base <- paste0(path, "/50-StopData/1997ToPresent_SurveyWide")
  
  filesZip <- list.files(base, pattern = ".zip")
  
  print("Unzipping files")
  pb <- txtProgressBar(min = 0, max = 10, initial = 0, style = 3)
  
  for (i in 1:length(filesZip)) {
    #print(paste0("Unzipping file ", i))
    unzip(paste0(base, "/", filesZip[i]), 
          exdir = base,
          overwrite = T)
    setTxtProgressBar(pb,i)
    
  }
  
  print("Unzipping migrants")
  
  unzip(paste0(path, "/MigrantNonBreeder.zip"), overwrite = T,
        exdir = path)
  unzip(paste0(path, "/MigrantNonBreeder/Migrants.zip"), overwrite = T,
        exdir = path)
  
  print("Unzipping other data")
  unzip(paste0(path, "/Routes.zip"), overwrite = T,
        exdir = path)
  unzip(paste0(path, "/Weather.zip"), overwrite = T,
        exdir = path)
  
}

readBBS <- function(path, migrant = F) {
  base <- paste0(path, "/50-StopData/1997ToPresent_SurveyWide")
  
  files <- list.files(base, pattern = ".csv")
  
  print("Reading in breeding birds...")
  pb <- txtProgressBar(min = 0, max = 10, initial = 0, style = 3)
  
  data <- read.csv(paste0(base, "/", files[1]))
  colnames(data) <- tolower(colnames(data))
  setTxtProgressBar(pb,1)
  
  for (j in 2:length(files)) {
    tmp <- read.csv(paste0(base, "/", files[j]))
    colnames(tmp) <- tolower(colnames(tmp))
    setTxtProgressBar(pb,j)
    
    data <- rbind(data, tmp)
  }
  
  if (migrant == T) {
    print("Reading in migrants...")
    # unzip(paste0(path, "MigrantNonBreeder.zip"), overwrite = T)
    # unzip(paste0(path, "MigrantNonBreeder/Migrants.zip"), overwrite = T)
    # 
    mig <- read.csv(paste0(path, "/Migrants.csv"))
    colnames(mig) <- tolower(colnames(mig))
    
    data <- rbind(data, mig)
  }
  
  return(data)
}

summarizeBBS <- function(raw) {
  raw$count <- rowSums(raw[,8:57])
  raw$id <- paste0(raw$countrynum, "_", raw$statenum, "_", raw$route)
  raw1 <- raw[,c("id", "year", "aou", "count")]
  
  return(raw1)
}

aouToSp <- function(raw, path) {
  spList <- read.fwf(file = paste0(path, "/SpeciesList.txt"),
                     widths = c(7, 5, 50, 50, 50, 
                                50, 50, 50, 50),
                     skip = 14,
                     col.names = c("Seq", "aou", "English",
                                   "French", "Spanish",
                                   "Order", "Family", "Genus", "Species"),
                     strip.white = T)
  
  spList$gs <- paste(spList$Genus, spList$Species, sep = " ")
  spList <- spList[,c("aou", "gs")]
  
  bbsSp <- merge(raw, spList, by = "aou")
  bbsSp <- bbsSp[,c("id", "year", "gs", "count")]
  colnames(bbsSp) <- c("id", "year", "species", "count")
  
  return(bbsSp)
}

And the tidyverse package:

library(tidyverse)

Step 1: Download the data

Raw Breeding Bird Survey data can be downloaded from the BBS ScienceBase Site. Under “Attached Files”, select “Download All” to download the data in a .zip folder called “2020Release-Nor.zip”. You need to manually unzip this file, which will give you a folder called “2020Release-Nor”. Set your working directory to wherever you saved this folder:

path <- "X:/data-BBS/2022Release_Nor"

Step 2: Unzip the data

This step will take a little while. You only need to run this one time, when you first download the data. After that, the unzipped files will be saved on your computer.

unzipBBS(path)

Step 3: Read in the data

The data are separated into 10 .csv files that hold the counts at each of the 50 stops on a BBS route. readBBS() reads these .csv files and combines these folders into a single dataframe.

bbs <- readBBS(path = path)

The BBS keeps records of migratory or non-breeding birds in a separate file. To read in these species as well, include migrant = T.

bbs <- readBBS(path = path, 
               migrant = T)

Step 4: Summarize the data

Usually when working with BBS data, a single count for an entire route is sufficient. This function adds up all the point counts within a route to a single value. It also creates an id column that contains the country, state, and route codes.

bbs <- summarizeBBS(bbs)

Step 5: Change AOU codes to species names

BBS data uses AOU codes, but specie names are more useful. aouToSp() reads in the SpeciesList.txt, which was downloaded with the raw BBS data, and uses it to replace AOU codes with species names.

bbs <- aouToSp(bbs, path = path)

Step 6: Get additional data

First, get the latitude and longitude from routes.csv.

locs <- read.csv(paste0(path, "/routes.csv")) %>%
  mutate(id = paste0(CountryNum, "_", StateNum, "_", Route)) %>%
  select(id, Latitude, Longitude)

Then, read in weather.csv to get observer ID for each observation and RunType, which is used to filter the quality of each observation. You may also want to keep other weather variables, such as temperature and wind speed.

weather <- read.csv(paste0(path, "/weather.csv")) %>%
  mutate(id = paste0(CountryNum, "_", StateNum, "_", Route),
         year = Year) %>%
  select(id, year, ObsN, RunType)

Merge these two dataframe with the count data. We’ll also filter the data in two ways. First, keep only routes that have RunType = 1. Second, we only read in data from 1997 to present, so keep only routes from after 1996.

bbs <- bbs %>%
  inner_join(locs, by = "id") %>%
  inner_join(weather, by = c("id", "year")) %>%
  filter(RunType == 1,
         year > 1996)

The BBS data is now ready to use!