---
title: "Camera Survey Analysis with tidycreel"
output:
  rmarkdown::html_vignette:
    highlight: null
vignette: >
  %\VignetteIndexEntry{Camera Survey Analysis with tidycreel}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

## Overview

Remote cameras mounted at boat launches or trail heads record angler arrivals
with no observer present. This enables 24-hour coverage of access points that
would otherwise require continuous staffing. The `tidycreel` package supports
two camera-based sub-modes:

- **Counter mode** — the camera records a single daily total of incoming
  anglers (e.g., a passive infrared counter). The data arrive as one row per
  sampling day with a numeric ingress count.
- **Ingress-egress mode** — the camera records individual arrival and
  departure timestamps for each angler or party. The raw data are paired
  POSIXct timestamps that are preprocessed with
  `preprocess_camera_timestamps()` before entering the standard effort
  estimation workflow.

In both modes, use the `camera_status` column to identify failures. Unlike
random missed observations (modeled via `missing_sections`), a camera failure
leaves an **informative gap**: the number of anglers that passed during the
outage is unknown and cannot be estimated from nearby observations. Remove
these rows before calling `add_counts()`.

## Example Data

This vignette uses three built-in datasets representing a hypothetical summer
creel survey at a Nebraska reservoir boat launch in June 2024.

```{r data}
library(tidycreel)

data(example_camera_counts)
data(example_camera_timestamps)
data(example_camera_interviews)

head(example_camera_counts)
head(example_camera_timestamps)
head(example_camera_interviews)
```

The `example_camera_counts` dataset contains 10 rows: nine operational days
plus one battery failure gap. The `example_camera_timestamps` dataset has 14
raw arrival/departure pairs across four sampling days. The
`example_camera_interviews` dataset has 40 complete angler interviews
targeting walleye and bass across eight sampling days.

## Counter Mode

### Build a Survey Calendar

Counter-mode surveys require a calendar that covers the sampling frame. Here
we build one from the unique dates in the counts and interview data.

```{r counter-calendar}
# Collect all unique sampling dates across datasets
all_dates <- sort(unique(c(
  example_camera_counts$date,
  example_camera_interviews$date
)))

# Assign day type for each date (weekday = Mon-Fri, weekend = Sat-Sun)
cam_calendar <- data.frame(
  date = all_dates,
  day_type = ifelse(
    weekdays(all_dates) %in% c("Saturday", "Sunday"),
    "weekend", "weekday"
  ),
  stringsAsFactors = FALSE
)

head(cam_calendar)
```

### Design Construction

Build a camera design using `creel_design()` with `survey_type = "camera"`.
The `camera_mode` argument is required. Omitting it produces an informative
error:

```{r counter-error, error = TRUE}
creel_design(
  cam_calendar,
  date = date, strata = day_type,
  survey_type = "camera"
)
```

Build the correct design with `camera_mode = "counter"`:

```{r counter-design}
design_counter <- creel_design(
  cam_calendar,
  date        = date,
  strata      = day_type,
  survey_type = "camera",
  camera_mode = "counter"
)
print(design_counter)
```

## Handling Informative Gaps

The battery failure row on 2024-06-11 has `ingress_count = NA`. This is not
a random unsampled day — the camera was physically unable to record. Including
it in `add_counts()` would silently propagate a missing value into the
Horvitz-Thompson estimator.

```{r gap-row}
# The gap row
subset(example_camera_counts, camera_status != "operational")
```

The correct approach is to **filter to operational rows** before calling
`add_counts()`. This is fundamentally different from `missing_sections`, which
models probabilistic non-coverage within a sampled period. A camera failure
means no data exist — the effort during that period is unknown.

```{r counter-filter}
# Keep only days when the camera was working
counts_clean <- subset(example_camera_counts, camera_status == "operational")
nrow(counts_clean) # 9 operational rows
```

### Effort Estimation

Camera effort is estimated by `est_effort_camera()`, not by the generic
`estimate_effort()`. The distinction matters and is not cosmetic: a camera
count is a daily total of **arrivals**, not an instantaneous count of anglers
present, so summing it over days gives arrivals rather than effort. The generic
estimator refuses a camera design for that reason:

```{r counter-refusal, error = TRUE}
design_counter <- add_counts(design_counter, counts_clean)
estimate_effort(design_counter)
```

`est_effort_camera()` converts counts to effort by calibrating them against
interview data: for each stratum it estimates `rho`, the hours of effort per
camera count, from the days that carry both a count and interviews, then
applies it to that stratum's total counts. The counts are the first phase of a
double sample and the days carrying interviews are the second, so the estimator
is a double-sampling ratio estimator (Cochran 1977, Chapter 12); the ratio's
variance is Cochran's eq. 2.46 with the finite-population correction omitted.
Calibrating camera counts against paired creel observations is established
practice -- Hartill et al. (2016), van Poorten et al. (2015), Eckelbecker et
al. (2022) -- but each of those uses a different estimator, and this
ratio-of-totals form is the package's own application rather than a
reproduction of any of them. See `?est_effort_camera` for the full note.

`example_camera_interviews` records one row per angler, so `n_anglers = 1`
states that `hours_fished` is already an individual angler's hours. Supplying
it is what earns the result its `angler-hours` label — without it the ratio
inherits whatever unit the effort column holds, which the package cannot
identify, and the unit is reported as unknown.

```{r counter-effort}
effort_counter <- suppressWarnings(est_effort_camera(
  design_counter,
  interviews = example_camera_interviews,
  n_anglers  = 1
))
print(effort_counter)
```

The `estimate` column is total angler-hours across the survey period. The
standard error carries two named components, which `se_components` reports
separately:

```{r counter-se-components}
effort_counter$se_components
```

`count_sampling` is the sampling variance of the camera counts themselves;
`calibration` is the variance of the estimated `rho`. Splitting them shows
which half of the uncertainty dominates — here the calibration ratio does, so
more interview days would buy more precision than more camera days would.

## Ingress-Egress Mode

When the camera records individual arrival and departure timestamps, use
`preprocess_camera_timestamps()` to aggregate the raw pairs into daily effort
hours before calling `add_counts()`.

### Preprocess Timestamps

```{r ie-preprocess}
daily_effort <- preprocess_camera_timestamps(
  example_camera_timestamps,
  date_col    = date,
  ingress_col = ingress_time,
  egress_col  = egress_time
)
print(daily_effort)
```

`preprocess_camera_timestamps()` sums all valid trip durations within each day
and returns a data frame with `date` and `daily_effort_hours`. Rows where
`egress_time < ingress_time` (negative durations) are dropped from that sum, and
a warning reports how many.

Read that warning rather than dismissing it, because the returned data will not
repeat it. A dropped pair does not make the day `NA`; it makes the day's total
smaller. On the fixture above, flipping a single ingress/egress pair takes one
day from 11 hours to 7.75 — a 30% undercount that arrives as an ordinary number,
indistinguishable in `daily_effort_hours` from a day that genuinely saw less
fishing. The warning is the only place the exclusion is visible, so treat a
non-zero count as something to resolve in the source data before estimating,
not as routine noise.

Because `add_counts()` requires all design strata columns, merge the day type
back in from the raw timestamps:

```{r ie-merge-strata}
day_type_key <- unique(example_camera_timestamps[, c("date", "day_type")])
daily_effort <- merge(daily_effort, day_type_key, by = "date")
print(daily_effort)
```

### Build the Design and Estimate Effort

```{r ie-design}
ie_calendar <- data.frame(
  date = daily_effort$date,
  day_type = daily_effort$day_type,
  stringsAsFactors = FALSE
)

design_ie <- creel_design(
  ie_calendar,
  date        = date,
  strata      = day_type,
  survey_type = "camera",
  camera_mode = "ingress_egress"
)

design_ie <- add_counts(design_ie, daily_effort)
```

Ingress-egress counts are already in hours, so the calibration ratio here is
close to dimensionless: it corrects camera-measured hours to interview-measured
angler-hours rather than converting a count into a duration. The estimator is
the same one.

```{r ie-effort}
ie_interviews <- example_camera_interviews[
  example_camera_interviews$date %in% daily_effort$date,
]

effort_ie <- suppressWarnings(est_effort_camera(
  design_ie,
  interviews = ie_interviews,
  n_anglers  = 1
))
print(effort_ie)
```

The standard error is `NA` here, and that is the estimator working rather than
failing. These four sampling days give the `weekend` stratum only one day
carrying both a count and interviews, and a single paired day gives the
calibration ratio no measurable spread. Reporting `0` for that variance would
present the most uncertain calibration as the most precise one, so the package
carries it as `NA` and says which stratum is responsible. A second matched
interview day in that stratum recovers a standard error.

## Catch Estimation

Camera designs estimate **effort only**. `estimate_catch_rate()` still works —
a catch rate comes from the interviews and does not involve the camera at all:

```{r catch-rate}
design_catch <- suppressMessages(add_interviews(
  design_counter,
  example_camera_interviews,
  catch       = walleye,
  effort      = hours_fished,
  trip_status = trip_status,
  n_anglers   = 1
))
catch_rate <- suppressWarnings(estimate_catch_rate(design_catch))
print(catch_rate)
```

A **total** catch is a different matter, and `estimate_total_catch()` refuses a
camera design:

```{r total-catch-refusal, error = TRUE}
estimate_total_catch(design_catch)
```

A total is the product of a rate and an effort, and this function builds its own
effort by the generic route — the one that sums arrivals. It would therefore
multiply a rate per angler-hour by a count of arrivals and report the product as
fish. The number looked entirely plausible, which is why this is refused rather
than warned about.

There is no camera catch estimator to reach for instead. `est_effort_camera()`
gives calibrated angler-hours, and multiplying those by the catch rate above is
arithmetic a reader can do deliberately — but the package will not do it
silently, because the standard error of that product needs the calibration
variance and the rate variance combined, and nothing here does that yet.

`estimate_total_harvest()` and `estimate_total_release()` refuse camera designs
for the same reason.


## Summary

The table below contrasts the two camera sub-modes:

```{r summary-table, echo = FALSE}
knitr::kable(data.frame(
  Feature = c(
    "Input data",
    "Preprocessing step",
    "Effort unit",
    "Gap handling",
    "camera_mode value"
  ),
  `Counter mode` = c(
    "One count per day",
    "None (counts used directly)",
    "Daily ingress count",
    "Exclude camera_status != 'operational' rows",
    '"counter"'
  ),
  `Ingress-egress mode` = c(
    "POSIXct arrival/departure pairs",
    "preprocess_camera_timestamps()",
    "Daily effort-hours",
    "Negative durations warned and excluded",
    '"ingress_egress"'
  ),
  check.names = FALSE
), caption = "Comparison of camera survey sub-modes in tidycreel")
```

Both sub-modes are estimated by the same function, `est_effort_camera()`, so no
changes to downstream code are required when switching between them. Neither
sub-mode goes through `estimate_effort()`, which refuses camera designs, nor
through `estimate_total_catch()`, `estimate_total_harvest()` or
`estimate_total_release()`, which refuse them for the same reason.

## References

- Jones, C. M., & Pollock, K. H. (2012). Recreational survey methods:
  estimation of effort, harvest, and abundance. Chapter 19 in *Fisheries
  Techniques* (3rd ed.), pp. 883-919. American Fisheries Society.

- Malvestuto, S. P. (1996). Sampling the recreational angler. Chapter 20 in
  *Fisheries Techniques* (2nd ed.), pp. 591-623. American Fisheries Society.

- Cochran, W. G. (1977). *Sampling Techniques* (3rd ed.). Wiley. Section 2.11
  (ratio estimator and its variance, eq. 2.46) and Chapter 12 (double
  sampling), Section 12.9, p. 343.

- Hartill, B. W., Payne, G. W., Rush, N., & Bian, R. (2016). Bridging the
  temporal gap: continuous and cost-effective monitoring of dynamic
  recreational fisheries by web cameras and creel surveys. *Fisheries
  Research*, 183, 488-497.

- van Poorten, B. T., Carruthers, T. R., Ward, H. G. M., & Varkey, D. A.
  (2015). Imputing recreational angling effort from time-lapse cameras using
  an hierarchical Bayesian model. *Fisheries Research*, 172, 265-273.

- Eckelbecker, R. W., Coleman, T. S., & Catalano, M. J. (2022). Incorporating
  time-lapse digital cameras into creel surveys at three Alabama reservoirs.
  *North American Journal of Fisheries Management*, 42, 1349-1358.

- Hartill, B. W., Taylor, S. M., Keller, K., & Weltersbach, M. S. (2020).
  Digital camera monitoring of recreational fishing effort: applications and
  challenges. *Fish and Fisheries*, 21, 204-215. A review of camera
  monitoring practice; it presents no estimator or variance.
