A collection of R scripts and Shiny apps for automating and managing iNaturalist observations.
rint/
├── R/ # General utility functions
│ ├── inat_api.R # iNaturalist API wrapper functions
│ └── (other shared utilities)
├── hugo-templates/ # Custom templates for Hugo gallery
│ ├── assets/css/custom.css # Elegant gallery styling
│ └── layouts/partials/ # Custom Hugo partials
├── batch_update_annotations.R # Specialized: Batch add "Alive"/"Organism"
├── find_duplicate_photos.R # Specialized: Find observations with duplicate photos
├── generate_hugo_gallery.R # Specialized: Generate photo portfolio from iNaturalist
├── app.R # Shiny app for banding assessment
├── setup_config.R # Initial configuration helper
└── config.json # API credentials and settings
Structure Philosophy: General-purpose functions live in R/, while specialized task scripts (sub-projects) live in the main folder.
-
Install R Packages:
install.packages(c("httr", "jsonlite", "shiny", "shinyjs", "bslib", "RSQLite", "digest")) # Optional (for perceptual hashing in photo duplicate detection): install.packages("magick")
-
Configure Credentials:
- Copy
config.example.jsontoconfig.json - Set
user_idto your iNaturalist username or ID - Set
access_tokento your iNaturalist JWT (API Token)- Find this by logging into iNaturalist
- Open Developer Tools (F12) > Network tab
- Perform an action (like loading the dashboard)
- Look for
Authorization: Bearer ...in request headers - Copy the token
- Set
target_species_id(e.g., 793469 for your target species) - Set
target_place_id(e.g., 7016 for Norway)
- Copy
-
Auto-Configure IDs:
source("setup_config.R")This will automatically populate annotation IDs and field IDs in
config.json.
Automatically add "Alive" and "Organism" annotations to observations:
source("batch_update_annotations.R")
batch_update_annotations()When to use: After uploading observations that need standardized annotations.
Find observations that share photos and populate a field with duplicate IDs.
Three detection modes:
| Mode | What it finds | Downloads photos? |
|---|---|---|
| Default | Same photo ID used in multiple observations | No |
compare_images |
Also: re-uploaded identical files (MD5 match) | Yes |
compare_images + use_perceptual |
Also: visually similar images | Yes |
source("find_duplicate_photos.R")
# Mode 1: Shared photo IDs only (fast, no downloads)
find_duplicate_photos(field_id = 7855)
# Mode 2: Also find re-uploaded copies (same image, different photo ID)
find_duplicate_photos(field_id = 7855, compare_images = TRUE)
# Mode 3: Full analysis with perceptual hashing
find_duplicate_photos(field_id = 7855, compare_images = TRUE, use_perceptual = TRUE)
# Preview changes without updating iNaturalist
find_duplicate_photos(field_id = 7855, dry_run = TRUE)
# With filters
find_duplicate_photos(field_id = 7855, taxon_id = 793469, place_id = 7016)Command-line usage:
# Shared photo IDs only
Rscript find_duplicate_photos.R --field-id 7855
# With image comparison
Rscript find_duplicate_photos.R --field-id 7855 --compare-images
# Full analysis
Rscript find_duplicate_photos.R --field-id 7855 --compare-images --perceptual
# Preview mode
Rscript find_duplicate_photos.R --field-id 7855 --dry-runRequirements:
- Default mode: Just
RSQLite,digest - Perceptual mode: Also requires
magickpackage - Creates
photo_cache.dbfor caching downloaded hashes
Interactive Shiny app for quickly reviewing observations and assessing banding status:
shiny::runApp("app.R")Keyboard shortcuts:
- 1: Present
- 2: Absent
- 3: Unable to Determine
- Space: Skip
When to use: Fast manual review of hundreds of bird observations to assess banding presence.
Generate an elegant, content-focused photo portfolio website from your iNaturalist observations.
Features:
- Auto-creates albums by country and iconic taxon (Birds, Mammals, etc.)
- Links each photo back to its iNaturalist observation
- Displays species name (common + scientific) on each photo
- Excludes observations with specific observation fields
- Uses hugo-theme-gallery for a modern, responsive design
source("generate_hugo_gallery.R")
# Generate gallery with default settings
generate_hugo_gallery()
# Preview what would be generated (no downloads)
generate_hugo_gallery(dry_run = TRUE)
# Exclude observations with a specific observation field
# (e.g., field ID 12345 marks photos you don't want in the gallery)
generate_hugo_gallery(exclude_field_id = 12345)
# Test with limited observations
generate_hugo_gallery(max_observations = 20)Command-line usage:
# Generate gallery
Rscript generate_hugo_gallery.R
# Preview mode
Rscript generate_hugo_gallery.R --dry-run
# Exclude specific field and limit for testing
Rscript generate_hugo_gallery.R --exclude-field 12345 --max 50
# Custom output directory
Rscript generate_hugo_gallery.R --output my-galleryConfiguration (in config.json):
{
"gallery": {
"output_dir": "hugo-gallery",
"exclude_field_id": null,
"image_size": "large",
"albums": {
"by_country": true,
"by_iconic_taxon": true,
"by_year": false
},
"site_title": "Nature Photography",
"site_description": "Wildlife photographs from iNaturalist"
}
}Building the site (requires Hugo installed):
cd hugo-gallery
hugo mod get -u # Download theme module
hugo server # Preview locally at http://localhost:1313
hugo # Build static site to /publicWhen to use: Create a beautiful portfolio website from your iNaturalist photos for sharing or self-hosting.
All scripts include built-in rate limiting (0.5s between calls) to respect iNaturalist's API guidelines.
- All specialized scripts source functions from
R/inat_api.R - Scripts can be run interactively in R or from command line with
Rscript - The photo duplicate finder caches results in SQLite for efficiency
- Perceptual hashing is slower but catches more duplicates (different photo IDs, same image)
- The Hugo gallery generator requires Hugo Extended (for image processing) to build the site
- Custom gallery styling can be modified in
hugo-templates/assets/css/custom.css