# Climatic niche geometry and workflow

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

    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))
    #>   cell occupied_weight current_niche_distance future_niche_distance
    #> 1    3               1               1.859389              2.270787
    #> 2    4               1               1.537120              1.933105
    #> 3    6               1               2.031325              2.430480
    #> 4    9               1               1.598781              1.990040
    #> 5   17               1               1.944319              2.357542
    #> 6   18               1               1.535579              1.947860
    #>   climate_change_amount niche_distance_change climate_reconfiguration
    #> 1             0.4139168             0.4113980              0.04559401
    #> 2             0.3988632             0.3959852              0.04782874
    #> 3             0.4002497             0.3991546              0.02958838
    #> 4             0.3939472             0.3912592              0.04594166
    #> 5             0.4155508             0.4132233              0.04392064
    #> 6             0.4143586             0.4122815              0.04143662
    #>   composition_change change_alignment niche_boundary_exceedance
    #> 1         0.04559401        0.9939147                0.36874556
    #> 2         0.04782874        0.9927844                0.03106366
    #> 3         0.02958838        0.9972638                0.52843849
    #> 4         0.04594166        0.9931767                0.08799862
    #> 5         0.04392064        0.9943989                0.45550087
    #> 6         0.04143662        0.9949873                0.04581913
    #>   outside_niche_exceedance current_niche_percentile future_niche_percentile
    #> 1               0.36874556                0.8500000               1.0000000
    #> 2               0.03106366                0.4166667               0.9666667
    #> 3               0.52843849                1.0000000               1.0000000
    #> 4               0.08799862                0.5166667               0.9833333
    #> 5               0.45550087                0.9833333               1.0000000
    #> 6               0.04581913                0.4000000               0.9833333
    #>   percentile_change                radial_direction
    #> 1        0.15000000 Away from realised niche centre
    #> 2        0.55000000 Away from realised niche centre
    #> 3        0.00000000 Away from realised niche centre
    #> 4        0.46666667    Limited Niche Distance Shift
    #> 5        0.01666667 Away from realised niche centre
    #> 6        0.58333333 Away from realised niche centre
    #>                   boundary_status
    #> 1 Beyond empirical niche boundary
    #> 2 Beyond empirical niche boundary
    #> 3 Beyond empirical niche boundary
    #> 4 Beyond empirical niche boundary
    #> 5 Beyond empirical niche boundary
    #> 6 Beyond empirical niche boundary

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

    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)

path | Climatic Displacement | Niche Distance Shift | Climatic Reconfiguration | Niche Boundary Exceedance
--- | --- | --- | --- | ---
unchanged | 0.000 | 0 | 0.000 | 0
towards centre | 1.000 | -1 | 0.000 | 0
away from centre | 1.000 | 1 | 0.000 | 1
same niche distance | 1.414 | 0 | 1.414 | 0

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.

    identity_error <- with(
      geometry_fit,
      climate_change_amount^2 -
        niche_distance_change^2 -
        climate_reconfiguration^2
    )
    max(abs(identity_error))
    #> [1] 0

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