---
title: "Custom metrics and process groups"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Custom metrics and process groups}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4)
```

```{r setup, message = FALSE}
library(blueterra)
library(terra)
```

Many terrain analyses use project-specific variables. `blueterra` supports
custom raster metrics when they share the same grid geometry as the metric stack
they extend. Custom process groups then document how those layers should be
organized and interpreted.

## Base Metrics

```{r base}
bathy <- read_bathy(blueterra_example("hitw"))
prepared <- prepare_bathy(bathy, depth_range = c(-220, -25), smooth = TRUE)

metrics <- derive_terrain(
  prepared,
  metrics = c("slope", "tri", "bpi", "curvature")
)

names(metrics)
```

## Expression-Based Metrics

Quoted expressions are evaluated with raster layers available by name. Character
strings are not evaluated as code.

```{r expression}
slope_tri <- derive_custom_metric(
  metrics,
  name = "slope_tri_index",
  expression = quote(slope_deg * tri)
)

slope_tri
```

## Function-Based Metrics

Functions are useful when a metric requires several operations or parameters.
The function must return a single-layer `terra::SpatRaster` on the same grid.

```{r function}
relief_position <- derive_custom_metric(
  metrics,
  name = "relief_position_index",
  fun = function(r, bpi_weight = 2) {
    out <- r[["tri"]] + bpi_weight * abs(r[["bpi_3x3"]])
    names(out) <- "relief_position_index"
    out
  },
  bpi_weight = 1.5
)

relief_position
```

## Add Layers to a Stack

```{r add}
extended <- add_metric_layers(metrics, slope_tri, relief_position)
names(extended)
```

`add_metric_layers()` checks CRS, extent, resolution, dimensions, and origin by
default. This protects later summaries and PCA tables from mixing non-aligned
rasters.

```{r custom-map, eval=requireNamespace("ggplot2", quietly = TRUE), fig.alt="Custom slope-TRI metric over hillshaded bathymetry."}
plot_metric(
  extended,
  "slope_tri_index",
  bathy = prepared,
  contours = TRUE,
  contour_interval = 25,
  title = "Custom Slope-TRI Index"
)
```

## Custom Catalog Rows

Catalog rows explain what a metric is, how it was produced, which group it
belongs to, and how it should be interpreted.

```{r catalog}
custom_rows <- create_metric_catalog(
  metric = c("slope_tri_index", "relief_position_index"),
  label = c("Slope-TRI index", "Relief-position index"),
  process_group = c("custom_relief", "custom_relief"),
  description = c(
    "Product of slope and terrain ruggedness index.",
    "TRI plus weighted absolute fine-scale BPI."
  ),
  units = c("index", "index"),
  source_function = c("derive_custom_metric", "derive_custom_metric"),
  scale_sensitive = c(TRUE, TRUE),
  interpretation_notes = c(
    "Example composite terrain-form index; choose only when it follows the analysis design.",
    "Example relief-position index; weights should be justified before inference."
  )
)

custom_catalog <- extend_metric_catalog(metric_catalog(), custom_rows)
validate_metric_catalog(custom_catalog)
custom_rows
```

## Assign Custom Process Groups

```{r assign}
assign_process_groups(extended, catalog = custom_catalog)
summarize_process_groups(extended, catalog = custom_catalog)
select_process_representatives(
  catalog = custom_catalog,
  metrics_available = names(extended),
  representatives = c(custom_relief = "relief_position_index")
)
```

Custom group names should describe terrain form or analysis design. They should
not imply a direct ecological or oceanographic process unless that link is
validated independently.

## Rename External Layers

Imported rasters often arrive with names that are readable to the analyst but
not stable enough for programmatic grouping.

```{r rename}
standardize_metric_names(c("Slope degrees", "Fine BPI", "Relief position index"))
rename_metric_layers(
  c("slope old", "bpi old"),
  c("slope old" = "slope_deg", "bpi old" = "bpi_3x3")
)
```
