Writes the indicator layers of a store built by build_obis_h3_duckdb() to
a directory of hive-partitioned Parquet files that a static web app can read
directly with DuckDB-WASM (one small file per map layer and H3 resolution):
Usage
obis_h3_export_parquet(
con,
dir_out,
release,
res_decade = 1:5,
parent_res = c(`6` = 2L, `7` = 3L),
parent_res_taxon = c(`6` = 0L, `7` = 0L),
parent_min_rows = 50000,
max_open = EXPORT_MAX_OPEN,
compression = "zstd",
overwrite = FALSE,
decades = seq(1960L, 2020L, by = 10L),
esn = 50L,
place_collections = NULL,
memory_limit = NULL,
threads = NULL,
verbose = TRUE
)Arguments
- con
connection from
obis_store_connect()(read-only is fine).- dir_out
output directory; files go under
dir_out/release/.- release
release tag, e.g.
"v20260728"(the date of the OBIS sync the store was built from); recorded inrelease.json. Itsobis_snapshotis the environment variableOBIS_DATA_DATE(the newest OBIS object's date, whichrefresh_obis_h3.shpasses) when that is aYYYY-MM-DDdate, else the date in the tag.- res_decade
H3 resolutions for the decade layers (default 1-5).
- parent_res
named integer vector, fine resolution -> parent resolution, for the
allandeovlayers: those resolutions are written one file per parent cell (res=<r>/p=<parent>/). Defaultc("6" = 2L, "7" = 3L);NULLwrites every resolution whole.- parent_res_taxon
the same for the taxon layer (default
c("6" = 0L, "7" = 0L), the base cell).- parent_min_rows
a (layer key, res) with at most this many rows is written whole even when its res is in
parent_res(default 50000).- max_open
max partitions written by one COPY (default 100); each open partition holds a file open, so keep it under the process limit (
ulimit -n, 256 on macOS by default).- compression
Parquet compression codec (default
"zstd").- overwrite
replace an existing
dir_out/release/(default FALSE: stop).- decades
decades (first year) to export in the decade layers; default 1960-2020. NULL exports every decade present.
- esn
expected sample size for ES(n) in the decade layers; default 50 (as in the store).
- place_collections
optional named list (or character vector), slug = the gazetteer collection's membership (a directory or URL, its
membership.json, or anythingobis_place_indicators()takes asmembership), e.g.list(eez = "<gazetteer base>/eez"). DefaultNULL: no place layer.- memory_limit
optional DuckDB
memory_limit(e.g."24GB").- threads
optional DuckDB thread count (e.g.
8).- verbose
print progress and the summary (default TRUE).
Details
<dir_out>/<release>/
release.json build metadata, row counts, layout, columns, file manifest
all/res=<1..5>/data_0.parquet idx_h3 (all taxa, all years)
all/res=<6,7>/p=<parent>/data_0.parquet the fine res, one file per parent cell
eov/eov=<eov>/res=<r>/data_0.parquet idx_h3_eov
taxon/rank=<rank>/taxon=<taxon>/res=<r>/ idx_h3_taxon (phylum / class / order / family)
decade/all/decade=<yyy0>/res=<r>/ per decade, all taxa (res in res_decade)
decade/eov/eov=<eov>/decade=<yyy0>/res=<r>/ per decade and EOV (res in res_decade)
place/coll=<slug>/data_0.parquet per-place pooled indicators (only with place_collections)
stats.parquet value ranges per layer x key x res x variable
taxon_groups.parquet the taxa and EOVs available, with totals and paths
files.parquet manifest: layer, path, url_path, rows, bytes per fileEvery indicator file has the columns h3 (lowercase hex string), cell_id,
n, sp, shannon, simpson, es; the partition keys live in the path
(read with hive_partitioning = true). The decade layers are computed from
occ_h3 with the same SQL builder as idx_h3 / idx_h3_eov (so the math is
identical to the store): n = records, sp = distinct species, Shannon,
Simpson and ES(esn), grouped by cell and decade = floor(date_year / 10) * 10; records without a year are left out of the decade layers. EOV membership
is the store's eov table (occ_h3.aphiaid = eov.taxonID), as in
obis_eov_bake(). Layers whose source tables are missing (e.g. no
idx_h3_eov in a store without taxonomy) are skipped with a message.
Partition values are percent-encoded by DuckDB (a space becomes %20), and
DuckDB decodes them on read. A static web server decodes the URL once, so a
browser must request the on-disk name with % itself encoded (%2520);
taxon_groups.parquet carries both path (on disk) and url_path (to
fetch), and files.parquet lists every file with both forms. Each partition
is exactly one data_0.parquet.
Parent partitions for the fine resolutions. A whole res-7 layer is tens
of MB, too much for a browser to load at once, so the resolutions named in
parent_res are split by parent cell: every row of all/res=7/ goes to
all/res=7/p=<h3 of its res-3 parent>/data_0.parquet (default res 6 by its
res-2 parent, res 7 by its res-3 parent), and a viewer loads only the parent
cells that cover its viewport. The taxon layer uses parent_res_taxon
(default the res-0 base cell: res-3 parents would make over a million
files across ~5k taxa). A (layer key, res) with at most parent_min_rows
rows stays one whole file, since splitting it would only add requests. The
decade layers are never split (their default resolutions are 1-5).
files.parquet has a parent column (the partition's parent cell, NULL for
a whole file; its resolution is the parent resolution), and each layer in
release.json lists the parent_res actually used.
Place layer (optional). With place_collections, each gazetteer
collection gets one file, place/coll=<slug>/data_0.parquet, with the
indicators of every place pooled from its res-7 member cells, records
weighted by each cell's share of area inside the place
(obis_place_indicators()): columns place_id, layer (all | eov),
key (” or the EOV), decade (NULL = all years, else the decades above),
n (fractional), sp, shannon, simpson, es, cells, area. These
are true per-place indices of the pooled, area-weighted counts, not means of
hexagon values. The files are listed in files.parquet (layer place) and
release.json (layers$place, with each collection's membership version);
stats.parquet does not cover them.
stats.parquet has one row per (layer, key, decade, res,
variable) with min, p02, p50, p98, max and n_cells (cells with
a non-NULL value; a variable NULL everywhere in a partition gets a row with
NULL ranges and n_cells 0), for colour ramps. key is the EOV or taxon
name (” for the all-taxa layers), rank the taxon rank (” otherwise),
decade NA outside the decade layers.
Examples
if (FALSE) { # \dontrun{
con <- obis_store_connect("~/data/obis/obis_h3.duckdb")
obis_h3_export_parquet(con, "~/data/obis-h3", "v20260728")
} # }