Skip to content

Commit 8fac158

Browse files
authored
Merge pull request #12 from FishEcologyScience/CST-564-client-support
CST-564 client support
2 parents d910b18 + f5ac3f4 commit 8fac158

32 files changed

Lines changed: 720 additions & 366 deletions

.Rbuildignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
^pkgdown$
1111
^tmp$
1212
^notes\.md$
13+
^\.vscode$

.github/workflows/R-CMD-check.yaml

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@ jobs:
1616
fail-fast: false
1717
matrix:
1818
config:
19-
# - {os: macOS-latest, r: 'devel'}
20-
# - {os: macOS-latest, r: 'release'}
21-
# - {os: windows-latest, r: 'devel'}
22-
# - {os: windows-latest, r: 'release'}
23-
# - {os: windows-latest, r: 'oldrel'}
24-
# - {os: ubuntu-22.04, r: 'devel'}
25-
- {os: ubuntu-22.04, r: 'release'}
26-
# - {os: ubuntu-22.04, r: 'oldrel'}
19+
#- {os: macOS-latest, r: 'release'}
20+
- {os: windows-latest, r: 'release'}
21+
- {os: ubuntu-latest, r: 'devel'}
22+
- {os: ubuntu-latest, r: 'release'}
23+
- {os: ubuntu-latest, r: 'oldrel'}
2724

2825
env:
2926
R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
@@ -41,13 +38,8 @@ jobs:
4138

4239
- uses: r-lib/actions/setup-r-dependencies@v2
4340
with:
44-
extra-packages: any::rcmdcheck, any::covr
41+
extra-packages: any::rcmdcheck
4542
needs: check
4643

4744
- uses: r-lib/actions/check-r-package@v2
4845

49-
# - name: Test coverage
50-
# if: matrix.config.os == 'ubuntu-22.04' && matrix.config.r == 'release'
51-
# run: |
52-
# covr::codecov(token = "${{secrets.CODECOV_TOKEN}}")
53-
# shell: Rscript {0}

.github/workflows/pkgdown.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples
2+
# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
release:
8+
types: [published]
9+
workflow_dispatch:
10+
11+
name: pkgdown.yaml
12+
13+
permissions: read-all
14+
15+
jobs:
16+
pkgdown:
17+
runs-on: ubuntu-latest
18+
# Only restrict concurrency for non-PR jobs
19+
concurrency:
20+
group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }}
21+
env:
22+
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
23+
permissions:
24+
contents: write
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- uses: r-lib/actions/setup-pandoc@v2
29+
30+
- uses: r-lib/actions/setup-r@v2
31+
with:
32+
use-public-rspm: true
33+
34+
- uses: r-lib/actions/setup-r-dependencies@v2
35+
with:
36+
extra-packages: any::pkgdown, local::.
37+
needs: website
38+
39+
- name: Build site
40+
run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE)
41+
shell: Rscript {0}
42+
43+
- name: Deploy to GitHub pages 🚀
44+
if: github.event_name != 'pull_request'
45+
uses: JamesIves/github-pages-deploy-action@v4.5.0
46+
with:
47+
clean: false
48+
branch: gh-pages
49+
folder: docs

DESCRIPTION

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: SAVM
22
Title: Submerged aquatic vegetation model
3-
Version: 0.0.1
4-
Date: 2025-03-21
3+
Version: 0.0.1.9004
4+
Date: 2025-07-15
55
Authors@R: c(
66
person(given = "Kevin",
77
family = "Cazelles",
@@ -12,11 +12,17 @@ Authors@R: c(
1212
family = "Beauchesne",
1313
role = c("aut"),
1414
email = "david.beauchesne@insileco.io",
15-
comment = c(ORCID = "0000-0002-3590-8161"))
15+
comment = c(ORCID = "0000-0002-3590-8161")),
16+
person(
17+
given = "Paul",
18+
family = "Bozneck",
19+
role = c("aut"),
20+
email = "Paul.Bzonek@dfo-mpo.gc.ca"
1621
)
17-
Description: Here is the description made of sentences.
18-
URL: https://github.com/inSileco/SAVM
19-
BugReports: https://github.com/inSileco/SAVM/issues
22+
)
23+
Description: Submerged aquatic vegetation model developed by the Fish Ecology Science Lab at DFO.
24+
URL: https://github.com/FishEcologyScience/SAVM
25+
BugReports: https://github.com/FishEcologyScience/SAVM/issues
2026
License: GPL-3
2127
Encoding: UTF-8
2228
LazyData: true

NAMESPACE

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
# Generated by roxygen2: do not edit by hand
22

3+
S3method(preview_grid,sav_data)
34
export(compute_fetch)
5+
export(invert_polygon)
46
export(plot_sav_density)
57
export(plot_sav_distribution)
68
export(plot_sav_tmap)
9+
export(preview_grid)
710
export(read_sav)
811
export(read_sav_aoi)
912
export(read_sav_csv)
1013
export(read_sav_pts)
1114
export(sav_model)
15+
import(randomForest)

NEWS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# SAVM (devel)
2+
3+
* Model and plot functions have been adjusted to handle `sf` objects (see #11).
4+
* The element `mean_fetch` returned by `compute_fetch()` is now a `sf` object (see #9).
5+
* `preview_grid()` allows to preview grid (see #8).
6+
* Add more guidance on reading shapefiles in the vignette (see #6).
7+
* `invert_polygon()` has been added to invert polygon (see #6).
8+
* `compute_fetch()` has a new argument `n_bearings` that provides the number of bearings, it replaces `n_quad_seg` (see #4 and #5).
9+
* `compute_fetch()` only compute the mean fetch for all bearings, columns with
10+
suffix `_all` where therefore removed (see #4).

R/compute_fetch.R

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
#' Polygon defining land boundaries used to compute fetch distances.
77
#' @param max_dist {`numeric`}\cr{}
88
#' Maximum fetch distance in kilometers. Fetch beyond this distance is capped.
9-
#' @param n_quad_seg {`integer`}\cr{}
10-
#' Number of segments per quadrant for fetch calculation.
11-
#' Ignored if `wind_weights` is provided.
9+
#' @param n_bearings {`integer`}\cr{}
10+
#' Total number of bearings for fetch calculation (minimal number required is
11+
#' 4, default is 167). Ignored if `wind_weights` is provided.
1212
#' @param wind_weights {`data.frame`}\cr{}
1313
#' A data frame specifying directional weights for wind exposure.
1414
#' Must contain two columns: `direction` (numeric, in degrees) and `weight`
@@ -17,12 +17,13 @@
1717
#' Coordinate reference system (CRS) passed to [sf::st_crs()], used to
1818
#' transform `points` and `polygon`.
1919
#'
20-
#' @details Wind fetch is the unobstructed distance over which wind travels
20+
#' @details
21+
#' Wind fetch is the unobstructed distance over which wind travels
2122
#' across a body of water before reaching a specific point. It plays a crucial
2223
#' role in wave generation, as longer fetch distances allow wind to transfer
2324
#' more energy to the water surface, leading to larger waves.
2425
#'
25-
#' For all points in `points`, 4 × `n_quad_seg` radial transects are generated
26+
#' For all points in `points`, `n_bearings` radial transects are generated
2627
#' by default. If `wind_weights` is specified, the column `direction`, which
2728
#' contains angles in degrees, is used instead to generate the transects. The
2829
#' transects are then clipped with the polygon using [`sf::st_intersection()`],
@@ -32,17 +33,16 @@
3233
#' element in the returned list and it used to generate the second element:
3334
#' `mean_fetch` that included wind fetch averages.
3435
#'
35-
#' Ensure that max_dist is specified in meters. An error will be thrown if the
36+
#' Ensure that `max_dist` is specified in meters. An error will be thrown if the
3637
#' spatial projection of points and polygon is not in a meter-based coordinate
3738
#' system.
3839
#'
39-
#' @return A list of two elements:
40-
#' * `mean_fetch`: data frame with 5 columns:
40+
#' @return
41+
#' A list of two elements:
42+
#' * `mean_fetch`: a `sf` object with 3 features:
4143
#' * `id_point`: point identifier
42-
#' * `fetch`: mean wind fetch based on the four highest values
43-
#' * `weighted_fetch`: mean weighted wind fetch based on the four highest values
44-
#' * `fetch_all`: mean wind fetch based on all values
45-
#' * `weighted_fetch_all`: mean wind weighted fetch based on all values
44+
#' * `fetch_km`: mean wind fetch based on all bearings.
45+
#' * `weighted_fetch_km`: mean weighted wind fetch based on all bearings.
4646
#' * `transect_lines`: a `sf` object containing all radial transect with the
4747
#' same columns as `points` and the following additional columns:
4848
#' * `id_point`: point identifier
@@ -63,36 +63,43 @@
6363
#'
6464
#' @examples
6565
#' \donttest{
66-
#'
6766
#' le_bound <- system.file("example", "lake_erie.gpkg", package = "SAVM") |>
6867
#' sf::st_read()
6968
#' le_pt <- system.file("example", "le_points.geojson", package = "SAVM") |>
7069
#' sf::st_read(quiet = TRUE)
7170
#' res <- compute_fetch(le_pt, le_bound, crs = 32617)
72-
#' # use wind-weight
71+
#' # use wind-weight
7372
#' res2 <- compute_fetch(
74-
#' le_pt, le_bound, max_dist = 20,
75-
#' wind_weights = data.frame(
76-
#' direction = seq(0, 360, by = 360 / 16)[-1],
77-
#' weight = rep(c(0, 1), each = 8)
78-
#' ),
79-
#' crs = 32617)
73+
#' le_pt, le_bound,
74+
#' max_dist = 20,
75+
#' wind_weights = data.frame(
76+
#' direction = seq(0, 360, by = 360 / 16)[-1],
77+
#' weight = rep(c(0, 1), each = 8)
78+
#' ),
79+
#' crs = 32617
80+
#' )
8081
#'
81-
#' # resultat
82+
#' # results
8283
#' res$mean_fetch
8384
#' res2$mean_fetch
8485
#'
8586
#' # visualizing fetch lines
8687
#' plot(le_bound |> sf::st_transform(crs = 32617) |> sf::st_geometry())
8788
#' plot(res$transect_lines |> sf::st_geometry(), add = TRUE, col = 2, lwd = 0.5)
8889
#' }
89-
compute_fetch <- function(points, polygon, max_dist = 15, n_quad_seg = 9, wind_weights = NULL, crs = NULL) {
90+
compute_fetch <- function(
91+
points, polygon, max_dist = 15, n_bearings = 16, wind_weights = NULL, crs = NULL) {
9092
valid_points(points)
9193
points$id_point <- seq_len(nrow(points))
9294
valid_polygon(polygon)
93-
sav_stop_if_not(max_dist > 0)
95+
sav_stop_if_not(max_dist > 0, "`max_dist` must be strictly positive.")
9496
max_dist <- 1e3 * max_dist
95-
sav_stop_if_not(n_quad_seg > 0)
97+
sav_stop_if_not(n_bearings >= 4, "`n_bearings` should be equal or greater than 4.")
98+
if (n_bearings > 64) {
99+
sav_msg_warning(
100+
"Large number of bearings detected, computation may take a long time."
101+
)
102+
}
96103

97104
if (!is.null(crs)) {
98105
if (!is_proj_unit_meter(crs)) {
@@ -127,11 +134,11 @@ compute_fetch <- function(points, polygon, max_dist = 15, n_quad_seg = 9, wind_w
127134

128135
if (is.null(wind_weights)) {
129136
d_direction <- data.frame(
130-
direction = utils::head(seq(0, 360, by = 360 / (n_quad_seg * 4)), -1),
137+
direction = utils::head(seq(0, 360, by = 360 / n_bearings), -1),
131138
weight = 1
132139
)
133140
} else {
134-
sav_msg_info("Using `wind_weights`, ignoring `n_quad_seg`")
141+
sav_msg_info("Using `wind_weights`, ignoring `n_bearings`")
135142
if (all(c("direction", "weight") %in% names(wind_weights))) {
136143
d_direction <- wind_weights[c("direction", "weight")]
137144
valid_direction(d_direction$direction)
@@ -146,7 +153,7 @@ compute_fetch <- function(points, polygon, max_dist = 15, n_quad_seg = 9, wind_w
146153
sav_msg_info("Cropping fetch lines")
147154
fetch_crop <- suppressWarnings(fetch_lines |> sf::st_intersection(polygon))
148155
geom_type <- sf::st_geometry_type(fetch_crop)
149-
# sf::st_intersection() generates multilinestring with extra lines if there
156+
# sf::st_intersection() generates MULTILINESTRING with extra lines if there
150157
# are intersections within the fetch lines
151158
transect_lines <- rbind(
152159
fetch_crop |>
@@ -156,28 +163,33 @@ compute_fetch <- function(points, polygon, max_dist = 15, n_quad_seg = 9, wind_w
156163
remove_detached_ends(points)
157164
) |>
158165
dplyr::arrange(id_point, direction)
159-
160166
transect_lines <- transect_lines |>
161167
dplyr::mutate(transect_length = sf::st_length(transect_lines)) |>
162168
dplyr::group_by(id_point) |>
163-
dplyr::mutate(rank = rank(transect_length, ties.method = "min"))
169+
# using -transect so that the longest are ranked 1
170+
dplyr::mutate(rank = rank(-transect_length, ties.method = "min"))
164171

165172
list(
166-
mean_fetch = transect_lines |>
167-
sf::st_drop_geometry() |>
168-
dplyr::group_by(id_point) |>
169-
# dplyr::mutate(rank = rank(transect_length)) |>
170-
dplyr::summarise(
171-
fetch_km = mean(transect_length[rank < 5]),
172-
weighted_fetch_km = mean(transect_length[rank < 5] * weight[rank < 5]),
173-
fetch_km_all = mean(transect_length),
174-
weighted_fetch_km_all = mean(transect_length * weight)
173+
mean_fetch = points |>
174+
dplyr::left_join(
175+
transect_lines |>
176+
sf::st_drop_geometry() |>
177+
dplyr::group_by(id_point) |>
178+
# dplyr::mutate(rank = rank(transect_length)) |>
179+
dplyr::summarise(
180+
fetch_km = mean(transect_length),
181+
weighted_fetch_km = mean(transect_length * weight)
182+
) |>
183+
dplyr::mutate(
184+
dplyr::across(
185+
!c(id_point),
186+
~ as.numeric(units::set_units(.x, "km"))
187+
)
188+
),
189+
by = "id_point"
175190
) |>
176-
dplyr::mutate(
177-
dplyr::across(
178-
!id_point,
179-
~ as.numeric(units::set_units(.x, "km"))
180-
)
191+
dplyr::select(
192+
c("id_point", "fetch_km", "weighted_fetch_km")
181193
),
182194
transect_lines = transect_lines
183195
)

R/invert_polygon.R

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#' Invert a Polygon
2+
#'
3+
#' @param polygon {`sf`}\cr{}
4+
#' Polygon defining land boundaries that needs to be inverted.
5+
#' @param ratio {`numeric [0-1]`}\cr{}
6+
#' Fraction convex, see [sf::st_concave_hull()]. This may need to be tweaked
7+
#' depending on the form of the polygon to be inverted.
8+
#'
9+
#' @details
10+
#' Utility function that inverts a polygon by drawing a concave hull around
11+
#' `polygon` (see [sf::st_concave_hull()]) and then computes the differences
12+
#' between the polygon and the concave hull (see [sf::st_concave_hull()]).
13+
#'
14+
#' @export
15+
#'
16+
#' @examples
17+
#' \donttest{
18+
#' erie_land <- system.file(
19+
#' "example", "lake_erie_land", "LkErie_Land_fromGLAF_Water_WGS_Feb2020.shx",
20+
#' package = "SAVM", mustWork = TRUE
21+
#' ) |> sf::st_read()
22+
#'
23+
#' erie_land |>
24+
#' sf::st_geometry() |>
25+
#' plot(col = 1)
26+
#'
27+
#' erie_land |>
28+
#' sf::st_geometry() |>
29+
#' invert_polygon() |>
30+
#' plot(col = 1)
31+
#' }
32+
33+
invert_polygon <- function(polygon, ratio = 0.5) {
34+
os2 <- sf::sf_use_s2()
35+
on.exit(suppressMessages(sf::sf_use_s2(os2)))
36+
suppressMessages(os2 <- sf::sf_use_s2(FALSE))
37+
sf::st_difference(polygon |> sf::st_concave_hull(ratio = ratio), polygon)
38+
}

0 commit comments

Comments
 (0)