The weighting is now described accurately as rank-reciprocal (each
variable’s weight is the inverse of its importance rank for the
species). Earlier text called it “AHP-weighted”, but no
pairwise-comparison matrices or consistency ratio were ever computed. No
behaviour changes. The area_summary() example’s slow second
call now runs under \donttest{} to keep example time
short.
predict_oyster() gains
weighting = c("rank", "evidence"). The default is
unchanged, so every existing call returns exactly what it returned in
1.5.0.
The problem it addresses: the engine renormalises its rank weights over whichever variables happen to be present, so a survey measuring four of a species’ seventeen scored variables returns a confident number on [0, 1] with nothing on it to say the rest of the evidence base was never observed. Absence is indistinguishable from irrelevance. Two further consequences follow, both measurable — a variable that does not vary still absorbs weight, and a variable that does not reproduce is weighted identically to one that does.
Under weighting = "evidence" each variable’s weight
becomes prior x reliability x spread, normalised.
Reliability multiplies the biological prior and never replaces it:
weighting on measured reliability alone would promote whatever the
instrument happens to measure well, which is circular. Coverage — the
share of the species’ total prior weight the survey observed — is
reported separately and never multiplied into the per-cell score, which
would put every location below any class threshold on a low-coverage
survey.
New exported functions:
evidence_coverage() — share of the species’ total prior
weight observed. Weight-based, unlike the count-based
data_completeness already returned, so missing a rank-1
variable costs six times what missing a rank-6 one does.
attempted_failed prices an instrument failure in the same
units.reliability_from_replicates() — reliability from real
repeat coverage, with a confidence interval and an
established flag.layer_reproducibility() — measures reliability for
every layer at once from a survey’s own overlapping lines: bins the
survey to a grid, pairs the lines crossing each cell, and passes the
pairs to reliability_from_replicates(). Returns a named
vector ready for predict_oyster(reliability = ), named as
the scored layer (depth, not depth_m), with
NA for layers with too few overlaps to test. Needs a
line/pass column and at least two lines.reliability_from_perturbation() — the same statistic
where there is no repeat coverage, by injecting the measured
per-observation error. derive propagates depth error into
quantities computed from depth.evidence_weights() — the weight derivation as an
auditable table, with provenance per layer.confidence_score() — per-location confidence from
weighted components.evidence_suitability() — re-weights existing
score_* columns and attaches coverage. Scoring curves are
untouched.weight_table_md() — weight derivation and coverage
ledger as markdown, for a methods section.Plus print() methods for evidence_weights
and evidence_coverage.
Tolerance profiles for populations or lineages, linked by genetic
relatedness, and maps of where each is more likely to occur.
Bring-your-own-data: occurrence records carrying a lineage identifier,
and a pairwise genetic distance matrix.
lineage_walkthrough.R runs the whole workflow on simulated
data.
as_lineage_distances() — validates a distance matrix
(matrix, dist, data frame or CSV).lineage_signal() — Mantel test of whether related
lineages have more similar fitted tolerances. Pooling is refused below
six fitted lineages, and when the signal is absent lineages are fitted
alone.fit_lineages() — one profile per lineage via the
existing Bayesian update. Where signal is shown, a Gaussian-process
prior decaying with genetic distance lets thin lineages borrow from
relatives, and lineages with no records get a profile from relatedness
alone (source = "borrowed").lineage_occurrence() — per-lineage suitability and a
posterior across lineages at every location, with an ambiguity measure
and an "Ambiguous" class where the environment does not
tell lineages apart.rank_source_populations() — ranks lineages as sources
for each site, with an optional discount for genetic distance from the
locally native lineage and optional posterior draws.lineage_fits_as_list() /
lineage_fits_from_list() — reduce a fitted model to plain
data (profile table, calibration, distances, signal test) that survives
JSON, and rebuild a scoring-ready model from it. The SaaS stores models
this way.update_species_tolerances() gains profile
(fit and cache under "<species>::<profile>")
and prior (per-variable prior means and SDs). Both default
to NULL, which leaves behaviour unchanged, and the result
now also carries tolerances.
update_species_tolerances()The Bayesian update fitted a private copy of the scoring curve that
differed from the one predict_oyster() uses, and reported
no uncertainty. All of the following are corrected; results change.
.score_numeric() as the maps, with shoulders
scaled to the candidate optimum exactly as the returned tolerance object
carries them. Before, the lower shoulder was held fixed while the
returned curve scaled it.sqrt(diag(cov)) dropped the
parameter names, so the by-name lookups returned NA for
every posterior_sd_* and ci95_*.updated_params[[v]]$at_bound_omin and
at_bound_omax say when an estimate sits on (or outside) the
search box; its Laplace curvature is meaningless there, so the prior SD
is reported instead, with a warning.If you have custom tolerance envelopes fitted on more than one variable, refit them. They were fitted under the inverted weights.
seafloormapRcore carries the only other copy of this
code (it cannot run on its own – it needs a species pack that supplies
get_species_tolerances()) and has the same corrections.
check_exclusions() (and so
predict_oyster())predict_oyster() no longer fails for
crassostrea_iredalei and saccostrea_cucullata
on a survey without a date column. With no date,
season is NA and the seasonal checks are meant
to be skipped, but season == "winter" is then
NA, NA & TRUE is NA, and any
cell colder than the species’ winter minimum (18 degrees for these two)
left NA in the flag. The assignment that records the reason
then stopped with “NAs are not allowed in subscripted assignments”. The
other fifteen species have winter minimums below the temperatures in
typical data, which hid it. Every species is now scored on the example
survey in the tests.exclusion_reason names each row’s own
value. The reason for an excluded row was built from every
flagged row’s temperature (or oxygen) at once, so with two cells too
cold both rows read “temp_too_cold (2.0 < 7.0); temp_too_cold (3.0
< 7.0)”. Each row now lists only its own. This affects the
temperature, seasonal and oxygen reasons; the salinity reason was
already per row.generate_summary_pdf() in a non-UTF-8 localeThe PDF device cannot draw an em dash or a middle dot in a C or other
non-UTF-8 locale and stopped with “conversion failure … in ‘mbcsToSbcs’”
– including under R CMD check on some machines. In such a
locale those characters now get plain-ASCII stand-ins; output in a UTF-8
locale is unchanged.
A location below confidence_floor (default 0.5) is
returned as "Insufficient data" rather than handed a class
it cannot support. The continuous score is still returned for it.
Consequences elsewhere:
area_summary() counts those locations toward the
surveyed area — that ground was surveyed — and never toward the suitable
area, which remains High + Moderate. They are excluded from patch
analysis.export_geotiff() now writes seven bands. Band 6 is
per-location confidence; band 7 is the
insufficient_mask. Unclassified locations are held out of
the band 1 interpolation rather than asserting a suitability into the
surface.export_qml_style() writes a companion
<name>_insufficient.qml styling band 7 as a grey
overlay, so unclassified ground reads in QGIS as a class rather than as
a hole in the map.R/evidence_weights.R is vendored from
seafloormapRcore. oystermapR does not import the core
package — it carries its own copy of the engine — so fix bugs in the
core copy first and re-vendor rather than editing in place.calculate_aragonite() — new exported
function. Computes aragonite saturation state (Ω_arag) from pH, total
alkalinity, temperature, and salinity with no external package
dependencies. Uses Lueker et al. (2000) K1/K2, Dickson (1990) KB,
Millero (1995) KW, Mucci (1983) Ksp_arag, Uppstrom (1974) [B_T], Riley
& Tongudai
(1967) [Ca²⁺]. Validated: T=12 °C, S=35, pH=8.1, TA=2300 µmol/kg →
Ω≈2.54.ph and
alkalinity columns are present in the survey data,
predict_oyster() automatically computes
omega_aragonite before scoring — no preprocessing step
required.inst/extdata/sample_survey.csv and
inst/extdata/example_bay_ctd.csv now include
ph and alkalinity columns with realistic
values for their respective locations.plot_tolerance(species, variable) —
new exported function. Draws the suitability scoring curve for any
species/variable combination directly from the tolerance specification,
no dataset required. Colour-coded zone backgrounds (green = optimal,
orange = poor/acceptable, red = excluded). For seasonal variables
(temperature), pass season = "all" to overlay all four
curves. Falls back to base R plot() if ggplot2 is not
installed.
area_summary(result) — new exported
function. Converts point-based suitability output to fine-scale area
estimates in m² (primary unit) and ha (secondary). Designed for
restoration ecology and science where sub-hectare precision matters. Key
features:
cell_size_m for known grids (ROV,
AUV, ADCP tracklines).terra::patches()) identifies
individual High/Moderate habitat patches by size, with a
viable flag against a configurable minimum area threshold
(default 100 m² = 0.01 ha, aligned with OSPAR oyster reef restoration
guidance). Largest/smallest/median patch size printed to console.Note: compare_species() and
sensitivity_analysis() were already implemented in
batch_compare.R and variable_importance.R
respectively.
variable_impact())variable_impact(result, species) — new
exported function. Returns a ranked summary table showing for each
environmental variable: species importance rank, normalised weight (%),
mean score across non-excluded points, net weighted contribution to the
suitability score, and data coverage (% of points with data). Sort by
"mean_contribution" (default, highest-impact variables
first), "mean_score" (lowest-scoring = likely bottlenecks),
or "pct_coverage" (sparsest data layers). Primary
diagnostic for QA and survey planning.scored for
all 17 species (rank 5). DO was previously only used as a hard exclusion
threshold. Sites are now credited for better oxygen conditions within
the acceptable range — a site at 9 mg/L scores higher than one at 5
mg/L. Species-specific optimal ranges applied (e.g. O. edulis 6–10 mg/L;
C. iredalei 4–8 mg/L).scored for 6 species
that lacked it (ostrea_edulis, magallana_gigas, crassostrea_angulata,
ostrea_stentina, ostrea_lurida, ostrea_angasi). Salinity was
exclusion-only for these species; sites at optimal salinity now score
higher than sites at merely acceptable salinity.dissolved_oxygen and salinity
added to col_aliases — these variables are now
recognised from standard column names (e.g. do,
do_mgl, sal, salinity_psu) and
properly matched to the scored entries.optimal_min corrected
from 0 m to 2 m with poor_min = 0. O. edulis is a subtidal
species; intertidal sites (0–2 m) now score in the “poor” rather than
“optimal” zone, consistent with published habitat assessments (Pogoda et
al. 2023).n_layers_scored — new integer output
column from predict_oyster(). Counts the number of
environmental variables that contributed to the suitability score at
each location. Use alongside data_completeness (fraction)
to identify points where the score rests on few data inputs.export_geotiff() now
writes n_layers_scored as a fifth raster band. Load it as a
thematic overlay in QGIS to visualise data coverage spatially alongside
the suitability heatmap.crassostrea_virginica — Eastern Oyster (NW Atlantic);
high data quality.saccostrea_glomerata — Sydney Rock Oyster (E
Australia); medium data quality.magallana_sikamea — Kumamoto Oyster (Japan / Pacific
NW); low data quality.magallana_ariakensis — Suminoe Oyster (E Asia); low
data quality.crassostrea_hongkongensis — Hong Kong Oyster (S China
Sea); high data quality; note on historical misidentification as C.
plicatula / C. rivularis included in species
metadata.crassostrea_nippona — Iwagaki Oyster (Japan / Korea);
medium data quality.crassostrea_belcheri — Tropical Rock Oyster (SE Asia);
low data quality.ostrea_chilensis — Chilean Oyster (S Chile / New
Zealand); low data quality.ostrea_denselamellosa — Korean Flat Oyster (E Asia);
low data quality.read_nortek_aquadopp() — reads Nortek Aquadopp ASCII
export (ENU or beam files); handles moored (fixed lat/lon) and
vessel-mounted (GPS in file) deployments.read_rdi_adcp() — reads Teledyne RDI binary PD0 files
via oce::read.adp.rdi(); falls back to WinRiver ASCII CSV
with VelEast_binN columns.read_aanderaa_csv() — reads Aanderaa Data Studio CSV
exports; auto-detects speed unit (cm/s vs m/s); handles “Horisontal
Speed” column name variant.example-bay-survey vignette: complete eight-step
pipeline using simulated Example Bay data (ADCP, single-beam soundings,
CTD), covering data ingest, QC, suitability prediction, risk scoring,
and GeoTIFF export.inst/extdata/:
example_bay_adcp.csv,
example_bay_soundings.xyz,
example_bay_ctd.csv.inst/REFERENCES.md expanded: added citations for all 9
new species; added Horn (1981) for terrain derivative methods; confirmed
every referenced source in the package has a corresponding entry.oystermapR_live_config() — stores credentials for
CMEMS, ICES, EMODnet, and FSA in a session-scoped environment;
credentials are never written to disk.fetch_live_environmental_data() — pulls salinity,
temperature, current speed, chlorophyll, HAB presence, and substrate
from external APIs and merges the result onto an existing survey
dataframe by rounded lat/lon grid keys.--force-download /
--output-format csv, --log-level changed to
WARN; all output is NetCDF parsed with ncdf4.read_soundings_xyz() — reads bathymetric sounding XYZ
files; derives slope and rugosity via terra::terrain.read_sonar_tif() updated to normalise backscatter
intensity to substrate_hardness in the range [0, 1];
reprojects to WGS84 if needed.qc_survey_data() — fixed crash when
apply_flags = TRUE: cli::cli_inform
bullet-prefix argument was incorrectly passed as a named function
argument instead of a named element of the message character
vector.devtools::document() warnings resolved: non-ASCII
characters replaced with \\uXXXX escapes in R source;
roxygen #' lines now use plain ASCII to avoid unknown-macro
warnings in compiled .Rd files.detect_season() / add_season_column() —
automatic season detection from a datetime column; used internally by
predict_oyster() to apply season-specific tolerance
weights.auto_tidal_correct() /
correct_to_chart_datum() — corrects depth readings to chart
datum using harmonic tidal prediction (nodal factors, UKHO-style
harmonics).add_intertidal_flag() — flags survey points that fall
within the intertidal zone based on tidal range.score_hab_risk() — HAB (Harmful Algal Bloom) risk
scoring with optional live ICES biotoxin data integration.score_anthropogenic_disturbance() — disturbance scoring
incorporating shipping density, aquaculture lease proximity, and
dredging history layers.score_wave_exposure() /
score_sediment_stability() — fetch-based wave exposure and
Shields criterion sediment stability scoring.add_shellfish_classification() — appends EU/UK
shellfish harvesting classification zone (A/B/C/Prohibited) from spatial
polygon layers.generate_summary_pdf() — produces a self-contained
four-page A4 PDF using only grDevices::pdf() and
ggplot2; no LaTeX or pandoc dependency.validate_against_records() now returns Brier score and
F1 alongside AUC, TSS, sensitivity, and specificity.spatial_block_cv() spatial block cross-validation
added.Initial CRAN release.
predict_oyster() — AHP-weighted suitability scoring
from tabular sensor data, with automatic column name matching, season
detection, hard exclusion checks, and optional GeoTIFF + contour export
for QGIS.check_exclusions() / score_locations() —
modular exclusion and scoring steps for programmatic use.export_geotiff() / export_qml_style() —
raster and QGIS colour-ramp export.Ostrea edulis, Magallana gigas, Crassostrea angulata, Ostrea
stentina, Ostrea lurida. See list_species().
read_nortek_adcp() — Nortek Signature 500 ADCP CSV
parser.read_sonar_tif() — Ping 3DSS / BioBase bathymetric
raster reader.merge_sensor_data() / ingest_sensors() —
spatial merge of multi-sensor datasets.validate_against_records() — AUC, TSS, Brier score, F1,
sensitivity/specificity vs known presence/absence records.spatial_block_cv() — spatial block cross-validation
(Roberts et al. 2017).permutation_importance() — variable importance by AUC
drop.sensitivity_analysis() — partial dependence
curves.update_species_tolerances() — MAP + Laplace or RWMH
MCMC updating of optimal-range parameters from field observations.get_tolerance_posteriors(),
save_tolerance_update(),
load_tolerance_update(),
reset_tolerance_update().score_predation_risk(), score_hab_risk(),
score_anthropogenic_disturbance(),
score_wave_exposure(),
score_sediment_stability(),
add_shellfish_classification().score_larval_connectivity() — hybrid union-find
Gaussian kernel + optional OpenDrift/FVCOM connectivity matrix
scoring.parse_opendrift_connectivity() — converts OpenDrift CSV
output to connectivity matrix format.compare_species(), compare_surveys(),
composite_seasonal().