Calculate density at each location from seasonal time and EDD (with optional pooled fallback)
Source:R/cam_calc_density_by_loc.R
cam_calc_density_by_loc.RdComputes seasonal (or aggregated) density estimates per deployment × species using time-in-front-of-camera (TIFC) totals and effective detection distance (EDD) lookups. Supports exact EDD matches on species group, vegetation category, season, and—when available—camera model and height. Optionally applies a dist-group–specific pooled fallback when exact EDDs are missing. Note that filtering locations with total days below a certain threshold (e.g., 30 days) should be done prior to running the this function.
Usage
cam_calc_density_by_loc(
duration_df,
edd_category_df,
model_df = NULL,
cam_fov_angle = 40,
format = c("long", "wide"),
include_project = TRUE,
height_col = "height",
dist_groups_df = NULL,
edd_df = NULL,
aggregate = FALSE,
agg_exclude_species = "Bear",
agg_exclude_season = "winter",
use_global_edd = FALSE,
annotate_edd_source = TRUE
)Arguments
- duration_df
Tibble with: project, location, species_common_name, season, total_season_days, total_duration (seconds). If present,
modeland/orheightare used directly.- edd_category_df
Tibble with vegetation/EDD category per camera; must contain
overall_categoryplus (project,location) orproject_location.- model_df
Optional tibble (
project,location,model) to attach ifduration_dflacks amodelcolumn.- cam_fov_angle
Camera FOV in degrees. Default 40.
- format
"long" (default) or "wide".
- include_project
Keep
projectin wide output. Default TRUE.- height_col
Column name in
duration_dfto use as height (default "height").- dist_groups_df
Optional override mapping (species_common_name -> dist_group). If NULL, an object named
dist_groupsmust exist in your env/package.- edd_df
Optional override EDD table. If NULL, an object named
eddmust exist. Must include: dist_group, season, overall_category, edd; ideallyn, and optionallymodel,height.- aggregate
Logical; if TRUE, return weighted mean per deployment × species (weights =
total_season_days). Rows matchingagg_exclude_species×agg_exclude_seasonare removed before aggregation. See alsoagg_exclude_species.- agg_exclude_species
Regex pattern (case-insensitive) for species to exclude from aggregation in a specific season. Default
"Bear". Set toNULLto disable exclusion entirely.- agg_exclude_season
Season label(s) to exclude for the matching species. Default
"winter". Set toNULLto disable exclusion entirely.- use_global_edd
Logical; if TRUE, fill missing exact EDDs using a dist-group–specific plan.
- annotate_edd_source
Logical; if TRUE (default), add
edd_sourcecolumn with values"observed","prefilled", or"pooled". See Details.
Details
Inputs and joins
duration_dfmust contain:project,location,species_common_name,season,total_season_days(days of operation in that season), andtotal_duration(seconds of TIFC in that season). If present,modeland/orheightare used directly.edd_category_dfmust provideoverall_categoryfor each (project,location) (or viaproject_locationthat gets split).Species are mapped to EDD groups via
dist_groups(override withdist_groups_df).EDDs are taken from
edd(override withedd_df). Exact joins use keys:dist_group,overall_category,season, and, when present in both data and lookup,modeland/orheight. If multiple EDD rows share the same keys, the entry with the largestnis chosen.
Density calculation
Area per season (m²):
area_m2 = π * (edd^2) * (cam_fov_angle / 360).Effort per season (m²·10⁻²):
effort = total_season_days * (area_m2 / 100)(legacy factor).CPUE:
cpue = total_duration / effort.Density (km⁻²):
density_km2 = (cpue / 86400) * 10000.If
edd,effort, ortotal_season_daysare missing/zero,density_km2isNA.
Pooled EDD fallback (use_global_edd = TRUE)
Uses weighted pooled EDDs (weights = n) following a conservative plan per dist_group:
Coyote,Deer,LargeUngulates: pool overoverall_category(keys:dist_group,season,model,height; absent keys are ignored).Bear,Hare,Lynx,SmallMustelids,Wolf: try pooled-overall first, then pool over height. If pooling supplies an EDD andannotate_edd_source = TRUE, marksedd_source = "pooled". If pooling fails, density remainsNA.
EDD source annotation (annotate_edd_source = TRUE)
Adds an edd_source column with three levels:
"observed": EDD came from a direct match in the lookup table withn > 0(real measured detections)."prefilled": EDD came from a direct match butn = 0, meaning the value was pre-filled in the sysdata EDD table to cover a missing model/height combination within the vegetation category."pooled": no exact match existed; EDD was derived at runtime byuse_global_eddpooling across vegetation categories.
Aggregation (aggregate = TRUE)
Returns one row per deployment × species with
weighted.mean(density_km2, w = total_season_days, na.rm = TRUE).Removes Bear–winter rows before aggregation (including zeros).
When
annotate_edd_source = TRUE,edd_sourceis carried through using the most conservative level across seasons:"pooled">"prefilled">"observed".
Outputs
format = "long": per-season rows withoverall_category,model,height,total_season_days,total_duration,density_km2, and (optionally)edd_source.format = "wide": densities pivoted to one row per deployment (diagnostic season-day columns included).aggregate = TRUE: one row per deployment × species (no seasons).
Examples
if (FALSE) { # \dontrun{
# dur is the output of cam_sum_total_time(), with model and height columns added
density <- dur |>
dplyr::left_join(model_lookup, by = c("project", "location")) |>
dplyr::mutate(height = "high") |>
dplyr::filter(total_season_days >= 30) |>
cam_calc_density_by_loc(
edd_category_df = edd_categories,
cam_fov_angle = 40,
format = "long",
aggregate = TRUE,
use_global_edd = TRUE,
annotate_edd_source = TRUE
)
} # }