---
title: "GLMM Aerial Effort Estimation"
output:
  rmarkdown::html_vignette:
    highlight: null
vignette: >
  %\VignetteIndexEntry{GLMM Aerial Effort Estimation}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

## When to Use the GLMM Estimator

If your pilot always flies at the same time of day — say, 10 AM — your
instantaneous count systematically over- or under-represents total daily effort.
Early-morning flights catch fewer anglers than are present at peak hours;
late-afternoon flights may miss early starters entirely. When flight timing is
non-random, the simple expansion (count × h_open / v) inherits this temporal
bias.

The GLMM approach (Askey et al. 2018) models how angler counts change through
the day using a quadratic hour effect and a day-level random intercept:
`count ~ poly(hour, 2) + (1 | date)`. Once the diurnal curve is estimated, the
model integrates predicted counts across the full open-water window — correcting
for wherever in the curve the actual flight fell.

**When to use the GLMM estimator:**

1. Flights always occur in the same part of the day (fixed morning or afternoon
   schedule).
2. You have multiple overflights per day across several days (minimum 8–10
   survey days recommended for stable random-effect estimation).

**When to use the simple estimator:**

1. Flight timing is randomly assigned across the open-water window.
2. You have only one count per day.

For the basic aerial workflow without GLMM correction, see the
[aerial surveys vignette](aerial-surveys.html).

## Example Data

The `example_aerial_glmm_counts` dataset contains 12 survey days with 4
overflights per day at fixed hours (7, 10, 13, and 16 hours), producing
48 observations. Angler counts follow a diurnal curve with day-level Poisson
variability — representative of a scenario where a fixed morning-to-afternoon
flight schedule is used throughout the season.

```{r data}
library(tidycreel)
data(example_aerial_glmm_counts)
head(example_aerial_glmm_counts)
```

The four columns are `date`, `day_type`, `n_anglers` (instantaneous count), and
`time_of_flight` (decimal hour of each overflight).

## Building the Aerial Design

Build an aerial `creel_design` from the survey dates and attach the count data.
The `h_open = 14` argument specifies the number of hours the fishery is open
each day — this enters the final expansion after the diurnal curve is
integrated.

```{r design}
aerial_cal <- unique(example_aerial_glmm_counts[, c("date", "day_type")])
aerial_cal <- aerial_cal[order(aerial_cal$date), ]

design <- creel_design(
  aerial_cal,
  date        = date,
  strata      = day_type,
  survey_type = "aerial",
  visibility_correction = "none",
  angler_ratio = 1,
  angler_ratio_se = 0,
  h_open      = 14
)

design <- add_counts(design, example_aerial_glmm_counts, count_col = n_anglers)
print(design)
```

## GLMM Effort Estimation

Call `estimate_effort_aerial_glmm()` with `time_col = time_of_flight`. The
default model fits a negative-binomial GLMM with a quadratic temporal effect
and a day-level random intercept:

```{r glmm-default}
glmm_result <- estimate_effort_aerial_glmm(design, time_col = time_of_flight)
print(glmm_result)
```

The model fits the diurnal count curve over all 48 observations, then
numerically integrates the predicted mean count across the full open-water
window (from 0.5 hours before the earliest flight to 0.5 hours after the
latest). The integrated area is scaled by `h_open / visibility_correction` to
convert counts to angler-hours.

## Variance and Confidence Intervals

Two variance methods are available.

**Delta method (default):** Propagates the fixed-effect covariance matrix from
`lme4::vcov()` to the derived integral via a gradient vector. This is fast
and analytic, and is the default when `boot = FALSE`.

**Parametric bootstrap:** `lme4::bootMer()` re-fits the model under parametric
resampling `nsim` times and uses the SD of the resulting totals as the SE.
This method can give more accurate CIs for skewed count distributions. Use it
for final production analyses; the delta method is appropriate for exploratory
work.

**Whichever method you choose, this vignette's design reports no interval at
all.** `creel_design()` above sets `visibility_correction = "none"`, which
declares that no detection-probability study was done. That is a statement of
ignorance, not a value of one: the correction's uncertainty is unknown, so it
cannot be propagated, and `se`, `ci_lower` and `ci_upper` all come back `NA`
rather than pretending the unknown component contributes zero.

To get an interval, say what the correction's uncertainty is — pass a numeric
`visibility_correction` together with `visibility_se` to `creel_design()`.
Declaring it known and exactly certain (`visibility_se = 0`) is also a valid
claim and does produce an interval; it is simply a different claim from having
never measured it.

```{r glmm-boot, eval = FALSE}
# Bootstrap CIs — use nboot = 500 for production analyses
glmm_boot <- estimate_effort_aerial_glmm(
  design,
  time_col = time_of_flight,
  boot = TRUE,
  nboot = 100L
)
print(glmm_boot)
```

## Downstream Estimation

`estimate_effort_aerial_glmm()` returns an effort estimate; it does not write
that estimate back into the design, and the total estimators do not take one as
an argument — `estimate_total_catch()` derives effort itself, from the counts
attached to whatever design it is given. So the GLMM figure is a standalone
result to read, report, or combine by hand, not an input the rest of the
pipeline picks up automatically.

What the downstream estimators need is a design carrying interviews. The
example below builds one from the complementary aerial interview data and
estimates catch rate and total catch from it. Note that this is a *different*
design object from the one fitted above: it uses `example_aerial_counts`, and
its effort comes from the standard aerial path rather than from the GLMM.

```{r downstream}
# Build a complementary design with matching interview dates
aerial_int_cal <- unique(example_aerial_counts[, c("date", "day_type")])
aerial_int_cal <- aerial_int_cal[order(aerial_int_cal$date), ]

design_int <- creel_design(
  aerial_int_cal,
  date        = date,
  strata      = day_type,
  survey_type = "aerial",
  visibility_correction = "none",
  angler_ratio = 1,
  angler_ratio_se = 0,
  h_open      = 14
)
design_int <- add_counts(design_int, example_aerial_counts)
design_int <- add_interviews(design_int, example_aerial_interviews,
  catch       = walleye_catch,
  effort      = hours_fished,
  trip_status = trip_status
)
catch_rate <- estimate_catch_rate(design_int)
print(catch_rate)

total_catch <- estimate_total_catch(design_int)
print(total_catch)
```

`example_aerial_interviews` carries no party-size column, so `add_interviews()`
cannot normalise effort to angler-hours and `estimate_total_catch()` warns that
the CPUE it multiplies is per *party*-hour while the count-derived effort is
angler-hours. The warning is correct for this dataset and is left visible rather
than suppressed: with one angler per party the total is right as printed, and
with larger parties it is **overstated** — by the mean party size, because the
party-hour rate is multiplied by an effort that already counts every angler.
Supplying `n_anglers = 2` on this example halves the total, from 250.6 to 125.3. Supply `n_anglers` from your own
interview data, or state a constant party size (`n_anglers = 1`) when every
interview really is a single angler, to remove the ambiguity.

## Comparison: Simple vs. GLMM Estimator

The two estimators need the counts at different grains, so they take different
designs built from the same data.

The GLMM consumes each overflight individually — the flight times are what it
fits the diurnal curve against, so the four flights a day must stay four rows.

The simple estimator does not model time. It treats each count as an
instantaneous estimate of the anglers present and expands by `h_open / v`. Four
flights on one day are therefore four looks at that day, not four sampled days:
they average to the day's mean occupancy, and the spread between them becomes
the within-day variance component. Naming `count_time_col` is what performs that
aggregation. Without it the four counts would be summed as separate sampling
units and the estimate would come back four times too large.

```{r comparison}
# Daily means for the simple estimator: the flights are four looks at each day,
# so they aggregate rather than accumulate.
design_daily <- add_counts(
  creel_design(
    aerial_cal,
    date        = date,
    strata      = day_type,
    survey_type = "aerial",
    visibility_correction = "none",
    angler_ratio = 1,
    angler_ratio_se = 0,
    h_open      = 14
  ),
  example_aerial_glmm_counts,
  count_col      = n_anglers,
  count_time_col = time_of_flight
)

# Simple aerial estimator — no diurnal correction
simple_result <- estimate_effort(design_daily)

# GLMM result from above
# glmm_result already computed

# Side-by-side comparison. Both estimators report a total across the sampled
# days, so the two numbers answer the same question and the gap between them is
# the diurnal correction.
comparison <- data.frame(
  method = c("GLMM", "Simple"),
  estimate = c(
    glmm_result$estimates$estimate,
    simple_result$estimates$estimate
  ),
  target = c(
    glmm_result$effort_target,
    simple_result$effort_target
  ),
  stringsAsFactors = FALSE
)

print(comparison)
```

The `target` column is worth checking rather than assuming. Both estimators
report `sampled_days`, so the two totals cover the same twelve days and the gap
between them is the diurnal correction and nothing else. The GLMM corrects for
the fact that all flights occurred at fixed hours (7, 10, 13, 16); the simple
estimator treats each count as representative of the full open-water window,
which inflates or deflates the total depending on where the peak falls in the
diurnal curve. Here it inflates, so the corrected estimate is the lower of the
two.

Neither estimator reports a confidence interval here, for the reason given under
"Variance and Confidence Intervals" above: this design declares
`visibility_correction = "none"`.

## Custom Formula

For surveys where a linear temporal term is sufficient — or where the analyst
prefers to control the model structure directly — pass a custom `formula`. The
formula must reference the actual count column (`n_anglers`) and the time
column by its exact name in the data.

```{r custom-formula}
glmm_linear <- estimate_effort_aerial_glmm(
  design,
  time_col = time_of_flight,
  formula  = n_anglers ~ time_of_flight + (1 | date)
)
print(glmm_linear)
```

A linear temporal term reduces flexibility but can improve stability when only
a few survey days are available. Use the default quadratic formula when you
have 8 or more survey days.

## References

- Askey, P. J., Ward, H., Godin, T., Boucher, M., & Northrup, S. (2018).
  Angler effort estimates from instantaneous aerial counts: use of
  high-frequency time-lapse camera data to inform model-based estimators.
  *North American Journal of Fisheries Management*, 38(1), 194–209.
  [https://doi.org/10.1002/nafm.10010](https://doi.org/10.1002/nafm.10010)

- 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.
