Skip to content

Latest commit

 

History

History
125 lines (86 loc) · 7.22 KB

File metadata and controls

125 lines (86 loc) · 7.22 KB

fresh fresh hex sticker

Freshwater Referenced Spatial Hydrology

A composable stream network modelling engine. Query and extract stream networks, classify habitat by gradient and channel width, segment networks at barriers and break points, aggregate features upstream or downstream, and run multi-species habitat modelling with parallel workers. Supports custom model outputs and attribute joining — channel width, mean annual discharge, precipitation, or any scalar value. Currently running on BC's Freshwater Atlas, designed to model fish habitat, connectivity, water temperature, channel morphology, and custom attributes for any species or question on any stream network.

Installation

pak::pak("NewGraphEnvironment/fresh")

Prerequisites

PostgreSQL with fwapg loaded. A local Docker setup is included:

cd docker
docker compose up -d db
docker compose run --rm loader

See docker/README.md for details. Connection via frs_db_conn() or direct DBI::dbConnect().

Example

Segment a stream network at gradient barriers and classify habitat for multiple species:

library(fresh)

conn <- DBI::dbConnect(RPostgres::Postgres(),
  host = "localhost", port = 5432,
  dbname = "fwapg", user = "postgres", password = "postgres")

# 1. Generate gradient access barriers
frs_break_find(conn, "working.tmp",
  attribute = "gradient", threshold = 0.15,
  to = "working.barriers_15")

# 2. Segment network at barriers + falls
frs_network_segment(conn, aoi = "BULK",
  to = "fresh.streams",
  break_sources = list(
    list(table = "working.barriers_15", label = "gradient_1500"),
    list(table = "working.falls", label = "blocked")))

# 3. Classify habitat per species
frs_habitat_classify(conn,
  table = "fresh.streams",
  to = "fresh.streams_habitat",
  species = c("CO", "BT", "ST"))

Or use the orchestrator for multi-WSG runs with any AOI:

# Province-wide with parallel workers
frs_habitat(conn, c("BULK", "MORR", "ZYMO"),
  to_streams = "fresh.streams",
  to_habitat = "fresh.streams_habitat",
  workers = 4, password = "postgres",
  break_sources = list(
    list(table = "working.falls", label = "blocked")))

# Custom AOI — sub-basin, territory, or any spatial extent
frs_habitat(conn,
  aoi = "wscode_ltree <@ '100.190442'::ltree",
  species = c("BT", "CO"),
  label = "richfield",
  to_streams = "fresh.streams",
  to_habitat = "fresh.streams_habitat")

See the pkgdown site for vignettes and function reference.

Primitives vs domain wrappers

fresh has two layers:

Generic primitives — segment / classify / cluster on the network: frs_classify(), frs_break_find(), frs_break_apply(), frs_network_segment(), frs_barriers_minimal(), frs_col_generate(), frs_col_join(), frs_aggregate(), frs_cluster(). Plus FWA-spanning operations on arbitrary point datasets: frs_network_features() (per-segment feature arrays for any FWA-snapped points), frs_point_match() (match two point datasets along the network within in-stream distance), frs_candidates_pick() (score + filter + dedup, e.g. multi-stream PSCIS-to-stream selection), frs_trace_downstream() (FWA-averaged gradient with localized-barrier handling), frs_wsg_drainage() (watershed-group drainage-closure primitive). Domain-neutral building blocks. Classify on any attribute into any label column. Break at any position. Cluster by any connectivity rule. No assumptions about what's being modelled.

Fish-habitat wrapperfrs_habitat_classify(), frs_habitat(), frs_params(). Convenience wrappers that classify segments as accessible / spawning / rearing / lake_rearing per species_code, driven by a per-species rules YAML. The output schema is fish-specific.

For non-fish domains — thermal refugia, riparian connectivity, sediment-transport models, custom classifications — compose the primitives directly on your own output schema. frs_classify() is pipeable and takes any label column name. The wrappers are a convenience, not the only way in.

Using with link

For fish habitat and fish passage work, fresh pairs with link. link interprets features — crossings, observations, falls, user-definite barriers, habitat confirmations — and produces the two inputs that drive frs_habitat_classify():

  • Break sources (via lnk_source() and the per-table specs fresh consumes).
  • Barrier overrides — a per-species skip list produced by lnk_barrier_overrides(). Passed to frs_habitat_classify(barrier_overrides = "...").

link's lnk_pipeline_*() helpers wrap the six-phase network-modelling flow end-to-end for a given config bundle, reproducing bcfishpass's classification method on BC's Freshwater Atlas. See link's Reproducing bcfishpass vignette for a worked example.

Fresh still runs standalone on any break sources you construct yourself — link is a convenient on-ramp, not a requirement.

Ecosystem

Package Role
fresh Stream network modelling engine (this package) — segment, classify, cluster, aggregate
link Feature-to-network interpretation — load + validate override CSVs, score and prioritize crossings, build per-species barrier skip lists, orchestrate bcfishpass-reproducing pipelines
flooded Delineate floodplain extents from DEMs and stream networks
drift Track land cover change within floodplains over time

Pipelines:

  • Fish habitat: link → fresh
  • Land cover change: fresh (network) → flooded (floodplains) → drift (land cover change)

Roadmap

fresh is currently FWA-grounded; the architecture is intentionally network-agnostic and active design work targets generalization beyond BC's Freshwater Atlas:

  • spyda — a stream-network topology engine for non-FWA networks (LiDAR-derived hydrography, OSM-derived networks, custom watershed delineations); design placeholder (#41)
  • Configurable column names via options() so the primitives work against networks that don't use FWA's blue_line_key / wscode_ltree / downstream_route_measure conventions (#44)
  • MAD predicate Phase 2 in the rule grammar — extend habitat rules to incorporate mean annual discharge (#114)
  • Channel-width estimation from bankfull regression for small-order streams (#28, #29)

The primitives layer is designed so that swapping the underlying network (FWA → spyda) does not require re-implementing the modelling logic — only the network-aware indexing and topology adapters change.

License

MIT