Skip to content

WildMeOrg/RWildbook

Repository files navigation

RWildbook: R Client for Wildbook v3 API

An R package for interacting with the Wildbook v3 API. This package provides an interface for authenticating with Wildbook instances and searching for encounters, individuals, and other data. For a Python client with aligned functionality see pywildbook.

Many thanks to Simon Bonner for the conception and development of this project for the Wildbook v1 API.

Installation

From R console

# Install dependencies first
install.packages(c("httr2", "R6"))

# Install from local directory
install.packages(".", repos = NULL, type = "source")

From terminal (Command line)

This is the recommended approach for a clean build that includes vignettes:

# 1. Build the source package (tarball)
R CMD build .

# 2. Install the resulting tarball
R CMD INSTALL RWildbook_1.0.0.tar.gz

Note on Vignettes: Building the vignettes requires Pandoc to be installed on your system.

If you do not have Pandoc installed, you can build and install without vignettes:

R CMD build . --no-build-vignettes
R CMD INSTALL RWildbook_1.0.0.tar.gz

Load the package

library(RWildbook)

Quick Start

library(RWildbook)

# Define null-coalescing operator if not already available
`%||%` <- function(x, y) if (is.null(x)) y else x

# Create a client instance
# The base URL can also be set via the WILDBOOK_URL environment variable
client <- WildbookClient$new(Sys.getenv("WILDBOOK_URL", "http://localhost:8080"))

# Login
# Credentials can be passed directly or sourced from WILDBOOK_USERNAME and WILDBOOK_PASSWORD environment variables
client$login()

# Search for encounters
query <- match_all()
results <- client$search_encounters(query, size = 10)

# Print results
for (encounter in results$hits) {
  cat(sprintf("%s: %s %s\n",
              encounter$id,
              encounter$genus,
              encounter$specificEpithet %||% ""))
}

# Logout when done
client$logout()

Authentication

The client uses session-based authentication. After logging in, the session cookie is automatically managed for all subsequent requests. For security, it is highly recommended to use environment variables for sensitive credentials.

# Create client
# The base URL can also be set via the WILDBOOK_URL environment variable
client <- WildbookClient$new(Sys.getenv("WILDBOOK_URL", "http://localhost:8080"))

# Login using environment variables (recommended)
# Credentials sourced from WILDBOOK_USERNAME and WILDBOOK_PASSWORD environment variables
user_info <- client$login()
# Logged in successfully as: user@example.com

# Or login with explicit credentials
user_info <- client$login("user@example.com", "password")

cat(sprintf("User ID: %s\n", user_info$id))
cat(sprintf("Full Name: %s\n", user_info$fullName))

# Check authentication status
if (client$is_authenticated()) {
  cat("✓ Authenticated\n")
}

# Get current user info
user <- client$get_current_user()

# Logout
client$logout()

Searching Encounters

Basic Search

# Get all encounters
results <- client$search_encounters(match_all(), size = 50)

# With pagination
results <- client$search_encounters(
  match_all(),
  from = 0,        # offset
  size = 20,       # page size
  sort = "year",   # sort field
  sort_order = "desc"
)

My Encounters

# Get my 10 most recent encounters
my_encounters <- client$search_encounters(
  client$filter_current_user(),
  size = 10,
  sort = "year",
  sort_order = "desc"
)

# Combine with other filters: my encounters of a specific species
query <- combine_queries(
  client$filter_current_user(),
  filter_by_species("Megaptera novaeangliae"),
  operator = "must"
)
results <- client$search_encounters(query)

Filtering by Species

# Search by species
query <- filter_by_species("novaeangliae")
results <- client$search_encounters(query)

# Search by genus and species
query <- filter_by_species("Megaptera novaeangliae")
results <- client$search_encounters(query)

Filtering by Date Range

# Encounters since 1 November 2025
query <- filter_by_date_range(start_date = "2025-11-01")
results <- client$search_encounters(query)

# Encounters between two dates
query <- filter_by_date_range(
  start_date = "2025-11-01",
  end_date = "2025-12-01"
)
results <- client$search_encounters(query)

Filtering by Location

# Filter by country
query <- filter_by_location(country = "Kenya")
results <- client$search_encounters(query)

# Filter by bounding box
query <- filter_by_location(
  min_lat = -5.0,
  max_lat = 5.0,
  min_lon = 35.0,
  max_lon = 42.0
)
results <- client$search_encounters(query)

Combining Multiple Filters

# Female humpback whales from 2020-2023
species <- filter_by_species("Megaptera novaeangliae")
sex <- filter_by_sex("female")
years <- filter_by_year_range(2020, 2023)

query <- combine_queries(species, sex, years, operator = "must")
results <- client$search_encounters(query)

Finding Unassigned Encounters

# Encounters without an assigned individual
query <- field_missing("individualId")
unassigned <- client$search_encounters(query)

Text Search

# Search for "beach" in locality descriptions
query <- text_search("verbatimLocality", "beach", fuzzy = TRUE)
results <- client$search_encounters(query)

Searching Individuals

# Find individuals with encounters
query <- field_exists("encounters")
results <- client$search_individuals(query, size = 20)

for (individual in results$hits) {
  cat(sprintf("%s: %s\n",
              individual$id,
              individual$displayName %||% "Unnamed"))
}

Getting Specific Records

# Get a specific encounter by UUID
encounter <- client$get_encounter("123e4567-e89b-12d3-a456-426614174000")
print(encounter)

# Get a specific individual by UUID
individual <- client$get_individual("987fcdeb-51a2-43f7-9876-543210fedcba")
print(individual)

User Dashboard

# Get dashboard data for the current user
dashboard <- client$get_user_home()

cat(sprintf("Latest encounters: %d\n", length(dashboard$latestEncounters)))
cat(sprintf("Projects: %d\n", length(dashboard$projects)))

Available Query Helpers

The package provides these helper functions for constructing queries:

  • match_all() - Match all documents
  • filter_by_sex(sex) - Filter by sex
  • filter_by_species(species, genus = NULL) - Filter by species
  • filter_by_year_range(start_year, end_year) - Filter by year range
  • filter_by_date_range(start_date, end_date) - Filter by date range (ISO 8601)
  • filter_by_location(country, location_id, min_lat, max_lat, min_lon, max_lon) - Filter by location
  • filter_by_individual(individual_id) - Find encounters for an individual
  • filter_by_submitter(submitter_id) - Filter by submitter
  • text_search(field, text, fuzzy = FALSE) - Text search in a field
  • field_exists(field) - Find documents where field exists
  • field_missing(field) - Find documents where field is missing
  • combine_queries(..., operator = "must") - Combine multiple queries with AND/OR/NOT logic

Custom Queries

For advanced use cases, you can construct your own OpenSearch/Elasticsearch queries:

# Custom query using Elasticsearch DSL
custom_query <- list(
  bool = list(
    must = list(
      list(term = list(genus = "Tursiops")),
      list(range = list(year = list(gte = 2020, lte = 2023)))
    ),
    must_not = list(
      list(term = list(sex = "unknown"))
    )
  )
)

results <- client$search_encounters(custom_query)

Working with Results

Results are returned as R lists. You can convert them to data frames for easier analysis:

library(dplyr)
library(purrr)

# Search for encounters
query <- match_all()
results <- client$search_encounters(query, size = 100)

# Convert to data frame
encounters_df <- map_dfr(results$hits, function(hit) {
  tibble(
    id = hit$id %||% NA,
    genus = hit$genus %||% NA,
    species = hit$specificEpithet %||% NA,
    year = hit$year %||% NA,
    sex = hit$sex %||% NA,
    locality = hit$verbatimLocality %||% NA,
    lat = hit$decimalLatitude %||% NA,
    lon = hit$decimalLongitude %||% NA
  )
})

# Analyze
encounters_df %>%
  group_by(genus, species) %>%
  summarise(count = n()) %>%
  arrange(desc(count))

Error Handling

RWildbook raises typed conditions that can be caught by class name, giving you precise control over error handling.

Condition classes

Class Raised when
wildbook_error Base class — catches any Wildbook error
wildbook_auth_error Server returns 401, or login returns success=FALSE
wildbook_not_authenticated Method called before login()
wildbook_forbidden Server returns 403
wildbook_not_found Server returns 404
wildbook_bad_request Server returns 400
wildbook_api_error Any other HTTP error

Catching specific errors

tryCatch(
  client$get_encounter("some-uuid"),
  wildbook_not_found  = function(e) cat("Not found:", e$message, "\n"),
  wildbook_auth_error = function(e) cat("Re-authentication required\n"),
  wildbook_error      = function(e) cat("Other Wildbook error:", e$message, "\n")
)

Catching all Wildbook errors

tryCatch(
  client$search_encounters(match_all()),
  wildbook_error = function(e) {
    cat("Wildbook error:", e$message, "\n")
  }
)

Automatic cleanup with with_wildbook_client()

Use with_wildbook_client() instead of a manual tryCatch/finally block to guarantee logout on both success and error:

# Credentials from WILDBOOK_URL, WILDBOOK_USERNAME, WILDBOOK_PASSWORD env vars
with_wildbook_client(\(client) {
  results <- client$search_encounters(match_all(), size = 10)
  results$hits
})

The client is always logged out when the function exits, even if an error occurs inside.

Examples

See the examples/ directory for complete examples:

  • basic_usage.R - Basic login, search, and logout
  • advanced_search.R - Complex queries, pagination, and filtering

Run examples:

# Set environment variables
export WILDBOOK_URL="http://localhost:8080"
export WILDBOOK_USERNAME="your@email.com"
export WILDBOOK_PASSWORD="yourpassword"

# Run in R
Rscript examples/basic_usage.R
Rscript examples/advanced_search.R

Vignettes

The package includes vignettes demonstrating common use cases:

  • Individual Statistics - Analyze encounter patterns, count distinct individuals by species, and identify frequently encountered individuals

View vignettes:

# List all vignettes
browseVignettes("RWildbook")

# Open a specific vignette
vignette("individual_statistics", package = "RWildbook")

Development

See CONTRIBUTING.md for contributor workflow, coding conventions, tests, and pull request guidance.

Package Structure

RWildbook/
├── R/
│   ├── client.R              # WildbookClient R6 class
│   ├── conditions.R          # Typed error condition hierarchy
│   └── queries.R             # Query helper functions
├── man/                      # Roxygen2-generated documentation
├── examples/
│   ├── basic_usage.R
│   └── advanced_search.R
├── tests/                    # Test files
├── DESCRIPTION              # Package metadata
├── NAMESPACE               # Package exports
└── README.md

Building Documentation

# Install roxygen2
install.packages("roxygen2")

# Generate documentation
roxygen2::roxygenize()

Running Tests

# Install testthat
install.packages("testthat")

# Run tests
testthat::test_dir("tests")

API Reference

WildbookClient Class

R6 class for interacting with Wildbook.

Methods

  • $new(base_url) - Create a new client instance
  • $login(username, password) - Authenticate user
  • $logout() - End session
  • $is_authenticated() - Check authentication status
  • $get_current_user() - Get current user info
  • $get_user_home() - Get user dashboard data
  • $search_encounters(query, from = 0, size = 10, sort = NULL, sort_order = NULL) - Search encounters
  • $get_encounter(encounter_id) - Get specific encounter
  • $search_individuals(query, from = 0, size = 10, sort = NULL, sort_order = NULL) - Search individuals
  • $get_individual(individual_id) - Get specific individual
  • $filter_current_user() - Query for encounters by the logged-in user

Requirements

  • R >= 4.2.0
  • httr2 >= 1.0.0
  • R6 >= 2.5.0

Versioning

RWildbook and pywildbook follow a shared versioning convention to make feature equivalence explicit:

  • Major and minor versions are synchronised across both libraries. RWildbook 1.2.x and pywildbook 1.2.x expose the same API surface.
  • Patch versions are independent. Bug fixes, dependency updates, and other library-specific changes do not require a coordinated release.
  • Minor bumps are coordinated. When new features are added they land in both libraries together, then both get the minor bump.

This project follows Semantic Versioning.

License

MIT License

Support

For issues, questions, and more information:

Related Projects

  • Wildbook - The main Wildbook platform
  • pywildbook - An Python client with aligned functionality

Acknowledgments

Built with:

  • httr2 - HTTP client
  • R6 - Object-oriented programming
  • jsonlite - JSON parsing

About

R package to extract data from the Wildbook framework for managing mark-recapture data.

Resources

License

Unknown, MIT licenses found

Licenses found

Unknown
LICENSE
MIT
LICENSE.md

Contributing

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors