fresh 0.27.5
channel_width_min_bypass block schema change: replace single stream_order key with stream_order_min + stream_order_max to express child-order ranges. The single key was modeling only stream_order = 1 (bcfp parity), but frs_order_child always supported a range — child_order_min / child_order_max parameters. The new pair maps directly. Validator rejects the legacy stream_order key with unknown keys to flag stale rules.yaml files for regeneration.
Unblocks link’s dimensions.csv::rear_stream_order_child_min / rear_stream_order_child_max columns. No behaviour change for frs_order_child itself; this is a YAML schema rename only.
fresh 0.27.4
Allow optional distance_max key inside channel_width_min_bypass block in rules YAML — link uses it to cap the bypass to the lower N metres of each direct-trib BLK (i.e. frs_order_child(distance_max = N)). Validator accepts a positive numeric scalar; rejects zero / negative / non-numeric values.
fresh 0.27.3
Restore stream_order_max predicate in frs_order_child() — the previous patch (0.27.2) removed the s.stream_order = s.stream_order_max filter because the column doesn’t exist on fresh.streams. That was the wrong fix: the predicate is load-bearing for direct-child semantics. Without it, multi-order BLKs like a named creek that grows from order-1 headwaters to order-3 mouth (e.g. Divan Creek, BLK 356353593 in HORS) get their order-1 headwater segments credited as direct trib mouths of large rivers — they are not.
Fix: derive stream_order_max per BLK on the fly via MAX(stream_order) OVER (PARTITION BY blue_line_key) in a CTE, then apply s.stream_order = s.stream_order_max to bound the bypass to mouth-side reaches only. Same answer as bcfishpass.streams.stream_order_max (which is a stored column there). Verified against HORS BT — eliminates ~95 km of spurious credit on multi-order BLK headwaters.
fresh 0.27.2
Fix frs_order_child() SQL referencing nonexistent column s.stream_order_max. fresh.streams has stream_order and stream_order_parent from FWA but no stream_order_max — the 0.27.0 SQL failed at execution against any real database (only the SQL-shape unit tests passed, and they asserted the broken predicate). Drop the broken predicate; default both child_order_min and child_order_max to 1L when neither is set, which matches bcfishpass’s hardcoded stream_order = 1 predicate exactly. Caller-passed bounds still apply unchanged.
Adds a regression test that scans the emitted SQL across every parameter combination for stream_order_max and fails if it reappears.
fresh 0.27.1
Patch follow-up to 0.27.0. .frs_load_rules validator now accepts the channel_width_min_bypass predicate that link emits to drive frs_order_child() post-classify (link#96 wiring). Validates that the field is a named mapping with stream_order and stream_order_parent_min integer scalars. Without this patch, link’s emitted rules.yaml fails fast at frs_params() with unknown predicates: channel_width_min_bypass before any classification runs.
fresh 0.27.0
Closes #158. New frs_order_child(): post-classification UPDATE that credits direct order-1 (or other) tributaries of order-N+ rivers with <label> = TRUE. Captures the bcfishpass rearing bypass currently hard-coded in model/02_habitat_linear/sql/load_habitat_linear_<sp>.sql for BT/CH/CO/ST/WCT — predicate cw.channel_width >= rear_channel_width_min OR (s.stream_order_parent >= 5 AND s.stream_order = 1).
- Parametric:
parent_order_min(default5Lmatches bcfp),child_order_min/max,distance_max. Generic on label column (rearing/lake_rearing/wetland_rearing). - Idempotent + additive:
<label> IS NOT TRUEguard. Never adds rearing above an inaccessible barrier:accessible = TRUEguard. - No existing-caller behaviour change. Wiring into link’s pipeline is a follow-up PR (link side:
lnk_pipeline_classifycallsfrs_order_childpost-classify per-species, gated ondimensions.csv::rear_stream_order_bypass).
fresh 0.26.0
Closes #191. .frs_connected_waterbody Phase 2 (upstream spawn) cluster + lake-adjacency gate is now opt-out via a lake_adjacent parameter on the rule. Default TRUE keeps bcfishpass-parity behaviour byte-identical for any caller not setting the knob; passing lake_adjacent: no in <sp>.spawn_connected.lake_adjacent runs the relaxed Phase 2 that credits any spawn-eligible segment upstream of and accessible from a qualifying rearing waterbody. Used by callers that need to credit upstream tributary reaches not in lake-adjacent clusters (e.g. NewGraph default-bundle SK methodology — link#87). .frs_load_rules validator updated to accept the new key.
Cross-host testing pattern (m4 ↔︎ m1 over Tailscale, with Sys.setenv() overrides for PG_*_SHARE to point fresh tests at a host’s local Docker fwapg when the bcfp tunnel is unavailable) documented in CLAUDE.md “Testing on alternate hosts.”
fresh 0.25.0
Two bcfishpass-parity fixes surfaced during link’s WSG-coverage expansion (link’s MORR + KISP runs, 2026-04-30).
frs_cluster phase-1 + confluence-boost interaction (#186). .frs_cluster_both previously excluded on-spawning rearing segments from the clustering CTE. Removing those segments could shift cluster_minimums into the confluence-boost zone (DRM < confluence_m) and validate clusters that bridge_gradient should deny. bcfishpass’s path-2 keeps on-spawning segments in clusters, so its cluster_min stays high and the confluence boost doesn’t fire. Phase-1 protection now lives only in the final UPDATE step — on-spawning segments retain label_cluster = TRUE regardless of cluster outcome, but the cluster boundaries used for phase-2 / phase-3 testing are unchanged. Reproduction case: MORR ST cluster 502 (Nado Creek + tributary 360704379, 12.58% gradient between trib confluence and downstream spawning) — bcfp denies the trib’s rearing-eligible segments; fresh now matches.
.frs_trace_downstream averaged FWA gradient (#187). .frs_trace_downstream previously used whse_basemapping.fwa_downstreamtrace, an iterator returning FWA-original linear_feature_id rows with feature-averaged gradients. Localized barriers on a sub-piece of a long FWA feature (e.g. a 7 m, 84% lake-outlet drop inside an otherwise flat 3 km feature) are invisible to that approach. Switched to a FWA_Downstream predicate join against the broken streams table arg — same pattern .frs_cluster_both phase-3 already uses. Localized gradients produced by frs_network_segment() are now visible to the trace. New required columns in origins_sql: wscode_ltree, localcode_ltree (.frs_connected_waterbody is the only caller; updated). Reproduction case: KISP SK at Kitwancool Lake — bcfp’s model/02_habitat_linear/sql/load_habitat_linear_sk.sql correctly stops at the lake-outlet drop; fresh now matches.
Both fixes tighten link’s bcfishpass-config parity rollup. No API changes for end users; .frs_trace_downstream is an internal helper. NEWS bump from 0.24.1 → 0.25.0 because behaviour changes are observable in frs_habitat_classify / frs_cluster / frs_habitat outputs for any caller running connectivity or cluster-aware classifications.
fresh 0.24.1
Refresh bundled inst/extdata/parameters_habitat_rules.yaml to match link’s current default-bundle output. The bundled sample had drifted to pre-in_waterbody / pre-area_only shape (last synced 2026-04-12) while link’s bundles evolved through three #69 phases. Sync recovers the canonical sample for any frs_habitat() caller using fresh’s bundled defaults.
Reframe vignettes/habitat-pipeline.Rmd as a primitives walkthrough. Header note up front directs production users to frs_habitat() and link’s rule-based pipeline; vignette body unchanged — primitives are still useful for debugging, custom pipelines, and understanding what frs_habitat() does internally.
No behaviour change to any exported function.
fresh 0.24.0
Add area_only: true flag to rule grammar — decouples bucket-flag derivation from the main rear predicate (#182).
Today, a waterbody_type: L or W rule placed in a species’ rear: list does two things at once: triggers fresh to derive a lake_rearing / wetland_rearing predicate (the per-segment flag that drives lake_rearing_ha / wetland_rearing_ha area rollups), AND OR’s into the main rear predicate (the per-segment rearing flag, which drives linear rearing_km). The two effects are coupled, so a user can’t say “credit the polygon area but exclude polygon-mainlines from linear km.”
This release adds area_only: true on a rule. When set, fresh uses the rule’s predicate to derive the corresponding bucket flag (lake_rearing or wetland_rearing) but does not include the rule in the OR-chain that builds the main rear predicate. Stream-edge rules (with in_waterbody: false from #180) decide what counts as linear; L/W rules with area_only: true decide what polygons contribute area without double-counting the polygon-mainline as linear.
-
area_only: trueon awaterbody_type: L|Wrule → rule contributes tolake_rear/wetland_rearpredicate derivation only, excluded from mainrearpredicate. -
area_only: falseor absent → today’s behaviour (rule contributes to both). Backward compatible. - Validator: requires
waterbody_type: LorW(no bucket flag to drive on stream-edge rules orwaterbody_type: R); rejects non-logical / NA / length>1 values. - 13 new tests across
test-frs_params.R(validator) andtest-frs_habitat_predicates.R(decoupling). 167 PASS in those files (was 154). Full suite green.
Coordinates with link#69 phase 2 — lnk_rules_build() will emit area_only: true on L/W polygon rule blocks driven by per-species rear_lake_area_only / rear_wetland_area_only columns in dimensions.csv. Combined with the polygon-rule edge_types_explicit: [1000, 1100] filter (mainlines only), this expresses the use-case-2 model: linear excludes polygon-mainlines, area still rolls up.
fresh 0.23.1
Hotfix on top of 0.23.0 — register in_waterbody with the rules-YAML validator so emitted rules pass loading.
.frs_load_rules() validates each rule entry’s keys against an allowlist of known predicates (valid_predicates in R/frs_params.R). 0.23.0 added the predicate at the SQL emission layer (.frs_rule_to_sql()) but missed adding it to the allowlist, so a rules YAML carrying in_waterbody: was rejected by .frs_load_rules() before SQL emission ever ran. This release adds in_waterbody to valid_predicates.
- 1 new test:
.frs_load_rulesaccepts a YAML within_waterbody: true|falseon edge-type and waterbody-type rules (113 PASS intest-frs_params.R, was 112). - No behaviour change beyond unblocking the predicate that 0.23.0 introduced.
fresh 0.23.0
Add in_waterbody boolean predicate to the rule grammar (#180).
The rule grammar in parameters_habitat_rules.yaml already had positive predicates for selecting segments inside polygon waterbodies (waterbody_type: R/L/W) but no symmetric way to restrict a rule to segments outside any waterbody. That left the stream-edge rule families silent on polygon membership, so a stream rule like edge_types_explicit: [1000, 1100, 2000, 2300] matched both true streams and the same-coded centerlines that thread through wetland/lake/river polygons — overlapping with the polygon rules on those segments and double-classifying.
This release adds in_waterbody: false | true as the natural complement to waterbody_type:. Stream-edge rules can now express “this classification only applies outside polygon footprints,” which is the semantic complement of the existing polygon rules. Together the two predicates partition the network into stream-edge classifications and polygon-classifications with no overlap.
-
in_waterbody: false→ addss.waterbody_key IS NULLto the rule’s AND chain. -
in_waterbody: true→ addss.waterbody_key IS NOT NULL. -
absent → no constraint (pre-
in_waterbodybehaviour, backward compatible). - Composes with
waterbody_type: <letter>— the positive predicate already impliesIS NOT NULL, so the two together are redundant rather than contradictory. - Validation: non-logical / NA / length>1 values raise an error at
.frs_rule_to_sql()time. - 5 new tests under
test-frs_params.R(112 in that file, was 107). Full suite green.
Coordinates with link#69 — lnk_rules_build() will emit in_waterbody: false on stream-edge rule blocks once this lands.
fresh 0.22.0
frs_habitat_overlay() simplified — drop format and long_value_col parameters; accept only the canonical source-table shape (#177).
-
Canonical shape: one row per (segment × species), with join keys in
by, the species code inspecies_col(default"species_code"), and one indicator column per habitat type. Indicator coercion accepts integer 1, text'true'/'t'/'1'(case + whitespace insensitive), boolean. -
Dropped paths (breaking, pre-1.0):
format = "wide"per-species-suffix layout (spawning_sk,rearing_sk) andformat = "long"(habitat_typerows +habitat_indindicator). Neither had current production consumers — the wide-suffix layout was scoped for direct reads ofbcfishpass.streams_habitat_known(never integrated); the long format was link’s read of bcfishpass’s pre-2026-04-26 CSV (bcfishpass moved to a different shape on 2026-04-26). -
New parameter:
species_col(default"species_code"). Was added in PR #176’s first attempt as an additive bolt-on; this release lands it as the only path. -
Non-canonical sources: transform first via a SQL view, R pivot, or upstream adapter (e.g., link’s forthcoming
lnk_ingest_bcfishpass()), then call overlay against the canonical-shape view. Shape-translation lives with the consumer; fresh stays a thin SQL adapter. -
Bridge mode (
bridge = NULL) unchanged — orthogonal to source shape. - Tests: dropped wide-suffix and long-format paths; canonical-shape integration tests exercise integer + text + boolean indicators, additive guard, custom
species_col, customby, and bridge mode.
Coordinated link release (0.12.0) updates the call site in lnk_pipeline_classify.
fresh 0.21.0
frs_habitat_overlay() rename + 3-way bridge join. Pre-1.0 cleanup driven by review of v0.20.0; no deprecation alias.
-
Param renames (breaking):
-
table→to(matches fresh’s “destination” convention) -
known→from(matches “source” convention; also detaches from the “known habitat” provenance framing — function is the abstract overlay mechanism)
-
-
New
bridge = NULLparameter for 3-way joins. When supplied, the SQL becomesto ← bridge ← fromwith range containment:bridge.downstream_route_measure >= from.downstream_route_measure AND bridge.upstream_route_measure <= from.upstream_route_measure. Letstotables keyed byid_segment(e.g.fresh.streams_habitat) overlay from sources keyed by(blue_line_key, drm)ranges. -
Range columns auto-stripped from
byin bridge mode —downstream_route_measure/upstream_route_measureare handled by the range predicates, so they don’t need to be (and shouldn’t be) in the equalitybyclause. -
frs_habitat_classify()’sknown = NULLshortcut dropped (also breaking). Convenience hid configurability now that overlay has format + bridge knobs. Caller pattern is now explicitly two-step:frs_habitat_classify()thenfrs_habitat_overlay(). - Reads as a sentence:
frs_habitat_overlay(from = X, to = Y, bridge = Z)— “overlay flags from X to Y via this bridge.” Domain-agnostic — bridge isn’t required to befresh.streams; any segments table providingid_segment+ range columns works (lake centerlines, wetland shorelines, future cottonwood-pinned segmentations).
Surfaced in link#55 when fresh.streams_habitat (keyed by id_segment only) needed overlay from user_habitat_classification (keyed by (blue_line_key, drm) with range [drm, urm]).
Tests: 52 (was 43 in v0.20.0) — added bridge schema validation, SQL shape verification, range-containment integration test on a 3-segment fixture.
fresh 0.20.0
frs_habitat_overlay() accepts long-format known-habitat tables in addition to the original wide-format. Backward-compatible: the new format arg defaults to "wide".
- New args:
format = c("wide", "long")andlong_value_col = "habitat_ind". - Long-format expects: join keys (per
by) +species_code+habitat_type+ a boolean / text indicator column. Each row is one (segment × species × habitat_type) tuple. Matches link’suser_habitat_classification.csvshape, which is the bcfishpassuser_habitat_classification.csvsource format (before bcfishpass pivots it to wide forstreams_habitat_known). - Indicator accepts boolean OR text —
'TRUE'/'t'/'true'all qualify. Non-matching values (e.g.'FALSE', NULL) skip. - Up-front validation: long-format known table must have all required columns (join keys +
species_code+habitat_type+ indicator). Errors clearly if missing. - Wide-format path unchanged. 11 new tests covering long-format unit + integration; previous wide tests all still pass (42 total on
frs_habitat_overlay).
Surfaced in link#55 — link’s user_habitat_classification table is loaded long-format by lnk_pipeline_prepare. Forcing a pivot in link to satisfy the wide-format assumption was special-case overhead. Now fresh handles either shape so callers use whichever they have.
fresh 0.19.0
Decompose frs_habitat_classify() into composable pieces; rename frs_habitat_known() → frs_habitat_overlay(). Pre-1.0, breaking changes accepted to set the right shape before external use.
- New exported
frs_habitat_predicates(sp_params)— pure-R helper that takes a single species’ params and returns a named list of SQL boolean predicates (spawn,rear,lake_rear,wetland_rear) ready to embed inCASE WHEN <pred> THEN TRUE ELSE FALSE END. Extracted fromfrs_habitat_classify()’s per-species loop. Testable without a DB; 33 unit tests cover both rules-YAML and CSV-ranges paths, edge_type filters, and the lake/wetland W-rule gating. -
frs_habitat_classify()shrinks from 551 → ~447 lines and gains aknown = NULLparameter — when supplied, callsfrs_habitat_overlay()automatically as a post-step. Rule-based classification + observation overlay can now be a single call. -
Breaking:
frs_habitat_known()renamed tofrs_habitat_overlay(). The mechanism is overlay (additive OR-in); “known” presupposed provenance the function doesn’t enforce. No deprecation alias — pre-1.0, no external users.
fresh 0.18.0
- New
frs_habitat_overlay()— stitches known-habitat boolean flags from a wide-format lookup table INTO an existingstreams_habitatclassified byfrs_habitat_classify(). Mirrors the bcfishpass blend of model output (habitat_linear_<sp>) with manual / observation-based knowns (streams_habitat_known) into the publishedstreams_habitat_linearinteger encoding. Purely additive (FALSE → TRUEonly). Per-species columns{habitat}_{species_lower}matched against the species code instreams_habitat; missing per-species columns skip with a verbose message rather than erroring. Compound join key parameterisable viaby(defaultc("blue_line_key", "downstream_route_measure")). Surfaced as a need in link#55 — link’s pipeline loadeduser_habitat_classification.csvfor break-points + barrier overrides but never propagated its flags intofresh.streams_habitat. After this lands, link’slnk_pipeline_classifycan callfrs_habitat_overlay()as a post-step.
fresh 0.17.1
- Params validator accepts
wetland_ha_minpredicate on rear rules withwaterbody_type: W— mirrors the existinglake_ha_min/waterbody_type: Lrule. The classifier in 0.17.0 already readwetland_ha_minfrom rear rules to filter the fwa_wetlands_poly join; the validator predicate allowlist hadn’t been extended so rules YAML with the key was rejected. Surfaced in link#51 whenlnk_rules_build()started emittingwaterbody_type: Wrules with the threshold.
fresh 0.17.0
-
frs_habitat_classify()now honours thewaterbody_type: L/waterbody_type: Wentries under a species’rear:rules in the rules YAML. Previously thelake_rearing/wetland_rearingbooleans were set whenever a segment fell in the species’ rear channel-width window and matched a lake/wetlandwaterbody_key— regardless of whether the species had a lake/wetland rear rule declared. Now the flag isFALSEunless the species has the matching rule, and the rule’s optionallake_ha_min/wetland_ha_minthreshold filters the polygon join to exclude waterbodies below that area (#165). Surfaced in link#51 when every species in link’s bcfishpass and default bundles produced bit-identicallake_rearing_hatotals despite the bundles declaring different rear rules.
fresh 0.16.0
-
frs_habitat_classify()output schema gains awetland_rearingboolean column, mirroringlake_rearingbut joined towhse_basemapping.fwa_wetlands_polyonwaterbody_key(#164). Additive schema change — existing callers are unaffected;lake_rearingsemantics unchanged. Prerequisite for link’s compound rearing rollup (link#51).
fresh 0.15.0
- Remove unused
frs_fish_habitat()andfrs_fish_obs(). Both were BC-specific fetchers hard-coded to bcfishpass/bcfishobs table names and had no callers in fresh or link. If BC-specific fetchers become useful later they belong in link where the domain context lives (#162) - Exported function count: 41 → 39.
- Add missing
@param barrier_overridesdocstring onfrs_habitat_classify()— the parameter was already in the signature but absent from roxygen so the rendered help didn’t describe link’s main integration point. - Docs role-clarity refresh: README + CLAUDE.md ecosystem tables updated to describe link’s full scope; pipeline wording split into fish-habitat (link → fresh) and land-cover-change (fresh → flooded → drift); CLAUDE.md version + architecture file list brought current.
fresh 0.14.0
- Add
frs_barriers_minimal()— reduce barrier points to the downstream-most per flow path viafwa_upstream()self-join. Extracts the bcfishpass “non-minimal removal” pattern from link’s compare pipeline into a reusable fresh function. Typical reduction ~27,000 raw gradient barriers → ~700 minimal on a full watershed group (#160)
fresh 0.13.8
Three-phase cluster connectivity for rearing.
- Phase 1: on-spawning segments (both rearing AND spawning) excluded from clustering — always valid. Prevents over-removal of rearing on spawning streams (#153)
- Phase 3:
FWA_Downstream()on the broken streams table (mainstem only) replacesfwa_downstreamtrace()on raw FWA. Finds spawning downstream of rearing clusters with path gradient + distance constraints (#153) - BULK CH rearing +6.0% → +2.6%, BT rearing +1.3% → -2.2%. All species within 5% across 4 WSGs
fresh 0.13.7
Apply bridge gradient along downstream path in frs_cluster.
-
frs_cluster()downstream check now appliesbridge_gradientandbridge_distancesegment-by-segment along thefwa_downstreamtracepath. Upstream check remains boolean —FWA_Upstreamreturns tributaries that interleave with mainstem inrow_number()ordering, making path gradient unreliable on branching networks (#153)
fresh 0.13.6
Support spawn_connected rules for waterbody-adjacent spawning.
- Parse
spawn_connectedblock in rules YAML — permissive spawn thresholds for segments in the downstream trace from waterbody outlets. Own validation separate from spawn/rear rules (#154) - Additive step in
.frs_connected_waterbody(): accessible segments in the trace meetingspawn_connectedthresholds getspawning = TRUEeven if they failed standard classification. BULK SK spawning -9.6% to -0.7% vs bcfishpass (#154)
fresh 0.13.5
Fix lake outlet ordering and extract reusable downstream trace.
- Fix multi-BLK lake outlet selection: use
wscode_ltreenetwork topology instead ofdownstream_route_measure(meaningless across BLKs). BULK SK spawning -22.6% to +0.1% vs bcfishpass (#147) - Fix cumulative distance partitioning: partition by
waterbody_keyinstead ofblue_line_keyso distance accumulates per lake, not per BLK (#147) - Extract
.frs_trace_downstream()— reusable downstream trace with distance cap and gradient stop (#147) - Rename
.frs_connected_spawning()to.frs_connected_waterbody()— parameterized for any waterbody type (#147) -
waterbody_type: Lnow includes reservoirs (fwa_manmade_waterbodies_poly) via shared.frs_waterbody_tables()helper — the FWA table split is digitization origin, not ecology (#147)
fresh 0.13.4
Index input tables for standalone frs_habitat_classify performance.
-
frs_habitat_classify()now indexes input tables before the access gating loop — 35x speedup when called directly (bypassingfrs_habitat) (#150) -
.frs_index_working()is now idempotent withIF NOT EXISTS— safe to call multiple times on the same table (#150)
fresh 0.13.3
Two-phase connected spawning and multi-label breaks.
- Two-phase connected spawning for lake-rearing species (SK, KO) matching bcfishpass v0.5.0: downstream trace from lake outlets (3km cap, gradient stop) + upstream cluster with lake polygon proximity (
ST_DWithin). Auto-dispatched when rearing rules includewaterbody_type: L(#147) - Multiple labels per break position preserved — a gradient_15 and a falls at the same measure both survive in
streams_breaks(#145)
fresh 0.13.2
Preserve multiple labels per break position.
- Multiple labels at the same
(blue_line_key, downstream_route_measure)now survive instreams_breaks— a gradient_15 and a falls at the same measure are both preserved. Enables per-barrier overrides, access reporting, and habitat reporting by barrier type (#145)
fresh 0.13.1
Persist gradient barriers and downstream-only distance fix.
-
to_barriersparameter onfrs_habitat()— persist gradient barriers table with ltree enrichment for link’slnk_barrier_overrides()(#143) -
connected_distance_maxdistance filter now downstream-only with correctfwa_upstreamdirection (#133) - Fix
aoi+wsginteraction — character aoi ANDed with WSG filter instead of replacing it (#141)
fresh 0.13.0
Distance filter, aoi fix, and measure rounding.
-
connected_distance_maxdistance filter now downstream-only — upstream spawning has no distance cap, matching bcfishpass v0.5.0 SK spawning logic. Uses DRM difference (same-BLK) and ST_Distance Euclidean (cross-BLK) (#133) - Fix
aoireplacingwsgfilter instead of being additive —frs_habitat(wsg = "ADMS", aoi = "edge_type != 6010")now correctly scopes to ADMS, not province-wide (#141) -
measure_precisionparameter onfrs_network_segment()andfrs_habitat()— controls break measure rounding. Default0(integer, matching bcfishpass). Breaks table rounded and deduped before splitting (#135) - Auto-skip gradient/channel_width inheritance on
waterbody_type: L/Wrules (#131)
fresh 0.12.9
Post-cluster distance filter, measure rounding, and lake threshold auto-skip.
-
connected_distance_maxnow caps habitat EXTENT from connected segments (not just search distance). Post-cluster filter removes individual segments beyond the cap. SK spawning 112 km → ~74 km (#133) -
measure_precisionparameter onfrs_network_segment()andfrs_habitat()— controls decimal places for break measure rounding. Default0(integer, matching bcfishpass). Breaks table rounded and deduped before splitting (#135) - Auto-skip gradient/channel_width inheritance on
waterbody_type: L/Wrules — lake/wetland flow lines are routing lines (#131)
fresh 0.12.8
Lake/wetland threshold auto-skip and connected distance cap.
- Auto-skip gradient/channel_width CSV inheritance on
waterbody_type: Landwaterbody_type: Wrules — lake/wetland flow lines are routing lines, channel dimensions are meaningless (#131) -
connected_distance_maxpredicate in rules YAML — caps network distance from connected habitat whenrequires_connectedis present. SK/KO spawning capped at 3km from rearing lake (#133)
fresh 0.12.7
Replace observations with barrier_overrides.
-
barrier_overridesparameter replacesobservationsonfrs_habitat()andfrs_habitat_classify()— accepts a pre-computed table of(blue_line_key, downstream_route_measure, species_code)from link. Fresh skips matched barriers without counting, thresholds, or date filters (#129) - Remove observation counting SQL and
observation_*columns fromparameters_fresh.csv— fish passage interpretation belongs in link, not the network engine
fresh 0.12.6
Multi-class gradient barrier detection.
-
frs_break_find(classes =)— single-pass multi-class gradient detection matching bcfishpass v0.5.0. Tags every vertex with its gradient class, groups consecutive same-class vertices into islands, places one barrier at each class transition. Catches transitions WITHIN steep sections that boolean above/below missed (#127) -
frs_break_find(blk_filter =)— restrict to main flow lines (blue_line_key = watershed_key). Default TRUE (matches bcfishpass) -
frs_habitat()now callsfrs_break_find()once with all gradient classes instead of looping per threshold. Simpler, faster, more barriers detected.
fresh 0.12.5
Observation-based access override.
-
observationsparameter onfrs_habitat()andfrs_habitat_classify()— fish observations upstream of gradient/falls barriers override access gating per species. Thresholds fromparameters_fresh.csv:observation_threshold,observation_date_min,observation_buffer_m,observation_species(#69) - Defaults match bcfishpass v0.5.0: BT >= 1 obs (all salmonids count), CH/CO/SK/PK/CM/ST >= 5 obs (species-specific), since 1990, 20m buffer
- ADMS: BT accessible +103%, CH/CO +10% with
bcfishobs.observations
fresh 0.12.4
Post-segmentation gradient control.
-
gradient_recomputeparameter onfrs_network_segment()andfrs_habitat()—TRUE(default) recomputes gradient from DEM vertices after splitting;FALSEinherits parent segment gradient, matching bcfishpass v0.5.0 behavior (#124) -
frs_col_generate(exclude =)— skip named columns from regeneration (generic, reusable)
fresh 0.12.3
SK/KO spawning requires connected rearing lake.
-
requires_connectedpredicate in rules YAML — spawning segments must be spatially connected to rearing viafrs_cluster(). No rearing lake = no spawning (#120) - New
.frs_run_connectivity()orchestrates both rearing cluster checks (cluster_rearing) and spawning connectivity checks (requires_connected+cluster_spawning) after classification - SK/KO:
cluster_spawning = TRUEwith 3km bridge distance. KO added toparameters_fresh.csv - ADMS sub-basin: SK spawning 15 → 0 (correct — no lakes >= 200 ha)
fresh 0.12.2
Fix short barrier detection.
-
frs_break_find()gainsmin_lengthparameter (default0) — keeps all gradient islands regardless of length. A 30m waterfall at 20% gradient is a real barrier but was silently dropped by the old 100m minimum. Passmin_length = 100to restore pre-0.12.2 behavior (#118) - ADMS sub-basin at 15%: 137 barriers (was 98, +39 recovered)
fresh 0.12.1
Per-rule threshold overrides in habitat rules YAML.
- Rules can now specify
gradient: [min, max]andchannel_width: [min, max]that override CSV inheritance per rule. Missing fields still inherit from CSV whenthresholds: true(#116) - Bundled YAML updated: all
waterbody_type: Rrules now havechannel_width: [0, 9999]— skips channel_width_min on river polygons (matching bcfishpass v0.5.0 pattern where river polygon widths are unreliable)
fresh 0.12.0
Habitat eligibility rules format (Phase 1).
- YAML-based habitat rules for multi-rule species — each species gets a list of rules per habitat type joined by OR, each rule is AND of predicates. Replaces the single-rule-per-CSV-row limitation (#113)
- Bundled
inst/extdata/parameters_habitat_rules.yamlwith NGE defaults for 11 species (synced fromlink/inst/extdata/parameters_habitat_dimensions.csv) -
frs_params(rules_yaml =)loads rules YAML (default = bundled,NULL= skip rules for backward compat) -
frs_habitat(rules =)pass-through (NULL= bundled,FALSE= disable, string path = custom file) -
thresholds: falseper rule — wetland-flow carve-out pattern where a rule bypasses CSV gradient/channel_width inheritance -
Default behavior change: SK rearing now lake-only (area >= 200 ha); PK/CM rearing = 0 (explicit
rear: []); CO/BT/CH/ST/WCT/RB rearing expanded to include river polygons + wetland-flow segments. Passrules = FALSEto restore pre-0.12.0 behavior. - Phase 2 MAD support deferred to #114
fresh 0.11.1
Gradient barrier label format precision fix.
- New
gradient_NNNNlabel format — 4-digit zero-padded basis points (threshold × 10000) preserves precision for fractional thresholds.0.05→gradient_0500,0.0549→gradient_0549,0.15→gradient_1500. Resolution: 1 basis point (#110) - Fixes silent precision loss in 0.11.0 where
as.integer(thr * 100)collapsed0.05and0.0549both togradient_5, causing distinct biological thresholds to share a single break - New
.frs_validate_gradient_thresholds()errors on out-of-range, excess precision, NA, or label-collision inputs (catches the bug class going forward) - Parser
.frs_access_label_filter()accepts BOTH new format and legacygradient_Nfor backward compat with user-supplied labels viafrs_break_find(label = "gradient_15")
fresh 0.11.0
Sub-segment gradient resolution via auto-derived breaks.
-
breaks_gradientparameter onfrs_habitat()— generates gradient breaks at biologically meaningful thresholds derived fromspawn_gradient_max+rear_gradient_maxinparameters_habitat_thresholds.csv. Three modes:NULL(default auto-derive), numeric vector (explicit override),numeric(0)(disable, 0.10.0 behavior) (#101) - Default behavior change:
frs_habitat()now produces ~30% more segments on typical sub-basins (ADMS test: 312 → 409). The added breaks are at biologically meaningful gradient thresholds and givefrs_cluster()the resolution it needs to detect within-segment steep sections that would otherwise be hidden by averaging - Pass
breaks_gradient = numeric(0)to restore 0.10.0 behavior (only species access thresholds)
fresh 0.10.0
Network cluster connectivity analysis.
-
frs_cluster()— generic cluster connectivity validation. Groups adjacent segments sharing one label, checks if another label exists upstream, downstream, or both on the network. Disconnected clusters set to FALSE. UsesST_ClusterDBSCANfor spatial clustering andfwa_downstreamtrace()for downstream trace with gradient bridge and distance cap (#107) - Per-species cluster config in
parameters_fresh.csv—cluster_rearing,cluster_direction,cluster_bridge_gradient,cluster_bridge_distance,cluster_confluence_m. Anadromous species opt in; resident species do not -
direction = "both"evaluates upstream and downstream independently, keeps clusters valid in either direction
fresh 0.9.0
Flexible AOI, configurable access gating, and feature indexing.
-
frs_habitat()accepts any AOI — sf polygons, WHERE clauses, ltree filters — not just WSG codes. Addspeciesandlabelparams for custom runs (#96) -
gateparameter onfrs_habitat_classify()— setFALSEto classify raw habitat potential without access restrictions (#98) -
blocking_labelsparameter — configurable which labels restrict access. Default"blocked". Setc("blocked", "potential")for conservative analysis. Only"blocked"andgradient_Nblock by default; everything else passes through -
frs_feature_find()— locate any point features on the network (crossings, observations, stations) withcol_idfor feature identity (#92) -
frs_feature_index()— index upstream/downstream relationships between segments and features as ID arrays (#93) -
frs_break_find()slimmed to gradient-only. Point/table modes moved tofrs_feature_find() - Province-wide run completed: 229 WSGs, 5.98M segments
- Classify growth penalty fixed: constant-time DELETE by
watershed_group_code(#91)
fresh 0.8.0
Unified pipeline, domain-agnostic network segmentation, and major performance improvements.
New functions
-
frs_network_segment()— domain-agnostic network segmentation. Extracts, enriches, breaks at any point sources, assignsid_segment. One table, one copy of geometry. -
frs_habitat_classify()— long-format habitat classification. One row per segment x species withaccessible,spawning,rearing,lake_rearing. Species-specific accessibility via break label filtering. -
frs_feature_find()— locate any point features on the network (crossings, observations, stations). Replacesfrs_break_find()points/table modes. -
frs_feature_index()— index upstream/downstream relationships between segments and features. Stores feature ID arrays per segment.
Pipeline changes
-
frs_habitat()rewritten to wrapfrs_network_segment()+frs_habitat_classify(). Auto-generates gradient barriers per WSG from species parameters. -
to_streams+to_habitatparams replaceto_prefixfor persistent output tables. - mirai replaces furrr for parallel execution — lighter daemons, foundation for crew.aws.batch.
-
frs_break_find()slimmed to gradient-only (island detection). Point/table modes moved tofrs_feature_find().
Performance
- Island-based gradient barriers: 85% fewer barriers than interval method (#86)
- Accessibility recycled by threshold group: 5 queries → 2 for species sharing thresholds (#89)
- Classify growth penalty fixed: constant time DELETE by
watershed_group_code(#91) - Breaks table indexed with ltree GIST for cross-BLK queries: 15x speedup
- Province-wide: 229 WSGs, 5.98M segments completed
fresh 0.7.0
Local Docker fwapg instance and generic network-referenced classification via break_sources.
- Replace
fallsparameter withbreak_sourceslist across pipeline (frs_habitat(),frs_habitat_partition(),frs_habitat_access()) — accepts any number of point tables withlabel,label_col, andlabel_mapfor flexible classification (#70) - Add
labelandsourcecolumns to breaks table for provenance tracking - Ship
inst/extdata/falls.csv(3,294 barrier falls) andinst/extdata/crossings.csv(533k crossings) withdata-raw/refresh scripts - Add Docker-based local fwapg: containerized PostGIS + loader with GDAL/psql/bcdata (#63)
- Fix Phase 2 sequential mode to reuse
conninstead of callingfrs_db_conn()
fresh 0.6.0
Parallelized multi-WSG habitat pipeline — 2.5x speedup over sequential on BULK.
- Add
frs_habitat()— orchestrator: run the full habitat pipeline across watershed groups withfurrrparallelism (#61) - Add
frs_habitat_partition()— generic partition prep (extract, enrich, pre-compute breaks). Accepts any AOI, not just WSG codes - Add
frs_habitat_access()— gradient + falls barrier computation at a threshold, deduplicated across species sharing the sameaccess_gradient_max - Add
frs_habitat_species()— classify one species with pre-computed access and habitat breaks - Both Phase 1 (partition prep across WSGs) and Phase 2 (species classification) parallelize via
furrr::future_map()whenworkers > 1 - Benchmark scripts and logs in
scripts/habitat/
fresh 0.5.0
Watershed-group habitat pipeline — run the full pipeline across all species in a WSG.
- Add
whereparameter tofrs_extract()for SQL predicate filtering, ANDed withaoiwhen both provided (#60) - Add
frs_wsg_species()to look up species presence and bcfishpass view names per watershed group from bundledwsg_species_presence.csv - Add
data-raw/pipeline_wsg.R— runs full habitat pipeline per species per WSG with timing (baseline: 20 min for BULK, 7 species, 32K segments)
fresh 0.4.1
- Add
frs_categorize()for priority-ordered boolean-to-category collapse — mapping codes for QGIS, reporting, gq style registry (#57) - Refine habitat pipeline vignette: code below each narrative section with function links, 2x2 scenario grid, access barriers on habitat map
fresh 0.4.0
Coho habitat pipeline — classify habitat on the database at any scale. New vignette proves the workflow on the Byman-Ailport subbasin of the Neexdzii Kwa.
- Add
frs_col_join()for generic lookup table enrichment — channel width, MAD, upstream area, any key (#51) - Add
frs_edge_types()lookup helper with bundled FWA edge type CSV (41 codes from GeoBC User Guide) - Add
toparam tofrs_network()— write results to DB working tables instead of pulling to R, scales to any number of watershed groups (#53) - Add
whereparam tofrs_classify()— scope classification by SQL predicate for edge-type-aware and accessibility-filtered habitat modelling - Add
whereandappendparams tofrs_break_find()table mode — filter points table and combine multiple break sources (gradient + falls) in one breaks table (#38) - Add
exclude_edge_typesparam tofrs_point_snap()— only exclude subsurface flow (1425) by default, not wetland connectors (1410) (#52) - Bundle bcfishpass habitat parameter CSVs and fresh-specific access gradient thresholds (#54)
- Add coho habitat pipeline vignette: extract → enrich → break (gradient + falls) → classify (accessible, spawning, rearing, lake rearing) → aggregate → scenario comparison
fresh 0.3.1
- Add
fromandextra_whereparams to waterbody specs infrs_network()for filtering waterbodies to those connected to habitat streams (#49) - Network traversal table configurable via
.frs_opt("tbl_network")(#44)
fresh 0.3.0
Server-side habitat model pipeline — replaces ~34 bcfishpass SQL scripts with 4 composable functions. See the function reference for details.
- Add
frs_extract()for staging read-only data to writable working schema (#36) - Add
frs_break()family (find,validate,apply, wrapper) for network geometry splitting viaST_LocateBetweenandfwa_slopealongintervalgradient sampling (#38) - Add
frs_classify()for labeling features by attribute ranges, break accessibility (viafwa_upstream), and manual overrides — pipeable for multi-label classification (#39) - Add
frs_aggregate()for network-directed feature summarization from points (#40) - Add
frs_col_generate()to convert gradient/measures/length to PostgreSQL generated columns — auto-recompute after geometry changes (#45) - Add
.frs_opt()for configurable column names viaoptions()— foundation for spyda compatibility (#44) - All write functions return
conninvisibly for consistent|>chaining
fresh 0.2.0
CRAN release: 2020-05-29
-
Breaking: All DB-using functions now take
connas the first required parameter instead of...connection args. Create a connection once withconn <- frs_db_conn()and pass it to all calls. Enables piping:conn |> frs_break() |> frs_classify()(#35)
fresh 0.1.0
CRAN release: 2019-10-21
- Multi-blue-line-key support for
frs_watershed_at_measure()andfrs_network()viaupstream_blkparam (#20) - Add
frs_watershed_at_measure()for watershed polygon delineation with subbasin subtraction - Add
frs_network()unified multi-table traversal function replacing per-type fetch functions - Add
frs_default_cols()with sensible column defaults for streams, lakes, crossings, fish obs, and falls - Add
upstream_measureparam for network subtraction between two points on the same stream - Add
frs_waterbody_network()for upstream/downstream lake and wetland queries via waterbody key bridge - Add
wscode_col,localcode_col, andextra_whereparams for custom table schemas - Add
frs_check_upstream()validation for cross-BLK network connectivity - Add
blue_line_keyandstream_order_minparams tofrs_point_snap()for targeted snapping via KNN (#16, #17, #7, #18) - Add stream filtering guards: exclude placeholder streams (999 wscode) and unmapped tributaries (NULL localcode) from network queries;
include_allto bypass. Subsurface flow (edge_type 1410/1425) kept in network results (real connectivity) but excluded from KNN snap candidates (#15) - Add
frs_clip()for clipping sf results to an AOI polygon, withclipparam onfrs_network()for inline use (#12) - Add
frs_watershed_split()for programmatic sub-basin delineation from break points — snap, delineate, subtract with stableblk/drmidentifiers (#31) - Security hardening: quote string values in SQL, validate table/column identifiers, clear error on missing PG env vars, gitignore credential files (#19)
- Input type validation on all numeric params
- Add subbasin query vignette with tmap v4 composition
- Fix ref CTE to always query stream network, not target table
Initial release. Stream network-aware spatial operations via direct SQL against fwapg and bcfishpass. See the function reference for details.
