The msens package reads the published
Marine Sensitivity Atlas (v8
“marine-atlas”) — species distribution models from many sources merged
onto a global 0.05° grid, scored for marine sensitivity over the US
study area, and released as partitioned Parquet on S3 with a STAC catalog and titiler tile services.
Everything below runs against the live public release —
no download, no credentials.
1. Attach the atlas
A single attach_atlas() call configures a DuckDB
connection to the S3 release and creates the table
views, so every reader and score helper composes off
one connection. Use anon = TRUE for anonymous public reads
of the released tables.
con <- attach_atlas(anon = TRUE)
# the atlas tables are now queryable by name
DBI::dbGetQuery(con, "SELECT table_name FROM information_schema.tables
WHERE table_schema = 'main' ORDER BY 1")$table_name
#> [1] "cell" "cell_metric" "dataset" "metric" "model"
#> [6] "native_asset" "taxon" "zone" "zone_cell" "zone_metric"2. Browse — datasets and taxa
tbl(con, "dataset") |>
arrange(sort_order) |>
select(ds_key, name_short, response_type, native_format) |>
collect() |>
knitr::kable()| ds_key | name_short | response_type | native_format |
|---|---|---|---|
| ms_merge | Marine Sensitivity Merged Model, 2026-03-25 | mixed | parquet |
| am | AquaMaps suitability for all marine taxa (except birds) globally, 2019 | suitability | raster |
| ca_nmfs | NMFS Core Areas, 2019 | range | vector |
| ch_nmfs | NMFS Critical Habitat for USA, 2025-05-12 | range | vector |
| ch_fws | FWS Critical Habitat for USA, 2025-05-05 | range | vector |
| rng_fws | FWS Current Range Maps, 2025-07-23 | range | vector |
| bl | BirdLife Birds of the World, 2024 | range | vector |
| rng_iucn | IUCN Red List Range Maps, 2025-2 | range | vector |
| rng_turtle_swot_dps | SWOT Global + NMFS DPS turtle ranges | range | vector |
| gm | NOAA SEFSC (density) | density | vector |
| nc | NOAA NCCOS (density) | density | raster |
How many merged taxa are valid globally vs. specifically in US waters, per scoring category?
tbl(con, "taxon") |>
group_by(sp_cat) |>
summarise(
valid_global = sum(as.integer(is_valid_global), na.rm = TRUE),
valid_us = sum(as.integer(is_valid_global & is_valid_usa), na.rm = TRUE),
.groups = "drop") |>
arrange(desc(valid_global)) |>
collect() |>
knitr::kable()| sp_cat | valid_global | valid_us |
|---|---|---|
| invertebrate | 10080 | 9364 |
| fish | 9242 | 6286 |
| bird | 8045 | 880 |
| coral | 1206 | 783 |
| primary_producer | 405 | 319 |
| mammal | 133 | 75 |
| turtle | 6 | 6 |
| reptile | 2 | 2 |
| amphibian | 0 | 0 |
3. Search the STAC catalog
Each dataset is a STAC collection; each model is an item whose assets
point at the published Cloud-Optimized GeoTIFFs, PMTiles, and Parquet.
Read the static catalog with
rstac::read_stac() (use rstac::stac() only for
STAC API servers).
library(rstac)
v8 <- read_stac("https://file.marinesensitivity.org/stac/v8/collection.json")
# dataset collections under v8
basename(dirname(vapply(Filter(\(l) l$rel == "child", v8$links), \(l) l$href, character(1))))
#> [1] "ms_merge" "am" "ca_nmfs"
#> [4] "ch_nmfs" "ch_fws" "rng_fws"
#> [7] "bl" "rng_iucn" "rng_turtle_swot_dps"
#> [10] "gm" "nc"
# one dataset's item + its published assets (native vs. gridded-model representations).
# item links are relative to the collection, so resolve against its base URL.
ch_base <- "https://file.marinesensitivity.org/stac/v8/ch_fws/"
ch <- read_stac(paste0(ch_base, "collection.json"))
item_href <- Filter(\(l) l$rel == "item", ch$links)[[1]]$href # "./…json"
it <- read_stac(paste0(ch_base, sub("^\\./", "", item_href)))
tibble(
asset = names(it$assets),
type = vapply(it$assets, \(a) if (is.null(a$type)) "" else a$type, character(1)),
href = vapply(it$assets, \(a) basename(a$href), character(1))) |>
knitr::kable()| asset | type | href |
|---|---|---|
| data | application/vnd.apache.parquet | dist_merged |
| pmtiles_native | application/vnd.pmtiles | ch_fws |
| cog_model | image/tiff; application=geotiff; profile=cloud-optimized | vec_grid |
Python reads the same catalog with
pystac:
4. Retrieve and map a distribution
The native_asset table maps each model to its published
tile services. Retrieve a merged taxon’s whole-range Cloud-Optimized
GeoTIFF and hand it to cog_tile_url(), which builds an XYZ
tile template that drops straight into a leaflet map. (For the US-scored
surface instead, cell_tile_url(mdl_key = mk) serves the
model_cell cells.)
library(leaflet)
mk <- "ms_merge|WORMS:137209" # Dermochelys coriacea (Leatherback Turtle)
# the published whole-range COG for this merged taxon
cog <- tbl(con, "native_asset") |>
filter(ms_merge_key == mk, ds_key == "ms_merge") |>
pull(asset_url) |> head(1)
url <- cog_tile_url(cog, colormap = "spectral_r", rescale = c(1, 100))
leaflet() |>
addProviderTiles("CartoDB.DarkMatter") |>
addTiles(urlTemplate = url, options = tileOptions(opacity = 0.9)) |>
setView(0, 20, zoom = 1)5. Map the sensitivity score surfaces
Scoring produces a per-cell value for each metric —
the equal-weight composite marine-sensitivity score and its per-category
components (bird, mammal, fish, …, ecoregion-rescaled to 0–100). These
live in the cell_metric table and are served as raster
tiles by titiler-v8, which runs a cell_id → value SQL query
over the scored cells on demand (there is no pre-rendered COG per metric
— the cells are the raster).
library(glue)
# the composite plus each per-category, ecoregion-rescaled component
score_metrics <- tbl(con, "metric") |>
filter(grepl("equalweights$|^extrisk_[a-z]+_ecoregion_rescaled$", metric_key)) |>
pull(metric_key) |> sort()
# a readable label from the metric_key (descriptions are sparse in the release)
label_of <- function(mk) ifelse(
grepl("equalweights$", mk), "composite (equal-weight)",
paste0(sub("^extrisk_([a-z]+)_.*$", "\\1", mk), " extinction-risk"))
# the (cell_id, value) SELECT titiler renders — the same path the scores app uses
cell_sql <- function(mk) glue(
"SELECT cm.cell_id, cm.val AS value FROM cell_metric cm ",
"JOIN metric m ON cm.metric_seq = m.metric_seq WHERE m.metric_key = '{mk}'")Ask titiler for each surface’s raster statistics — proof the served tiles carry real per-cell values (not a static image), computed live over the scored cells:
stats <- lapply(score_metrics, \(mk) cell_stats(cell_sql(mk)))
names(stats) <- score_metrics
purrr::imap_dfr(stats, \(s, mk) tibble::tibble(
surface = label_of(mk), cells = s$n,
min = round(s$min, 1), mean = round(s$mean, 1),
median = round(s$p50, 1), p98 = round(s$p98, 1), max = round(s$max, 1))) |>
arrange(desc(mean)) |>
knitr::kable(caption = "titiler raster statistics per score surface (US study area)")| surface | cells | min | mean | median | p98 | max |
|---|---|---|---|---|---|---|
| mammal extinction-risk | 623212 | 0 | 40.6 | 38.8 | 89.3 | 100 |
| turtle extinction-risk | 406103 | 0 | 36.8 | 23.8 | 94.2 | 100 |
| bird extinction-risk | 623212 | 0 | 36.2 | 38.6 | 83.5 | 100 |
| composite (equal-weight) | 623212 | 0 | 22.1 | 20.0 | 56.0 | 96 |
| fish extinction-risk | 589800 | 0 | 18.1 | 11.3 | 71.3 | 100 |
| invertebrate extinction-risk | 623212 | 0 | 14.9 | 3.6 | 74.7 | 100 |
| coral extinction-risk | 542955 | 0 | 11.1 | 3.5 | 65.6 | 100 |
Hand each metric’s SQL to cell_tile_url() for a leaflet
layer, rescaled to its own range. A layers control toggles between the
composite and its components — one raster surface at a time:
# add components first, composite last (so it is the default-visible base layer)
ordered <- c(setdiff(score_metrics, grep("equalweights$", score_metrics, value = TRUE)),
grep("equalweights$", score_metrics, value = TRUE))
m <- leaflet() |> addProviderTiles("CartoDB.DarkMatter") |> setView(-105, 38, zoom = 3)
for (mk in ordered) {
s <- stats[[mk]]
m <- m |> addTiles(
urlTemplate = cell_tile_url(cell_sql(mk), colormap = "spectral_r",
rescale = c(s$min, s$max)),
group = label_of(mk), options = tileOptions(opacity = 0.9))
}
m |> addLayersControl(
baseGroups = label_of(rev(ordered)), # composite listed first
options = layersControlOptions(collapsed = FALSE))6. Score a Program Area
scores_for_pra() returns the per-category
marine-sensitivity scores for a BOEM Program Area — ready for a flower
plot, where each petal’s height is the category score.
sc <- scores_for_pra(con, "ALA") # ALA = a BOEM Program Area key
knitr::kable(sc[c("component", "score")], digits = 1)| component | score |
|---|---|
| bird | 55.1 |
| coral | 15.3 |
| fish | 37.5 |
| invertebrate | 14.6 |
| mammal | 35.5 |
| primary producer | 10.4 |
| turtle | 70.2 |
| primprod | 1.4 |
plot_flower(
sc,
fld_category = component,
fld_height = score,
fld_width = even,
tooltip_expr = "{component}: {round(score, 1)}",
title = "ALA")7. Species
The taxon table carries each merged taxon’s category,
governing extinction-risk score, and protection flags — e.g. the
highest-risk mammals in US waters:
tbl(con, "taxon") |>
filter(sp_cat == "mammal", is_valid_usa, er_score >= 80) |>
select(scientific_name, common_name, extrisk_code, er_score, is_mmpa) |>
arrange(desc(er_score), scientific_name) |>
head(10) |>
collect() |>
knitr::kable()| scientific_name | common_name | extrisk_code | er_score | is_mmpa |
|---|---|---|---|---|
| Balaena mysticetus | Bowhead Whale | NMFS:EN | 100 | TRUE |
| Balaenoptera borealis | Sei Whale | NMFS:EN | 100 | TRUE |
| Balaenoptera edeni | Bryde’s Whale | NMFS:EN | 100 | TRUE |
| Balaenoptera musculus | Blue Whale | NMFS:EN | 100 | TRUE |
| Balaenoptera physalus | Fin Whale | NMFS:EN | 100 | TRUE |
| Balaenoptera ricei | Rice’s Whale | NMFS:EN | 100 | TRUE |
| Delphinapterus leucas | Beluga Whale | NMFS:EN | 100 | TRUE |
| Eschrichtius robustus | Gray Whale | NMFS:EN | 100 | TRUE |
| Eubalaena glacialis | North Atlantic Right Whale | NMFS:EN | 100 | TRUE |
| Eubalaena japonica | North Pacific Right Whale | NMFS:EN | 100 | TRUE |
To go the other way — which species occur in a set of cells
— use species_for_cells(con, cells), which joins the merged
model_cell surface to taxon (that view needs
credentials, so call attach_atlas() without
anon).