---
title: "Contours and hydraulic-gradient arrows"
description: "Create contours and checked downgradient display symbols while keeping support and interpretation limits explicit."
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Contours and hydraulic-gradient arrows}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(
  collapse = TRUE, comment = "#>", fig.bg = "white",
  dev.args = list(bg = "white"), fig.width = 8, fig.height = 6,
  dpi = 144, fig.align = "center"
)
```

```{r surface}
library(potentiomap)
data("synthetic_wells")
points <- ps_make_points(
  synthetic_wells, "x", "y", "gw_elevation", "well_id", "EPSG:26916"
)
aoi <- ps_sample_aoi()
surface <- suppressWarnings(ps_interpolate(
  points, methods = "TPS", grid_res = 150, mask = aoi, padding = 0
)$TPS)
head_cols <- hcl.colors(64, "RdYlBu", rev = TRUE)
```

The default contour result is a line `SpatVector`. A structured result adds an
inventory of every requested level.

```{r contours}
contours <- ps_contours(
  surface, interval = 1, return = "result"
)
contours$manifest
```

The one-unit interval produces enough lines to show the TPS surface clearly.
Use `levels = c(...)` when specific regulatory or interpretive elevations are
required. Explicit levels outside the finite surface range remain in the
manifest and produce a classed warning. Open lines are not silently closed or
converted to polygons.

```{r contour-map, fig.alt="Synthetic thin-plate-spline potentiometric surface shaded blue at lower heads and red at higher heads, with one-unit dark contours and groundwater observation wells."}
par(bg = "white")
terra::plot(
  surface, col = head_cols,
  main = "TPS surface with one-unit contours"
)
terra::plot(contours$contours, add = TRUE, col = "#263845", lwd = 1.2)
terra::plot(points, add = TRUE, pch = 21, bg = "white", cex = 0.75)
```

Individual contour lines can be divided where local support changes.

```{r contour-support}
support <- ps_prediction_support(points, surface = surface)
classified <- suppressWarnings(ps_contour_support(
  contours$contours, support = support,
  supported_distance = 500, approximate_distance = 1200,
  require_inside_hull = TRUE
))
classified$summary
```

```{r contour-support-map, fig.width=8, fig.height=6, fig.alt="Synthetic potentiometric contours divided into green solid supported sections and orange dashed approximate sections, with groundwater observation wells shown as points."}
support_colors <- c(supported = "#176b4d", approximate = "#c56a00")
support_lty <- c(supported = 1, approximate = 2)
segment_values <- terra::values(classified$segments)

par(bg = "white", fg = "#263845")
terra::plot(
  aoi, col = "#f7f5ef", border = "#8b969c",
  main = "Contour support from user-defined criteria"
)
for (support_class in names(support_lty)) {
  keep <- segment_values$support_class == support_class
  if (!any(keep)) next
  terra::plot(
    classified$segments[keep], add = TRUE,
    col = support_colors[support_class],
    lty = support_lty[support_class], lwd = 2
  )
}
terra::plot(points, add = TRUE, pch = 21, bg = "white", cex = 0.7)
```

Solid green sections are supported; dashed orange sections are approximate.

The `line_type` field suggests solid, dashed, or dotted symbology while keeping
the support class and reason as GIS attributes. The classes use thresholds
chosen by the user, not confidence intervals. Proximity to wells does not prove
that a contour is correct, distance does not prove that it is wrong, and every
section remains an interpolation from the modeled surface. Coarse support cells
can shift the apparent solid-to-dashed transition.

The [contour-support threshold comparison](https://el-cordero.github.io/potentiomap/articles/contour-support-thresholds.html)
applies tight, balanced, broad, and network-relative criteria to the same
modeled contours so their effects can be compared directly.

Hydraulic-gradient arrows point along the local negative modeled-head gradient.
The following deliberately uses short, sparse display symbols.

```{r arrows}
flow <- ps_flow_arrows(
  surface, scale = 25, res_factor = 8, endpoint_action = "shorten"
)
flow$validation_summary
head(flow$validation)
```

`endpoint_action = "flag"` preserves failing lines and reports them.
`"shorten"` repeatedly halves their length along the original direction.
`"drop"` removes failures. `"none"` retains the version 0.1.0 unvalidated
geometry for comparison. No policy reverses or bends an arrow.

```{r arrow-map, fig.alt="Synthetic TPS potentiometric surface with one-unit contours, groundwater observation wells, and short sparse arrows pointing toward decreasing modeled head."}
bases <- ps_arrow_vertices(flow$arrows, "first")
tips <- ps_arrow_vertices(flow$arrows, "last")
b <- terra::crds(bases)
t <- terra::crds(tips)

par(bg = "white")
terra::plot(
  surface, col = head_cols,
  main = "Inferred negative-gradient direction"
)
terra::plot(contours$contours, add = TRUE, col = "#52646d", lwd = 0.9)
arrows(
  b[, 1], b[, 2], t[, 1], t[, 2],
  length = 0.06, col = "#111111"
)
terra::plot(points, add = TRUE, pch = 21, bg = "white", cex = 0.65)
```

Arrow length is a cartographic convention based on gradient, cell size, and the
chosen scale. The line is not a velocity, travel time, particle path, or traced
groundwater path. Endpoint checks only evaluate a straight symbol against the
supplied raster; they do not prove that the interpolated surface is physically
correct.
