Changelog
Source:NEWS.md
obisindicators 0.8.0
-
New
obis_place_indicators(): indicators per place, pooled from area-weighted H3 cells. It takes a place-cell membership (place_id,h3,share, as the Ocean Metrics gazetteer publishes it per collection) and, for each place, sums every species’ records over the member cells weighted by the cell’s share of area inside the place (ni = sum(records * share); the gazetteer keeps shares of at least 1%), then computesn,sp, Shannon, Simpson and ES(50) with the store’s indicator SQL. Layersallandeov(per EOV), each for all years and per decade (1960-2020, as the export’s decade layers); alsocells(member cells with records) andarea(their summed share). These are true per-place indices of the pooled, area-weighted counts, not means of hexagon values:nis fractional because of the weighting, richness counts every species with any weighted record inside the place, and ES(50) is NA where the weightednis below 50. - New
obis_place_membership()resolves a gazetteer collection (slug, directory, URL ormembership.json) to its membership parquet files at one resolution. -
obis_h3_export_parquet()gainsplace_collections(defaultNULL, no change): a named list of collections, each written as one fileplace/coll=<slug>/data_0.parquetwith every place, layer and decade, listed infiles.parquet(layerplace) andrelease.json(layers$place, with each collection’s membership version).stats.parquetdoes not cover it. The monthly refresh turns it on withOBIS_PLACE_COLLECTIONS="<slug> ..."(optionallyGAZETTEER_BASE), passed through byrun_refresh_docker.sh. - The shared indicator SQL (
.h3t_indicators_sql()) is documented for fractional counts; it needed no change, and integer results are unchanged.
obisindicators 0.7.3
-
release.json’sobis_snapshotis the OBIS data date, the date of the newest object ins3://obis-open-data/occurrence/the store was built from, not the date the refresh ran. OBIS updates that folder one dataset at a time and publishes no export date, sorefresh_obis_h3.shreads it off the synced files (whichaws s3 syncstamps with their S3LastModified) and passes it asOBIS_DATA_DATE; without it, the date in the release tag is used as before. For v20261012 that is 2026-10-11 (synced 2026-10-12). The release tag stays the sync date. -
latest.jsonalso carriesworms_version, the ChecklistBank WoRMS version the taxonomy came from.
obisindicators 0.7.2
-
Family is a precomputed taxon rank.
H3T_IDX_RANKSgains"family", so the store’sidx_h3_taxonand the export’staxon/rank=family/layer carry every family (about 5,800 in the v20260728 store), and a single-family h3t query takes the precomputed path.migrate_add_idx_h3_taxon.Radds it to an existing store. -
The monthly
obis-h3refresh runs in Docker on the Mac mini. Newdata-raw/docker/Dockerfile(R 4.6.1, CRAN from a dated Posit Package Manager snapshot, duckdb 1.5.5, the h3/spatial/httpfs extensions, AWS CLI v2) anddata-raw/run_refresh_docker.sh(Colima VM started and stopped per run, data on the external drive mounted at/share/data, the new store rsynced to msens for h3t).refresh_obis_h3.shruns R directly whenBUILD_CONTAINERis empty (inside the image); msens’sdocker exec plumbermode is unchanged. -
refresh_obis_h3.shsources every file inR/for the export: the subset it sourced before lackedeov.R, so the export stopped attaxon_groups(could not find function "obis_eov_label"). -
obis_h3_export_parquet()computesstats.parquetone layer and variable at a time: the single UNPIVOT over every layer held all values inquantile_cont()states at once and, with the family rank, was killed for lack of memory in a 24 GiB VM. Same rows, values and column types (regression test). -
The WoRMS taxonomy is refreshed by the monthly run. New step (a2) in
refresh_obis_h3.sh: when theversionof ChecklistBank dataset 2011 (WoRMS’s monthly DwC-A, public) differs fromtaxon.parquet.json, it downloads the archive, runsbuild_taxon_parquet.Rand records the version, whichrelease.jsoncarries asworms_version. New flags--skip-taxonomyand--taxonomy-only. The build’s gap-fill of AphiaIDs missing from the download (WoRMS REST) is unchanged.run_refresh_docker.shskips a separate mount for a checkout that sits inside the data folder (Colima refuses overlapping mounts). -
release.json’sobisindicators_versioncomes from the checkout’sDESCRIPTIONwhenOBIS_PKG_ROOTis set (.obisindicators_version()), so the image needs no installed package.
obisindicators 0.7.1
-
obis_h3_export_parquet()splits the fine resolutions by parent cell, so a browser loads only the cells covering its viewport instead of a whole 33 MBall/res=7file. New argumentsparent_res(defaultc("6" = 2L, "7" = 3L): res 6 by its res-2 parent, res 7 by its res-3 parent, for theallandeovlayers),parent_res_taxon(default the res-0 base cell, which keeps the taxon layer to thousands rather than millions of files),parent_min_rows(default 50000: smaller (layer key, res) partitions stay whole) andmax_open(partitions per COPY). Split partitions are written to.../res=<r>/p=<parent h3>/data_0.parquet; res 1-5 and the decade layers stay whole.files.parquetgains aparentcolumn (NULL for whole files) andrelease.jsondocuments each layer’sparent_resand the rule (parents). -
New
data-raw/refresh_obis_h3.sh: the monthlyobis-h3release pipeline (sync OBIS open data, build the store,obis_h3_export_parquet(), validate against the previous release, upload to S3, then writeobis-h3/latest.json). Scheduled fromoceanmetrics/erddap-placeson the msens runner; documented indata-raw/README.md. Supports--skip-sync/-build/-export/-upload,--releaseand--dry-run.
obisindicators 0.7.0
-
New
obis_h3_export_parquet(): export a store to hive-partitioned Parquet for a serverless web app (static hosting + DuckDB-WASM + deck.glH3HexagonLayer). Writes<dir_out>/<release>/withall/res=N/(idx_h3),eov/eov=<eov>/res=N/(idx_h3_eov),taxon/rank=<rank>/taxon=<taxon>/res=N/(idx_h3_taxon), and two layers computed fromocc_h3per decade (decade/all/decade=<yyy0>/res=N/,decade/eov/eov=<eov>/decade=<yyy0>/res=N/; res 1-5 and decades 1960-2020 by default). Every file hash3(lowercase hex string),cell_id,n,sp,shannon,simpson,es. Also writesstats.parquet(min, 2nd, 50th, 98th percentile, max and cell count per layer x key x res x variable, for colour ramps),taxon_groups.parquet(the taxa and EOVs on offer, with totals and on-disk / URL paths),files.parquet(a manifest of every file with its row count and size, since HTTP cannot glob) andrelease.json(release, snapshot, package and DuckDB versions, row counts per layer, layout and columns). Each partition is exactly onedata_0.parquet; the taxon layer is written in batches of at most 100 partitions per COPY to stay under the OS open-file limit. Partition values are percent-encoded by DuckDB;url_pathgives the form to fetch over HTTP. - The indicator SQL behind
idx_h3,idx_h3_taxonandidx_h3_eovnow comes from one internal builder (.h3t_indicators_sql()), which the decade layers of the export reuse. The output of the store build is unchanged. -
jsonliteadded to Suggests (used by the export).
obisindicators 0.6.1
-
Precomputed store articles: the figures now show on the pkgdown site (and in the
paper/gallery on GitHub). The vignettes that query anobis_h3store (scaling,taxon_children) rendered code-only (eval = FALSE) because the store is not available on GitHub Actions. They move tovignettes/articles/(pkgdown-only, excluded from the package build) as*.Rmd.origsources thatdata-raw/precompute_articles.Rknits locally against a store into committed*.Rmd+figures/; CI renders those as-is. The tile maps stay live.- The
paper/Quarto notebooks are folded into the articles, which now double as the manuscript’s figure notebooks:scaling(Tables 2, 5; Fig. 3),taxon_children(Table 3; Fig. 4), and new articleseov(Table 4; Figs. 1–2),decadal(Fig. 5) andplaces(Fig. 6). Each article saves its manuscript figure + CSV topaper/figures/(now committed) viapaper/_common.R;paper/README.mdis the figure gallery and index. - Site navigation: articles are grouped into “Shipped data” and “The obis_h3 store”. No changes to exported functions.
- The
-
Analysis-side helpers for the paper’s figures and case studies — the tile-SQL builders now have data-frame counterparts, so every figure in
paper/is a call to a tested, exported function rather than notebook code. No new indicator math: everything runs the SQL already pinned tocalc_indicators()/calc_spue(), or calls those references directly.- Store (
R/store.R):obis_store_connect()(read-only +LOAD h3),obis_store_stats()(tables, totals, cells by resolution — Table 2),obis_h3t_query()(run any tile SQL at a fixed resolution, keyed on the hex string),obis_cell_indicators()(n/sp/shannon/es per cell for any filter, readingidx_h3/idx_h3_eovwhen it can),obis_bench()+obis_bench_queries()(cold/warm timings of the four serving paths),h3_res_table(). - Scale (
R/scale.R):calc_scale_curves()andcalc_spue_scale()— occupied cells, records-per-cell quantiles, ES-eligible fraction, medians and reliability floors across H3 resolutions (Fig. 3). - Comparisons (
R/compare.R):calc_rank_vs_subtree()+obis_rank_presets()(records by DwC rank column vs AphiaID subtree — Table 3),calc_eov_totals()+compare_eov_totals()(per-EOV totals and before/after a taxonomy change — Table 4),calc_spue_cells(),h3_raster_to_cells()(aggregate an SDM raster to H3 by pixel centres or hexagon centroids) andcompare_spue_sdm()(Spearman + calibration bins, effort-gated — Fig. 4). - Periods (
R/temporal.R):calc_period_indicators()andcalc_period_change()(decadal ES50/richness per cell, change where both decades are reliable — Fig. 5). - Places (
R/place.R):place_cells()andcalc_place_indicators()(roll H3 cells up to sanctuary/EEZ polygons and compute indicators withcalc_indicators()— Fig. 6). - Visualize (
R/visualize.R):hex_sf()(cells → dateline-wrapped polygons),gmap_cells()(map a per-cell table with an optional reliability mask),plot_scale_curves(),plot_spue_sdm();gmap_indicator()gains abboxargument and drops the deprecatedaes_string(). - Tests:
tests/testthat/test-paper-helpers.Ron a new synthetic fixture (helper-paper.R: turtles + birds with DwC ranks and years, bakedidx_h3andidx_h3_eov) asserts exact values for every helper, including precomputed-vs-live agreement ofobis_cell_indicators(). -
paper/: Quarto notebooks, one per figure/table (store stats + benchmarks, rank vs subtree, gap-fill EOV totals, EOV maps, scale curves, SPUE vs SDM, decadal change, place roll-ups), driven byOBIS_H3_DUCKDB; pluspaper/build_demo_store.Rto build a regional demo store (Gulf of Mexico + Caribbean) from a local OBIS export and a WoRMS table. - Suggests:
terra(raster aggregation),patchwork(figure panels).
- Store (
obisindicators 0.5.0
-
Essential Ocean Variables (EOVs) — new
R/eov.Rmaps the GOOS/IOOS biology & ecosystems EOVs onto the H3 store. The IOOS Marine Life Data Network defines each EOV as a handful of root WoRMS AphiaIDs (33 seeds across 7 EOVs: fish, hardCorals, mangroves, marineMammals, seabirds, seagrasses, seaTurtles), which is exactly a multi-seed version of the existing subtree walk.-
obis_eov_seeds()/obis_eov_aphiaid()— the EOV definitions. -
obis_eov_sql()— tile SQL for an EOV, routing to a precomputed layer when it can and to the live AphiaID-subtree path when it must (year filters, several EOVs at once, or a store without the layer baked). -
obis_eov_bake()— addseov(membership) andidx_h3_eov(precomputed indicators, res 1-7) to a store; now also a step inbuild_obis_h3_duckdb(), skipped with a note whentaxonis absent. - New driver
data-raw/migrate_add_eov.R.
-
-
WoRMS gap-fill — new
R/taxon_gapfill.Rcloses a silent coverage hole. The bulk WoRMStaxon.txtdownload is not a complete cover of the AphiaIDs OBIS carries: on the 2026-07 global store 12,021 of 167,190 distinctocc_h3.aphiaidvalues (~7%, covering 8.3M of 121.9M records) were absent fromtaxon, so they were invisible to everyaphiaid/ EOV / SPUE query.-
obis_taxon_orphans()— report the gap. -
wm_aphia_records()— batched, parallel WoRMS REST lookups (AphiaRecordsByAphiaIDs, 50 ids/request), the per-id supplement to the bulk download. -
obis_taxon_fill_gaps()— fills orphans and iterates to transitive closure, because inserting an orphan whose ancestors are still missing leaves it disconnected from any seed above it. Warns and reportsclosed = FALSErather than implying a whole tree ifmax_roundsis hit. - New driver
data-raw/migrate_fill_taxon_gaps.R. - Adds
httr2to Suggests.
-
Two taxon-group presets in the h3-db app returned zero records (fixed in
MarineSensitivity/apps/h3-db, noted here because it motivated the EOV design): filtering the DwCclasscolumn cannot match when WoRMS files the name at another rank —class='Actinopterygii'(a Gigaclass; OBIS’s class isTeleostei, 44.2M records) andclass='Anthozoa'(a Subphylum; OBIS usesHexacorallia/Octocorallia) both matched nothing. AphiaID subtree walking is rank-agnostic and immune to this.Parity:
.h3t_idx_eov_sql()is a fifth copy of thecalc_indicators()ES(n) term and is pinned cell-for-cell by the newtests/testthat/test-eov-parity.R.
obisindicators 0.4.2
-
Vignette H3 tile maps now actually render (the
mapgl/MapLibre chunks that 0.4.1 still left as code-only):-
vignette("h3t")renders the interactive ES50 hexagon map;vignette("taxon_children")renders the Cetacea-records map and the SPUE map. These are client-side MapLibre widgets — tiles are fetched from the deployedh3tservice in the reader’s browser, so they need no store/S3 at build time. -
New Remote dependency: the maps require the h3t-antimeridian fix in
mapgl(renders H3 hexagons that cross the dateline), pinned viaRemotes: mapgl=bbest/mapgl@fix/h3t-antimeridian(walkerke/mapgl#211). Switch to upstreammapglonce that PR merges. - Fixed a latent bug in the
h3tmap example —interpolate(values=)now has the same length asstops=(a 3-stop viridis ramp), which previously would have errored had the chunk been evaluated. -
vignette("scaling")stays code-only: every chunk runs live benchmark queries against the deployed DuckDB store (no map widgets), which CI cannot reach.
-
obisindicators 0.4.1
-
Vignettes now render their evaluable R chunks on the pkgdown site instead of showing code-only:
-
vignette("h3")runs the full h3-grid →calc_indicators()→gmap_indicator()pipeline on shippedocc_1Mand renders the ES(50) map (only thedeckglinstall/demo stays code-only). -
vignette("taxon_children")now renders the generatedWITH RECURSIVEtile SQL and tile URLs fromobis_h3t_sql(aphiaid=)/obis_spue_sql()(pure builders, no store needed); only the DB-connection andmapglwidget chunks stay code-only. -
vignette("h3t")suppresses package-load messages so the evaluatedobis_h3t_sql()output is clean. - Chunks that genuinely require the deployed S3 / DuckDB store /
h3ttile service (vignette("scaling"), the build/deploy/map chunks) remain illustrative code, as they cannot run in CI.
-
obisindicators 0.4.0
-
Automatic per-tile spatial pruning via
hex_prune(supersedes 0.3.0’s client-side{{bbox}}placeholder). The tile bbox is fully determined by thez/x/ytile address, so the client no longer states it — theh3tserver derives each tile’s covering coarse H3 cells fromz/x/yand injects the prune itself:-
build_obis_h3_duckdb()now materializes a coarse H3-parent columnhex_prune = h3_cell_to_parent(cell_id, LEAST(res, H3T_PRUNE_RES))(res 3) onocc_h3andidx_h3and clusters both by(res, hex_prune, cell_id). This replaces the interimlat/lngclustering — pruning on the H3 index is 2D (tighter than a latitude band), needs no extra float columns, and the covering ids are canonical H3 (they match across the R build and the server with no version drift). Store schema change — rebuild, or migrate withdata-raw/migrate_add_spatial_cluster.R(dropslat/lng, addshex_prune). -
obis_h3t_sql()/obis_spue_sql()now emit plain per-resolution SELECTs — thebbox_placeholderargument is removed. The server (MarineSensitivity/server/h3t,CalCOFI/api-h3t-py) rewrites the query to addhex_prune IN (<covering cells>)to any scan of a table carrying that column; stores without it are untouched. - The math is unchanged and still pinned to
calc_indicators()/calc_spue();test-h3t-prune.Rasserts the prune is result-preserving (ahex_prune-pruned scan + the server’s outer centroid filter returns exactly the same cells and values as the unpruned scan).
-
obisindicators 0.3.0
-
Spatial (bbox) tile pruning (interim — the
{{bbox}}placeholder andlat/lngclustering here were replaced by thehex_pruneapproach in 0.4.0; kept for history). Liveocc_h3/idx_h3tile maps were pruned per tile instead of aggregating the whole globe for every tile:-
build_obis_h3_duckdb()materialized cell-centroidlat/lngcolumns onocc_h3andidx_h3and clustered both by(res, lat, lng). -
obis_h3t_sql()/obis_spue_sql()gained abbox_placeholderargument (default"{{bbox}}") that theh3tservice substituted per tile with alat/lngpredicate. - The math stayed pinned to
calc_indicators()/calc_spue(); the prune was asserted result-preserving.
-
obisindicators 0.2.0
- Added WoRMS taxonomy children resolution and an effort proxy (SPUE) so OBIS can be filtered by any WoRMS AphiaID at any rank from the local snapshot (see
vignette("taxon_children"),vignette("scaling")):-
obis_taxon_children()/obis_taxon_subtree_sql()recursively walk ataxontable (baked into the store;data-raw/build_taxon_parquet.R+data-raw/migrate_add_taxon.R) to return every descendant taxon. -
obis_h3t_sql(aphiaid=)serves arbitrary-rank children maps by filteringocc_h3.aphiaidto the resolved subtree via aWITH RECURSIVECTE. -
calc_spue()(R reference) +obis_spue_sql()compute the presence-only effort proxyrecords(target subtree) / records(effort subtree)per cell, pinned bytest-spue-parity.R.
-
- Added an H3 tiling (h3t) workflow to serve indicators as on-demand H3 hexagon map tiles (see
vignette("h3t")):-
build_obis_h3_duckdb()builds an authoritative DuckDB store from OBIS open-data parquet — a precomputed all-taxa indicator layer (idx_h3, res 1–7) plus a species-level store (occ_h3, tiers 3/5/7) for on-the-fly taxon/year-filtered queries. The ES50/Shannon/Simpson/richness math is the SQL translation ofcalc_indicators(), pinned by a new parity test. -
obis_h3t_sql()/obis_h3t_url()compose the validated read-onlySELECTand the base64-encoded tile URL for theh3tservice (MarineSensitivity/server/h3t) andmapgl::add_h3t_source(). - canonical SQL in
inst/sql/; server build driver indata-raw/build_obis_h3_duckdb.R.
-
obisindicators 0.0.2
- Renamed functions for consistency:
-
calc_es50()->calc_indicators() -
gmap_metric()->gmap_indicator()
-
- Fixed
gmap_indicator()to use default Robinson projection. - Updated vignettes with name changes and use of “indicators” over “metrics”.
- Supplemented documentation for
calc_indicators()with expected input and output columns to data frame. - Added documentation for
occ_fkandocc_1960stoocc_2010s - Made generation of occ_* datasets more reproducible with
set.seed()and sampled versions of dataset to minimize file size on Github indata-raw/occ.R.