---
title: "Climatic niche geometry and workflow"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Climatic niche geometry and workflow}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

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

`climniche` compares current and projected climate at each location with one
current climatic niche reference. This vignette defines that reference, the
four reported quantities and the fitting and projection workflow.

## Current climatic niche reference

Let $c_i$ and $f_i$ denote current and projected climate at location $i$. A
non-negative weight $w_i$ determines how much that location contributes to the
current climatic niche reference. Its centre is

$$
\mu=\frac{\sum_i w_i c_i}{\sum_i w_i}.
$$

Distance is measured under a positive semidefinite climatic metric $A$:

$$
d_A(x,y)=\sqrt{(x-y)^\mathsf{T}A(x-y)}.
$$

If $r_{0i}=d_A(c_i,\mu)$, the weighted quantile

$$
B_q=Q_q^w(r_0)
$$

defines the empirical radial boundary. The transformation, metric, centre and
boundary are fitted from current climate and remain fixed when projected
conditions are evaluated.

## Reported quantities

At each location, `climniche` calculates

$$
\begin{aligned}
D_i &= d_A(f_i,c_i),\\
R_i &= d_A(f_i,\mu)-d_A(c_i,\mu),\\
C_i &= \sqrt{\max(0,D_i^2-R_i^2)},\\
E_i &= \max(0,d_A(f_i,\mu)-B_q).
\end{aligned}
$$

Climatic Displacement ($D_i$) is the magnitude of local climatic change. Niche
Distance Shift ($R_i$) is negative when projected climate moves towards the
reference centre and positive when it moves away. Climatic Reconfiguration
($C_i$) describes displacement that is not expressed as a change in distance
from the centre. The three quantities satisfy
$D_i^2=R_i^2+C_i^2$.

If $r_{1i}=d_A(f_i,\mu)$ and $\theta_i$ is the angle between the centred current
and projected vectors, then

$$
C_i^2=2r_{0i}r_{1i}\{1-\cos(\theta_i)\}.
$$

Climatic Reconfiguration is derived from Climatic Displacement and Niche
Distance Shift. It is not a strict orthogonal projection or an independently
estimated ecological process. Niche Boundary Exceedance ($E_i$) is separate
from this decomposition and measures projected distance beyond $B_q$.

## Workflow

Every input interface follows four steps.

1. Match current and projected climatic variables and locations, then convert
   occurrence, range or SDM data to non-negative reference weights.
2. Fit preprocessing and standardisation to current climate, construct the
   climatic metric and estimate the weighted niche centre and boundary.
3. Hold the fitted reference fixed while calculating the four quantities and
   climatic-variable contributions for projected conditions.
4. Return location-level values, weighted summaries, maps and reports from the
   same fitted reference.

`fit_climniche()` accepts matrices and data frames. `fit_climniche_raster()`
accepts `RasterLayer`, `RasterStack` and `RasterBrick` objects, whereas
`fit_climniche_terra()` accepts `SpatRaster` objects. The current reference can
also be fitted with `fit_climniche_reference()` and reused with
`project_climniche()`. `fit_climniche_series()` applies one fitted reference to
an ordered set of projections.

```{r}
library(climniche)

sim <- simulate_climniche(n = 200, p = 6, seed = 7)
fit <- fit_climniche(
  current = sim[["current"]],
  future = sim[["future_away"]],
  occupied = sim[["occupied"]],
  sensitivity = sim[["sensitivity"]]
)

head(climniche_table(fit))
```

## Interpreting equal climatic displacement

Four two-variable examples start one unit from a fixed centre. The towards and
away examples have the same Climatic Displacement but opposite Niche Distance
Shift. The fourth example changes climatic configuration while retaining the
same niche distance.

```{r}
current <- matrix(
  rep(c(1, 0), 4),
  ncol = 2,
  byrow = TRUE,
  dimnames = list(
    c("unchanged", "towards centre", "away from centre", "same niche distance"),
    c("climate 1", "climate 2")
  )
)
future <- rbind(
  unchanged = c(1, 0),
  `towards centre` = c(0, 0),
  `away from centre` = c(2, 0),
  `same niche distance` = c(0, 1)
)
colnames(future) <- colnames(current)

geometry_fit <- fit_climniche(
  current = current,
  future = future,
  occupied = rep(1, nrow(current)),
  center = c(0, 0),
  A = diag(2),
  scale = FALSE,
  preprocess = FALSE,
  boundary = 0.95,
  tolerance = 0
)

geometry_table <- data.frame(
  path = rownames(current),
  `Climatic Displacement` = geometry_fit[["climate_change_amount"]],
  `Niche Distance Shift` = geometry_fit[["niche_distance_change"]],
  `Climatic Reconfiguration` =
    geometry_fit[["climate_reconfiguration"]],
  `Niche Boundary Exceedance` =
    geometry_fit[["niche_boundary_exceedance"]],
  check.names = FALSE
)

knitr::kable(geometry_table, digits = 3)
```

The towards and away examples both have $D_i=1$, with $R_i=-1$ and $R_i=1$,
respectively. Moving to $(0,1)$ leaves niche distance unchanged, so $R_i=0$
and all displacement is represented by Climatic Reconfiguration. Only the away
example lies beyond the fitted boundary.

```{r}
identity_error <- with(
  geometry_fit,
  climate_change_amount^2 -
    niche_distance_change^2 -
    climate_reconfiguration^2
)
max(abs(identity_error))
```

## Using SDM outputs

Binary SDM output can define zero-one reference weights. Continuous suitability
values can remain continuous; `occupied_threshold` sets values at or below a
cutoff to zero without converting larger values to one. These weights define
how current locations contribute to the climatic niche reference. They are not
a projected suitability response, and the four `climniche` quantities remain
distances and positions in climatic space.
