Release CalCOFI Database

Published

2026-04-02

Overview

Goal: Create a frozen (immutable) release of the CalCOFI integrated database by assembling all ingest parquet outputs. This is the “caboose” notebook that always runs last, after all ingest notebooks complete.

Upstream notebooks are auto-discovered from calcofi: YAML frontmatter in each .qmd. All workflows with workflow_type: ingest or spatial feed into this release notebook via dependency: [auto] in _targets.R.

Code
flowchart LR
    subgraph ingest["Ingest Notebooks"]
        i1["ichthyo"]
        i2["bottle"]
        i3["ctd-cast"]
        i4["dic"]
        i5["inverts"]
        i6["spatial"]
    end
    subgraph assembly["Assembly (this notebook)"]
        a["Load all parquet<br/>into in-memory DuckDB"]
    end
    subgraph release["Frozen Release"]
        f["Clean parquet<br/>+ manifest.json"]
    end
    i1 --> a
    i2 --> a
    i3 --> a
    i4 --> a
    i5 --> a
    i6 --> a
    a --> f

    style ingest fill:#e3f2fd,stroke:#1565c0
    style assembly fill:#fff3e0,stroke:#ef6c00
    style release fill:#e8f4e8,stroke:#2e7d32

Figure 1: Pipeline: ingest notebooks produce parquet → release caboose assembles frozen release

0.1 Setup

Code
devtools::load_all(here::here("../calcofi4db"))
ℹ Loading calcofi4db
Code
devtools::load_all(here::here("../calcofi4r"))
ℹ Loading calcofi4r
Registered S3 method overwritten by 'quantmod':
  method            from
  as.zoo.data.frame zoo 
Code
# cleanup_gcs_obsolete(dry_run = F)
librarian::shelf(
  CalCOFI / calcofi4db,
  CalCOFI / calcofi4r,
  DBI,
  dplyr,
  DT,
  fs,
  glue,
  here,
  jsonlite,
  purrr,
  tibble,
  quiet = T
)
options(DT.options = list(scrollX = TRUE))

# release version
release_version <- format(Sys.Date(), "v%Y.%m.%d")
message(glue("Release version: {release_version}"))
Release version: v2026.04.02

0.2 Assemble from Ingest Outputs

Load all per-dataset parquet outputs into an in-memory DuckDB. Each ingest notebook writes parquet to data/parquet/{dataset}/ and uploads to gs://calcofi-db/ingest/{dataset}/.

Code
con_wdl <- get_duckdb_con(":memory:")
load_duckdb_extension(con_wdl, "spatial")
Loaded extension: spatial
Code
# discover upstream parquet dirs from .qmd frontmatter
wf <- parse_qmd_frontmatter(here())
parquet_dirs <- wf |>
  filter(workflow_type %in% c("ingest", "spatial")) |>
  pull(output) |>
  dirname() |>
  purrr::map_chr(~ here(.x))
message(glue("Loading {length(parquet_dirs)} ingest parquet dirs"))
Loading 6 ingest parquet dirs
Code
load_stats <- purrr::map_dfr(parquet_dirs, function(pd) {
  load_prior_tables(
    con = con_wdl,
    parquet_dir = pd
  )
})
Loaded bottle_measurement: 11135600 rows
Loaded bottle: 895371 rows
Loaded cast_condition: 235513 rows
Loaded casts: 35644 rows
Loaded dataset: 5 rows
Loaded measurement_type: 104 rows
Excluding 1 supplemental table(s): ctd_wide
Loaded ctd_cast: 6065096 rows
Loaded measurement_type: 104 rows
Loaded ctd_measurement: 236782294 rows (partitioned)
Loaded ctd_summary: 104828768 rows (partitioned)
Loaded dataset: 5 rows
Loaded dic_measurement_summary: 15786 rows
Loaded dic_measurement: 16391 rows
Loaded dic_sample: 4391 rows
Loaded measurement_type: 104 rows
Loaded boem_wind_planning: 9833 rows
Loaded ca_assembly_districts: 80 rows
Loaded ca_cdfw_regions: 7 rows
Loaded ca_county_boundaries: 58 rows
Loaded ca_cowcod_conservation: 2 rows
Loaded ca_marine_protected_areas: 155 rows
Loaded ca_maritime_boundaries: 2 rows
Loaded ca_ports: 194 rows
Loaded ca_senate_districts: 40 rows
Loaded ca_swqpa: 36 rows
Loaded ca_watershed_boundaries: 140 rows
Loaded meow_ecoregions: 232 rows
Loaded noaa_aquaculture_aoas: 10 rows
Loaded noaa_iea_regions: 1 rows
Loaded noaa_maritime_boundaries: 260 rows
Loaded noaa_ocean_disposal: 2148 rows
Loaded noaa_onms_sanctuaries: 16 rows
Loaded cruise: 691 rows
Loaded dataset: 5 rows
Loaded grid: 218 rows
Loaded ichthyo: 830873 rows
Loaded lookup: 26 rows
Loaded net: 76512 rows
Loaded segment: 60413 rows
Loaded ship: 48 rows
Loaded site: 61104 rows
Loaded species: 1144 rows
Loaded taxa_rank: 41 rows
Loaded taxon: 3348 rows
Loaded tow: 75506 rows
Loaded cruise: 691 rows
Loaded dataset: 5 rows
Loaded grid: 218 rows
Loaded invert_count: 9628 rows
Loaded invert_size: 4574 rows
Loaded net: 76512 rows
Loaded ship: 48 rows
Loaded site: 61104 rows
Loaded tow: 75506 rows
Code
load_stats |>
  datatable(caption = "Assembled tables from ingest parquet outputs")

0.3 Scan Manifests for Mismatches

Code
# scan all ingest manifests for unresolved mismatches
all_manifests <- list.files(
  "data/parquet", "manifest.json",
  recursive = TRUE, full.names = TRUE)

all_mismatches <- purrr::compact(lapply(all_manifests, function(mf) {
  m <- jsonlite::read_json(mf)
  if (is.null(m$mismatches)) return(NULL)
  dataset <- basename(dirname(mf))
  purrr::imap_dfr(m$mismatches, function(items, category) {
    if (length(items) == 0) return(NULL)
    purrr::map_dfr(items, function(x) {
      # replace NULL values with NA so as_tibble works
      x[vapply(x, is.null, logical(1))] <- NA
      as_tibble(x)
    }) |>
      mutate(dataset = dataset, category = category, .before = 1)
  })
}))

if (length(all_mismatches) > 0) {
  d_mismatches <- bind_rows(all_mismatches)
  message(glue("{nrow(d_mismatches)} unresolved mismatch(es) across manifests"))
  d_mismatches |>
    datatable(caption = "Unresolved mismatches (from manifest.json)")
} else {
  message("No unresolved mismatches found across manifests")
}
3 unresolved mismatch(es) across manifests

0.4 Validate

Cross-dataset validation to ensure data integrity before freezing.

Code
# grid_key integrity: casts.grid_key should all be in grid.grid_key
tbls <- DBI::dbListTables(con_wdl)

if (all(c("casts", "grid") %in% tbls)) {
  # use information_schema to check columns (avoids GEOMETRY type issues)
  casts_cols_wdl <- dbGetQuery(
    con_wdl,
    "SELECT column_name FROM information_schema.columns
     WHERE table_name = 'casts'"
  )$column_name
  grid_cols_wdl <- dbGetQuery(
    con_wdl,
    "SELECT column_name FROM information_schema.columns
     WHERE table_name = 'grid'"
  )$column_name
  if ("grid_key" %in% casts_cols_wdl && "grid_key" %in% grid_cols_wdl) {
    grid_orphans <- dbGetQuery(
      con_wdl,
      "SELECT COUNT(*) AS n FROM casts c
       WHERE c.grid_key IS NOT NULL
         AND c.grid_key NOT IN (SELECT grid_key FROM grid)"
    )$n
    message(glue("Grid key orphans in casts: {grid_orphans}"))
    # Grid key orphans in casts: 0
  }
}
Grid key orphans in casts: 0
Code
# ship PK uniqueness
if ("ship" %in% tbls) {
  ship_dups <- dbGetQuery(
    con_wdl,
    "SELECT ship_key, COUNT(*) AS n FROM ship
     GROUP BY ship_key HAVING COUNT(*) > 1"
  )
  if (nrow(ship_dups) > 0) {
    warning(glue("Duplicate ship_key values: {nrow(ship_dups)}"))
  } else {
    message("ship_key: all unique")
  }
}
ship_key: all unique
Code
# ship_key: all unique

# cruise PK uniqueness
if ("cruise" %in% tbls) {
  cruise_dups <- dbGetQuery(
    con_wdl,
    "SELECT cruise_key, COUNT(*) AS n FROM cruise
     GROUP BY cruise_key HAVING COUNT(*) > 1"
  )
  if (nrow(cruise_dups) > 0) {
    warning(glue("Duplicate cruise_key values: {nrow(cruise_dups)}"))
  } else {
    message("cruise_key: all unique")
  }
}
cruise_key: all unique
Code
# cruise_key: all unique

# cruise bridge coverage
if ("casts" %in% tbls) {
  bridge_stats <- dbGetQuery(
    con_wdl,
    "SELECT
      COUNT(*) AS total_casts,
      SUM(CASE WHEN ship_key IS NOT NULL THEN 1 ELSE 0 END) AS with_ship_key,
      SUM(CASE WHEN cruise_key IS NOT NULL THEN 1 ELSE 0 END) AS with_cruise_key
    FROM casts"
  )
  bridge_stats |> datatable(caption = "Cruise bridge coverage")
}
Code
# cruise_key format validation (YYYY-MM-NODC)
if ("cruise" %in% tbls) {
  bad_ck <- dbGetQuery(
    con_wdl,
    "SELECT cruise_key FROM cruise
     WHERE cruise_key IS NOT NULL
       AND NOT regexp_matches(cruise_key, '^\\d{4}-\\d{2}-.+$')"
  )
  if (nrow(bad_ck) > 0) {
    warning(glue("cruise_key format violations: {nrow(bad_ck)} rows"))
  } else {
    message("cruise_key: all match YYYY-MM-NODC format")
  }
}
Warning: cruise_key format violations: 1 rows
Code
# Warning message: cruise_key format violations: 1 rows
# cruise_key: 2019-07-

# site_key format validation (NNN.N NNN.N)
for (tbl_name in intersect(c("site", "casts", "ctd_cast"), tbls)) {
  tbl_cols <- dbGetQuery(
    con_wdl,
    glue(
      "SELECT column_name FROM information_schema.columns
     WHERE table_name = '{tbl_name}'"
    )
  )$column_name
  if ("site_key" %in% tbl_cols) {
    bad_sk <- dbGetQuery(
      con_wdl,
      glue(
        "SELECT COUNT(*) AS n FROM {tbl_name}
         WHERE site_key IS NOT NULL
           AND NOT regexp_matches(site_key, '^\\d{{3}}\\.\\d \\d{{3}}\\.\\d$')"
      )
    )$n
    if (bad_sk > 0) {
      warning(glue("site_key format violations in {tbl_name}: {bad_sk} rows"))
    } else {
      message(glue("site_key in {tbl_name}: all match NNN.N NNN.N format"))
    }
  }
}
Warning: site_key format violations in site: 982 rows
site_key in casts: all match NNN.N NNN.N format
Code
# site_key in casts: all match NNN.N NNN.N format
# Warning message: site_key format violations in site: 982 rows

# cruise_summary table for Shiny app
if (all(c("cruise", "ship") %in% tbls)) {
  # detect site_key vs sta_key for backward compat with old parquets
  ctd_site_col <- ifelse(
    "site_key" %in%
      dbGetQuery(
        con_wdl,
        "SELECT column_name FROM information_schema.columns
       WHERE table_name = 'ctd_cast'"
      )$column_name,
    "site_key",
    "sta_key"
  )

  dbExecute(
    con_wdl,
    glue(
      "
    CREATE OR REPLACE TABLE cruise_summary AS
    SELECT
      cr.cruise_key,
      EXTRACT(YEAR FROM cr.date_ym)::INTEGER  AS year,
      EXTRACT(MONTH FROM cr.date_ym)::INTEGER AS month,
      s.ship_name,
      s.ship_nodc,
      COALESCE((SELECT COUNT(DISTINCT site_key) FROM site
                WHERE cruise_key = cr.cruise_key), 0) AS ichthyo,
      COALESCE((SELECT COUNT(DISTINCT site_key) FROM casts
                WHERE cruise_key = cr.cruise_key), 0) AS bottle,
      COALESCE((SELECT COUNT(DISTINCT {ctd_site_col}) FROM ctd_cast
                WHERE cruise_key = cr.cruise_key), 0) AS ctd_cast,
      COALESCE((SELECT COUNT(DISTINCT ds.site_key)
                FROM dic_sample ds
                JOIN casts c ON ds.cast_id = c.cast_id
                WHERE c.cruise_key = cr.cruise_key), 0) AS dic
    FROM cruise cr
    JOIN ship s ON cr.ship_key = s.ship_key
    ORDER BY year DESC, month DESC"
    )
  )
  n_cs <- dbGetQuery(con_wdl, "SELECT COUNT(*) AS n FROM cruise_summary")$n
  message(glue("Created cruise_summary table: {n_cs} rows"))
}
Created cruise_summary table: 691 rows
Code
# Created cruise_summary table: 691 rows
tbl(con_wdl, "cruise_summary") |>
  collect() |>
  datatable(caption = "cruise_summary preview")
Code
# run standard release validation (wrapped in tryCatch for GEOMETRY compat)
tryCatch(
  {
    validation <- validate_for_release(con_wdl)
    if (validation$passed) {
      message("Release validation passed!")
    } else {
      cat("Validation FAILED:\n")
      cat(paste("-", validation$errors, collapse = "\n"))
    }
  },
  error = function(e) {
    message(glue("validate_for_release skipped: {e$message}"))
  }
)
Validation FAILED:
- Table 'ca_ports' has 4 NULL values in required column 'gnis_id'
- Table 'casts' has 298 NULL values in required column 'grid_key'
- Table 'casts' has 49 NULL values in required column 'ship_key'
- Table 'casts' has 49 NULL values in required column 'cruise_key'
- Table 'ctd_measurement' has 3773585 NULL values in required column 'ctd_cast_uuid'
- Table 'ctd_measurement' has 3773585 NULL values in required column 'cruise_key'
- Table 'dic_measurement' has 12683 NULL values in required column 'cast_id'
- Table 'dic_measurement' has 12721 NULL values in required column 'bottle_id'
- Table 'dic_measurement_summary' has 12592 NULL values in required column 'cast_id'
- Table 'dic_measurement_summary' has 12611 NULL values in required column 'bottle_id'
- Table 'dic_sample' has 3305 NULL values in required column 'cast_id'
- Table 'dic_sample' has 3326 NULL values in required column 'bottle_id'
- Table 'site' has 2084 NULL values in required column 'grid_key'
- Table 'species' has 170 NULL values in required column 'gbif_id'

0.5 Show Combined Schema

Code
# dir_frozen used later; define early so ERD can reference it
dir_frozen <- here(glue("data/releases/{release_version}"))
dir.create(dir_frozen, recursive = TRUE, showWarnings = FALSE)

erd <- cc_erd(con_wdl, layout = "elk")
plot(erd)

erd <- cc_erd(
  con_wdl,
  colors = list(
    lightblue = c("cruise", "ship", "site", "tow", "net"),
    lightyellow = c("ichthyo", "species", "lookup", "taxon", "taxa_rank"),
    lightgreen = c("grid", "segment"),
    pink = c(
      "casts",
      "bottle",
      "bottle_measurement",
      "cast_condition",
      "measurement_type"
    ),
    lavender = c("ctd_cast", "ctd_measurement", "ctd_summary"),
    lightsalmon = c("dic_sample", "dic_measurement", "dic_measurement_summary"),
    white = c("dataset")
  )
)
plot(erd)
Code
# exclude internal tables and views
schema_tbls <- setdiff(
  tbls,
  c("_meta", "_sp_update", "casts_derived", "ctd_cast_derived"))

# merge per-dataset relationships.json files
rels_paths <- c(
  here("data/parquet/swfsc_ichthyo/relationships.json"),
  here("data/parquet/calcofi_bottle/relationships.json"),
  here("data/parquet/calcofi_ctd-cast/relationships.json"),
  here("data/parquet/calcofi_dic/relationships.json")
)
rels_paths <- rels_paths[file.exists(rels_paths)]

dir_frozen <- here(glue("data/releases/{release_version}"))
dir.create(dir_frozen, recursive = TRUE, showWarnings = FALSE)
rels_merged_path <- file.path(dir_frozen, "relationships.json")

if (length(rels_paths) > 0) {
  merge_relationships_json(rels_paths, rels_merged_path)

  # add cross-dataset FKs (bottle casts → ichthyo cruise/ship/grid)
  rels_merged <- jsonlite::fromJSON(
    rels_merged_path, simplifyVector = FALSE)
  cross_fks <- list(
    list(table = "casts", column = "cruise_key",
         ref_table = "cruise", ref_column = "cruise_key"),
    list(table = "casts", column = "ship_key",
         ref_table = "ship", ref_column = "ship_key"),
    list(table = "casts", column = "grid_key",
         ref_table = "grid", ref_column = "grid_key"))
  rels_merged$foreign_keys <- c(
    rels_merged$foreign_keys, cross_fks)
  jsonlite::write_json(
    rels_merged, rels_merged_path,
    auto_unbox = TRUE, pretty = TRUE, null = "null")
}
Merged relationships.json: 15 PKs, 13 FKs from 4 files
Code
# render color-coded ERD (cc_erd handles GEOMETRY columns natively)
cc_erd(
  con_wdl,
  tables   = schema_tbls,
  rels_path = rels_merged_path,
  colors = list(
    lightblue   = c("cruise", "ship", "site", "tow", "net"),
    lightyellow = c("ichthyo", "species", "lookup",
                    "taxon", "taxa_rank"),
    lightgreen  = c("grid", "segment"),
    pink        = c("casts", "bottle", "bottle_measurement",
                    "cast_condition", "measurement_type"),
    lavender    = c("ctd_cast", "ctd_measurement", "ctd_summary"),
    lightsalmon = c("dic_sample", "dic_measurement",
                    "dic_measurement_summary"),
    white       = "dataset"))
---
config:
  layout: elk
---
erDiagram
    boem_wind_planning {
        int id
        varchar protraction_number
        varchar block_number
        varchar block_label
        varchar sub_block
        varchar additional_information
        varchar primary_wpa_category
        varchar secondary_wpa_category
        varchar url1
        varchar url2
        double shape_length
        double shape_area
        geometry geom
    }
    bottle {
        int cast_id FK
        int bottle_id PK
        varchar site_key
        varchar depth_key
        double depth_m
        smallint btl_num
        varchar rec_ind
        double depth_qual
        varchar inc_tim
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
    }
    bottle_measurement {
        bigint bottle_measurement_id PK
        double bottle_id FK
        varchar measurement_type FK
        double measurement_value
        double measurement_prec
        varchar measurement_qual
    }
    ca_assembly_districts {
        int id
        double area
        varchar district
        double members
        varchar locked
        varchar name
        double population
        double cvap_19
        double hsp_cvap_1
        double doj_nh_blk
        double doj_nh_asn
        double nh_wht_cva
        double ideal_valu
        double deviation
        double f_deviatio
        double f_cvap_19
        double f_hsp_cvap
        double f_doj_nh_b
        double f_doj_nh_a
        double f_nh_wht_c
        double district_n
        varchar district_l
        geometry geom
    }
    ca_cdfw_regions {
        int id
        int objectid
        int region
        varchar name
        varchar address
        varchar city
        varchar zip_code
        varchar phone
        varchar url
        double acres
        double shape_area
        double shape_length
        varchar global_id
        geometry geom
    }
    ca_county_boundaries {
        int id
        varchar statefp
        varchar countyfp
        varchar countyns
        varchar geoid
        varchar name
        varchar namelsad
        varchar lsad
        varchar classfp
        varchar mtfcc
        varchar csafp
        varchar cbsafp
        varchar metdivfp
        varchar funcstat
        double aland
        double awater
        varchar intptlat
        varchar intptlon
        double shape_leng
        double shape_area
        geometry geom
    }
    ca_cowcod_conservation {
        int id
        double objectid_2
        double objectid
        double id_1
        double mlpa_mlpal
        double perimeter
        double hectares
        double shape_leng
        double shape_le_1
        double acres
        double et_id
        double shape_le_2
        double shape_area
        double shape_len
        geometry geom
    }
    ca_marine_protected_areas {
        int id
        double objectid
        varchar name
        varchar ccr
        double area_sq_mi
        varchar study_regi
        varchar fullname
        varchar type
        varchar dfg_url
        int ccr_int
        varchar shortname
        double acres
        double hectares
        geometry geom
    }
    ca_maritime_boundaries {
        int id
        varchar sourcethm
        double count
        geometry geom
    }
    ca_ports {
        int id
        double area
        double perimeter
        double calgnis
        double calgnis_id
        varchar feat_name
        int portcode
        int biogeogrp
        int commercial
        varchar p_county
        varchar gnis_id
        varchar feat_type
        int elevat_ft
        varchar variant
        int port_cat1
        int port_cat2
        geometry geom
    }
    ca_senate_districts {
        int id
        double area
        varchar district
        double members
        varchar locked
        varchar name
        double population
        double cvap_19
        double hsp_cvap_1
        double doj_nh_ind
        double doj_nh_blk
        double doj_nh_asn
        double nh_wht_cva
        double ideal_valu
        double deviation
        double f_deviatio
        double f_cvap_19
        double f_hsp_cvap
        double f_doj_nh_i
        double f_doj_nh_b
        double f_doj_nh_a
        double f_nh_wht_c
        varchar district_l
        varchar multiple_f
        geometry geom
    }
    ca_swqpa {
        int id
        double swqpa_num
        varchar swqpaname
        geometry geom
    }
    ca_watershed_boundaries {
        int id
        double objectid
        varchar tnmid
        varchar metasourceid
        varchar sourcedatadesc
        varchar sourceoriginator
        varchar sourcefeatureid
        timestamptz loaddate
        varchar referencegnis_ids
        double areaacres
        double areasqkm
        varchar states
        varchar huc8
        varchar name
        double shape_area
        double shape_length
        varchar global_id
        geometry geom
    }
    cast_condition {
        bigint cast_condition_id PK
        double cast_id FK
        varchar condition_type FK
        double condition_value
    }
    casts {
        int cast_id PK
        varchar cruise_key_0
        varchar cast_key
        varchar site_key
        varchar sta_code
        double distance
        double lat_dec
        double lon_dec
        double rpt_line
        double st_line
        double ac_line
        double rpt_sta
        double st_station
        double ac_sta
        double bottom_depth_m
        varchar ship_name
        varchar ship_code
        varchar data_type
        smallint order_occ
        smallint event_num
        varchar cruz_leg
        varchar orig_sta_id
        varchar data_or
        double int_chl
        double int_c14
        varchar inc_str
        varchar inc_end
        varchar pst_lan
        varchar civil_t
        timestamp datetime_utc
        geometry geom
        varchar grid_key FK
        varchar ship_key FK
        varchar cruise_key FK
    }
    cruise {
        uuid cruise_uuid
        date date_ym
        varchar ship_key FK
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
        varchar cruise_key PK
    }
    ctd_cast {
        varchar ctd_cast_uuid
        varchar cruise_key
        varchar data_stage
        varchar cast_dir
        varchar ord_occ
        double event_num
        varchar cast_key
        timestamp datetime_utc
        double lat_dec
        double lon_dec
        varchar sta_key
        varchar line
        varchar sta
        double ox_aveu_m_sta_corr
        varchar ship_key
        geometry geom
        varchar grid_key
    }
    ctd_measurement {
        varchar ctd_measurement_uuid
        varchar ctd_cast_uuid
        double depth_m
        varchar measurement_type
        double measurement_value
        varchar measurement_qual
        varchar cruise_key
    }
    ctd_summary {
        varchar ctd_summary_uuid PK
        varchar site_key
        double depth_m
        varchar measurement_type
        double avg
        double stddev
        bigint n_obs
        varchar cruise_key
    }
    dataset {
        varchar provider
        varchar dataset
        varchar dataset_name
        varchar description
        varchar citation_main
        bool citation_others
        varchar link_calcofi_org
        varchar link_data_source
        varchar link_others
        varchar tables
        varchar coverage_temporal
        varchar coverage_spatial
        varchar license
        varchar pi_names
    }
    dic_measurement {
        bigint dic_measurement_id PK
        varchar expocode
        varchar site_key
        timestamp datetime_utc
        double depth_m
        double latitude
        double longitude
        int cast_id
        int bottle_id
        varchar measurement_type FK
        double measurement_value
        varchar measurement_qual
    }
    dic_measurement_summary {
        varchar site_key
        timestamp datetime_utc
        double depth_m
        double latitude
        double longitude
        varchar measurement_type FK
        double avg
        double stddev
        bigint n_obs
        int cast_id
        int bottle_id
    }
    dic_sample {
        varchar expocode
        varchar ship_name
        timestamp datetime_utc
        varchar site_key
        double latitude
        double longitude
        double depth_m
        int cast_id
        int bottle_id
        geometry geom
    }
    grid {
        varchar grid_key
        double station
        double line
        varchar shore
        varchar pattern
        int spacing
        varchar zone
        double area_km2
        geometry geom
        geometry geom_ctr
    }
    ichthyo {
        uuid ichthyo_uuid PK
        uuid net_uuid FK
        smallint species_id FK
        varchar life_stage
        varchar measurement_type
        double measurement_value
        int tally
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
    }
    invert_count {
        varchar cruise
        varchar ship_name
        varchar ship_code
        int order_occupied
        varchar tow_type
        int tow_number
        varchar net_location
        timestamp time_start
        double latitude
        double longitude
        double line
        double station
        double standard_haul_factor
        double volume_sampled
        double proportion_sorted
        varchar scientific_name
        varchar common_name
        int itis_tsn
        int calcofi_species_code
        int invert_count
        double inverts_10m2
        double inverts_100m3
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
    }
    invert_size {
        varchar cruise
        varchar ship_name
        varchar ship_code
        int order_occupied
        varchar tow_type
        int tow_number
        varchar net_location
        timestamp time_start
        double latitude
        double longitude
        double line
        double station
        double standard_haul_factor
        double volume_sampled
        double proportion_sorted
        varchar scientific_name
        varchar common_name
        int itis_tsn
        int calcofi_species_code
        double invert_size
        int invert_count
        double inverts_10m2
        double inverts_100m3
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
    }
    lookup {
        int lookup_id PK
        varchar lookup_type
        int lookup_num
        varchar lookup_chr
        varchar description
        varchar _source_file
        int _source_row
        varchar _ingested_at
        varchar _source_uuid
    }
    measurement_type {
        varchar measurement_type PK
        varchar description
        varchar units
        varchar _source_column
        varchar _source_table
        varchar _source_datasets
        varchar _qual_column
        varchar _prec_column
    }
    meow_ecoregions {
        int id
        double eco_code
        varchar ecoregion
        double prov_code
        varchar province
        double rlm_code
        varchar realm
        double alt_code
        double eco_code_x
        varchar lat_zone
        geometry geom
    }
    net {
        uuid net_uuid PK
        uuid tow_uuid FK
        varchar side
        double std_haul_factor
        double vol_sampled_m3
        double prop_sorted
        double smallplankton
        double totalplankton
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
    }
    noaa_aquaculture_aoas {
        int id
        double shape_leng
        double shape_area
        varchar study_area
        varchar option
        double acres
        geometry geom
    }
    noaa_iea_regions {
        bigint OGC_FID
        varchar id
        int objectid
        varchar lme_name
        varchar grouping
        varchar arctic
        varchar uslmes
        double shape_leng
        double shape_area
        double sum_gis_km
        int mrgid
        double minlat
        double minlong
        double maxlat
        double maxlong
        double lat
        double lon
        int lme_number
        varchar id_1
        int mrgid_1
        varchar geoname
        int mrgid_ter1
        varchar pol_type
        int mrgid_sov1
        varchar territory1
        varchar iso_ter1
        varchar sovereign1
        varchar mrgid_ter2
        varchar mrgid_sov2
        varchar territory2
        varchar iso_ter2
        varchar sovereign2
        varchar mrgid_ter3
        varchar mrgid_sov3
        varchar territory3
        varchar iso_ter3
        varchar sovereign3
        double x_1
        double y_1
        int mrgid_eez
        int area_km2
        varchar iso_sov1
        varchar iso_sov2
        varchar iso_sov3
        int un_sov1
        varchar un_sov2
        varchar un_sov3
        int un_ter1
        varchar un_ter2
        varchar un_ter3
        geometry geom
    }
    noaa_maritime_boundaries {
        int id
        double objectid
        varchar bound_id
        varchar region
        varchar feat_type
        date pub_date
        date apprv_date
        varchar legal_auth
        varchar aor
        varchar note
        varchar supp_info
        double ts
        double cz
        double eez
        double f_eez
        double symbol
        double unilateral
        geometry geom
    }
    noaa_ocean_disposal {
        int id
        varchar designation
        varchar source_id
        varchar description
        varchar measurement
        varchar primary_use
        varchar period_use
        varchar restrictions
        varchar status
        varchar state
        varchar site_label
        varchar depth
        geometry geom
    }
    noaa_onms_sanctuaries {
        int id
        varchar sanctuary
        varchar nms
        geometry geom
    }
    segment {
        varchar cruise_key
        uuid site_uuid_beg
        uuid site_uuid_end
        double lon_beg
        double lat_beg
        double lon_end
        double lat_end
        timestamp time_beg
        timestamp time_end
        double time_hr
        double length_km
        double km_per_hr
        geometry geom
        int segment_id
    }
    ship {
        varchar ship_key PK
        varchar ship_name
        varchar ship_nodc
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
    }
    site {
        uuid site_uuid PK
        uuid cruise_uuid
        smallint orderocc
        double longitude
        double latitude
        double line
        double station
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
        varchar cruise_key FK
        geometry geom
        varchar grid_key
        varchar site_key
    }
    species {
        smallint species_id PK
        varchar scientific_name
        int itis_id
        int worms_id
        varchar common_name
        int gbif_id
    }
    taxa_rank {
        varchar taxonRank
        int rank_order
    }
    taxon {
        varchar authority
        int taxonID
        int acceptedNameUsageID
        int parentNameUsageID
        varchar scientificName
        varchar taxonRank
        varchar taxonomicStatus
        varchar scientificNameAuthorship
    }
    tow {
        uuid tow_uuid PK
        uuid site_uuid FK
        varchar tow_type_key
        smallint tow_number
        timestamp time_start
        varchar _source_file
        int _source_row
        timestamp _ingested_at
        varchar _source_uuid
    }
    net ||--o{ ichthyo : "net_uuid"
    species ||--o{ ichthyo : "species_id"
    tow ||--o{ net : "tow_uuid"
    site ||--o{ tow : "site_uuid"
    cruise ||--o{ site : "cruise_key"
    ship ||--o{ cruise : "ship_key"
    casts ||--o{ bottle : "cast_id"
    bottle ||--o{ bottle_measurement : "bottle_id"
    measurement_type ||--o{ bottle_measurement : "measurement_type"
    casts ||--o{ cast_condition : "cast_id"
    measurement_type ||--o{ cast_condition : "condition_type"
    measurement_type ||--o{ dic_measurement : "measurement_type"
    measurement_type ||--o{ dic_measurement_summary : "measurement_type"
    cruise ||--o{ casts : "cruise_key"
    ship ||--o{ casts : "ship_key"
    grid ||--o{ casts : "grid_key"
    classDef lightblue fill:#ADD8E6,stroke:#68828A
    class cruise,ship,site,tow,net lightblue
    classDef lightyellow fill:#FFFFE0,stroke:#999986
    class ichthyo,species,lookup,taxon,taxa_rank lightyellow
    classDef lightgreen fill:#90EE90,stroke:#568F56
    class grid,segment lightgreen
    classDef pink fill:#FFC0CB,stroke:#99737A
    class casts,bottle,bottle_measurement,cast_condition,measurement_type pink
    classDef lavender fill:#E6E6FA,stroke:#8A8A96
    class ctd_cast,ctd_measurement,ctd_summary lavender
    classDef lightsalmon fill:#FFA07A,stroke:#996049
    class dic_sample,dic_measurement,dic_measurement_summary lightsalmon
    classDef white fill:#FFFFFF,stroke:#999999
    class dataset white

0.6 Create Frozen Release

Strip provenance columns and export clean parquet files for public access. See Frozen DuckLake pattern.

Code
# freeze + upload enabled so GCS release includes all tables (incl. geometry)
# knitr::opts_chunk$set(eval = FALSE)
Code
dir_frozen <- here(glue("data/releases/{release_version}"))
message(glue("Creating frozen release: {release_version}"))
Creating frozen release: v2026.04.02
Code
# get tables to freeze (exclude internal and view tables)
tables_to_freeze <- schema_tbls

# create frozen parquet (strips provenance)
# partition large tables by cruise_key for efficient reads
freeze_stats <- write_parquet_outputs(
  con = con_wdl,
  output_dir = file.path(dir_frozen, "parquet"),
  tables = tables_to_freeze,
  partition_by = list(
    ctd_measurement = "cruise_key",
    ctd_summary = "cruise_key"
  ),
  strip_provenance = TRUE,
  compression = "zstd"
)
Exported boem_wind_planning: 9833 rows, 1.36 MB
Exported bottle: 895371 rows, 5.7 MB
Exported bottle_measurement: 11135600 rows, 44.79 MB
Exported ca_assembly_districts: 80 rows, 3.54 MB
Exported ca_cdfw_regions: 7 rows, 4.34 MB
Exported ca_county_boundaries: 58 rows, 3.09 MB
Exported ca_cowcod_conservation: 2 rows, 0.01 MB
Exported ca_marine_protected_areas: 155 rows, 1.1 MB
Exported ca_maritime_boundaries: 2 rows, 0.83 MB
Exported ca_ports: 194 rows, 0.01 MB
Exported ca_senate_districts: 40 rows, 3.2 MB
Exported ca_swqpa: 36 rows, 0.75 MB
Exported ca_watershed_boundaries: 140 rows, 19.34 MB
Exported cast_condition: 235513 rows, 0.71 MB
Exported casts: 35644 rows, 1.37 MB
Exported cruise: 691 rows, 0 MB
Exported ctd_cast: 6065096 rows, 168.89 MB
Exported ctd_measurement: 236782294 rows, 7.94 GB (partitioned)
Exported ctd_summary: 104828768 rows, 3.01 GB (partitioned)
Exported dataset: 5 rows, 0.01 MB
Exported dic_measurement: 16391 rows, 0.1 MB
Exported dic_measurement_summary: 15786 rows, 0.09 MB
Exported dic_sample: 4391 rows, 0.03 MB
Exported grid: 218 rows, 0.1 MB
Exported ichthyo: 830873 rows, 14.62 MB
Exported invert_count: 9628 rows, 0.21 MB
Exported invert_size: 4574 rows, 0.06 MB
Exported lookup: 26 rows, 0 MB
Exported measurement_type: 104 rows, 0 MB
Exported meow_ecoregions: 232 rows, 0.35 MB
Exported net: 76512 rows, 0.78 MB
Exported noaa_aquaculture_aoas: 10 rows, 0 MB
Exported noaa_iea_regions: 1 rows, 0.14 MB
Exported noaa_maritime_boundaries: 260 rows, 2.62 MB
Exported noaa_ocean_disposal: 2148 rows, 2.34 MB
Exported noaa_onms_sanctuaries: 16 rows, 2.83 MB
Exported segment: 60413 rows, 2.63 MB
Exported ship: 48 rows, 0 MB
Exported site: 61104 rows, 0.85 MB
Exported species: 1144 rows, 0.03 MB
Exported taxa_rank: 41 rows, 0 MB
Exported taxon: 3348 rows, 0.05 MB
Exported tow: 75506 rows, 0.45 MB
Wrote manifest to /Users/bbest/Github/CalCOFI/workflows/data/releases/v2026.04.02/parquet/manifest.json
Code
freeze_stats |>
  datatable(caption = glue("Frozen release {release_version} statistics"))

0.7 Release Notes

Code
# build release notes
tables_list <- paste0(
  "- ",
  freeze_stats$table,
  " (",
  format(freeze_stats$rows, big.mark = ","),
  " rows)"
)

release_notes <- paste0(
  "# CalCOFI Database Release ",
  release_version,
  "\n\n",
  "**Release Date**: ",
  Sys.Date(),
  "\n\n",
  "## Tables Included\n\n",
  paste(tables_list, collapse = "\n"),
  "\n\n",
  "## Total\n\n",
  "- **Tables**: ",
  nrow(freeze_stats),
  "\n",
  "- **Total Rows**: ",
  format(sum(freeze_stats$rows), big.mark = ","),
  "\n",
  "- **Total Size**: ",
  round(sum(freeze_stats$file_size) / 1024 / 1024, 1),
  " MB\n\n",
  "## Data Sources\n\n",
  "- `ingest_swfsc_ichthyo.qmd` - Ichthyo tables (cruise, ship, site, tow, net, species, ichthyo, grid, segment, lookup, taxon, taxa_rank)\n",
  "- `ingest_calcofi_bottle.qmd` - Bottle/cast tables (casts, bottle, bottle_measurement, cast_condition, measurement_type)\n",
  "- `ingest_calcofi_ctd-cast.qmd` - CTD tables (ctd_cast, ctd_measurement, ctd_summary, measurement_type)\n",
  "- `ingest_calcofi_dic.qmd` - DIC/alkalinity tables (dic_sample, dic_measurement, dic_measurement_summary, dataset)\n\n",
  "## Cross-Dataset Integration\n\n",
  "- **Ship matching**: Reconciled ship codes between bottle casts and swfsc ship reference\n",
  "- **Cruise bridge**: Derived cruise_key (YYYY-MM-NODC) for bottle casts via ship matching + datetime\n",
  "- **Taxonomy**: Standardized species with WoRMS AphiaID, ITIS TSN, GBIF backbone key\n",
  "- **Taxon hierarchy**: Built taxon + taxa_rank tables from WoRMS/ITIS classification\n\n",
  "## Access\n\n",
  "Parquet files can be queried directly from GCS:\n\n",
  "```r\n",
  "library(duckdb)\n",
  "con <- dbConnect(duckdb())\n",
  "dbExecute(con, 'INSTALL httpfs; LOAD httpfs;')\n",
  "dbGetQuery(con, \"\n",
  "  SELECT * FROM read_parquet(\n",
  "    'https://storage.googleapis.com/calcofi-db/ducklake/releases/",
  release_version,
  "/parquet/ichthyo.parquet')\n",
  "  LIMIT 10\")\n",
  "```\n\n",
  "Or use calcofi4r:\n\n",
  "```r\n",
  "library(calcofi4r)\n",
  "con <- cc_get_db(version = '",
  release_version,
  "')\n",
  "```\n"
)

writeLines(release_notes, file.path(dir_frozen, "RELEASE_NOTES.md"))
message(glue(
  "Release notes written to {file.path(dir_frozen, 'RELEASE_NOTES.md')}"
))
Release notes written to /Users/bbest/Github/CalCOFI/workflows/data/releases/v2026.04.02/RELEASE_NOTES.md
Code
cat(release_notes)

1 CalCOFI Database Release v2026.04.02

Release Date: 2026-04-02

1.1 Tables Included

  • boem_wind_planning ( 9,833 rows)
  • bottle ( 895,371 rows)
  • bottle_measurement ( 11,135,600 rows)
  • ca_assembly_districts ( 80 rows)
  • ca_cdfw_regions ( 7 rows)
  • ca_county_boundaries ( 58 rows)
  • ca_cowcod_conservation ( 2 rows)
  • ca_marine_protected_areas ( 155 rows)
  • ca_maritime_boundaries ( 2 rows)
  • ca_ports ( 194 rows)
  • ca_senate_districts ( 40 rows)
  • ca_swqpa ( 36 rows)
  • ca_watershed_boundaries ( 140 rows)
  • cast_condition ( 235,513 rows)
  • casts ( 35,644 rows)
  • cruise ( 691 rows)
  • ctd_cast ( 6,065,096 rows)
  • ctd_measurement (236,782,294 rows)
  • ctd_summary (104,828,768 rows)
  • dataset ( 5 rows)
  • dic_measurement ( 16,391 rows)
  • dic_measurement_summary ( 15,786 rows)
  • dic_sample ( 4,391 rows)
  • grid ( 218 rows)
  • ichthyo ( 830,873 rows)
  • invert_count ( 9,628 rows)
  • invert_size ( 4,574 rows)
  • lookup ( 26 rows)
  • measurement_type ( 104 rows)
  • meow_ecoregions ( 232 rows)
  • net ( 76,512 rows)
  • noaa_aquaculture_aoas ( 10 rows)
  • noaa_iea_regions ( 1 rows)
  • noaa_maritime_boundaries ( 260 rows)
  • noaa_ocean_disposal ( 2,148 rows)
  • noaa_onms_sanctuaries ( 16 rows)
  • segment ( 60,413 rows)
  • ship ( 48 rows)
  • site ( 61,104 rows)
  • species ( 1,144 rows)
  • taxa_rank ( 41 rows)
  • taxon ( 3,348 rows)
  • tow ( 75,506 rows)

1.2 Total

  • Tables: 43
  • Total Rows: 361,152,303
  • Total Size: 11498.3 MB

1.3 Data Sources

  • ingest_swfsc_ichthyo.qmd - Ichthyo tables (cruise, ship, site, tow, net, species, ichthyo, grid, segment, lookup, taxon, taxa_rank)
  • ingest_calcofi_bottle.qmd - Bottle/cast tables (casts, bottle, bottle_measurement, cast_condition, measurement_type)
  • ingest_calcofi_ctd-cast.qmd - CTD tables (ctd_cast, ctd_measurement, ctd_summary, measurement_type)
  • ingest_calcofi_dic.qmd - DIC/alkalinity tables (dic_sample, dic_measurement, dic_measurement_summary, dataset)

1.4 Cross-Dataset Integration

  • Ship matching: Reconciled ship codes between bottle casts and swfsc ship reference
  • Cruise bridge: Derived cruise_key (YYYY-MM-NODC) for bottle casts via ship matching + datetime
  • Taxonomy: Standardized species with WoRMS AphiaID, ITIS TSN, GBIF backbone key
  • Taxon hierarchy: Built taxon + taxa_rank tables from WoRMS/ITIS classification

1.5 Access

Parquet files can be queried directly from GCS:

library(duckdb)
con <- dbConnect(duckdb())
dbExecute(con, 'INSTALL httpfs; LOAD httpfs;')
dbGetQuery(con, "
  SELECT * FROM read_parquet(
    'https://storage.googleapis.com/calcofi-db/ducklake/releases/v2026.04.02/parquet/ichthyo.parquet')
  LIMIT 10")

Or use calcofi4r:

library(calcofi4r)
con <- cc_get_db(version = 'v2026.04.02')

1.6 Upload Frozen Release to GCS

Code
# upload frozen release to GCS
upload_frozen_release(
  release_dir = dir_frozen,
  version = release_version,
  set_latest = TRUE
)
Uploading frozen release v2026.04.02 to gs://calcofi-db/ducklake/releases/v2026.04.02...
Created catalog.json with 43 tables
Uploading 234 parquet files...
Uploaded RELEASE_NOTES.md
Uploaded relationships.json
Updated versions.json
Set latest.txt to v2026.04.02
Release v2026.04.02 uploaded successfully: 234 files, 11498.3 MB

1.7 Cleanup

Code
# close in-memory DuckDB connection
close_duckdb(con_wdl)
message("Assembly DuckDB connection closed")
Assembly DuckDB connection closed
Code
# summary
message(glue("\n=== Summary ==="))
=== Summary ===
Code
message(glue("Frozen release: {release_version} created at {dir_frozen}"))
Frozen release: v2026.04.02 created at /Users/bbest/Github/CalCOFI/workflows/data/releases/v2026.04.02
Code
message(glue("Tables: {nrow(freeze_stats)}"))
Tables: 43
Code
message(glue("Total rows: {format(sum(freeze_stats$rows), big.mark = ',')}"))
Total rows: 361,152,303
Code
devtools::session_info()
─ Session info ───────────────────────────────────────────────────────────────
 setting  value
 version  R version 4.5.2 (2025-10-31)
 os       macOS Sequoia 15.7.1
 system   aarch64, darwin20
 ui       X11
 language (EN)
 collate  en_US.UTF-8
 ctype    en_US.UTF-8
 tz       Europe/Rome
 date     2026-04-02
 pandoc   3.8.3 @ /opt/homebrew/bin/ (via rmarkdown)
 quarto   1.8.25 @ /usr/local/bin/quarto

─ Packages ───────────────────────────────────────────────────────────────────
 !  package            * version    date (UTC) lib source
    abind                1.4-8      2024-09-12 [1] CRAN (R 4.5.0)
    arrow                23.0.1.1   2026-02-24 [1] CRAN (R 4.5.2)
    assertthat           0.2.1      2019-03-21 [1] CRAN (R 4.5.0)
    backports            1.5.0      2024-05-23 [1] CRAN (R 4.5.0)
    base64enc            0.1-6      2026-02-02 [1] CRAN (R 4.5.2)
    bit                  4.6.0      2025-03-06 [1] CRAN (R 4.5.0)
    bit64                4.6.0-1    2025-01-16 [1] CRAN (R 4.5.0)
    blob                 1.3.0      2026-01-14 [1] CRAN (R 4.5.2)
    broom                1.0.12     2026-01-27 [1] CRAN (R 4.5.2)
    bslib                0.10.0     2026-01-26 [1] CRAN (R 4.5.2)
    cachem               1.1.0      2024-05-16 [1] CRAN (R 4.5.0)
 P  calcofi4db         * 2.5.1      2026-04-02 [?] load_all()
 VP calcofi4r          * 1.1.5      2026-03-26 [?] Github (calcofi/calcofi4r@a1d14ec) (on disk 1.1.4)
    class                7.3-23     2025-01-01 [1] CRAN (R 4.5.2)
    classInt             0.4-11     2025-01-08 [1] CRAN (R 4.5.0)
    cli                  3.6.5      2025-04-23 [1] CRAN (R 4.5.0)
    codetools            0.2-20     2024-03-31 [1] CRAN (R 4.5.2)
    crosstalk            1.2.2      2025-08-26 [1] CRAN (R 4.5.0)
    curl                 7.0.0      2025-08-19 [1] CRAN (R 4.5.0)
    data.table           1.18.2.1   2026-01-27 [1] CRAN (R 4.5.2)
    DBI                * 1.3.0      2026-02-25 [1] CRAN (R 4.5.2)
    dbplyr               2.5.2      2026-02-13 [1] CRAN (R 4.5.2)
    desc                 1.4.3      2023-12-10 [1] CRAN (R 4.5.0)
    devtools             2.5.0      2026-03-14 [1] CRAN (R 4.5.2)
    DiagrammeR           1.0.11     2024-02-02 [1] CRAN (R 4.5.0)
    DiagrammeRsvg        0.1        2016-02-04 [1] CRAN (R 4.5.0)
    digest               0.6.39     2025-11-19 [1] CRAN (R 4.5.2)
    dm                   1.1.1      2026-03-05 [1] CRAN (R 4.5.2)
    dplyr              * 1.2.0      2026-02-03 [1] CRAN (R 4.5.2)
    DT                 * 0.34.0     2025-09-02 [1] CRAN (R 4.5.0)
    duckdb               1.5.0      2026-03-14 [1] CRAN (R 4.5.2)
    dygraphs             1.1.1.6    2018-07-11 [1] CRAN (R 4.5.0)
    e1071                1.7-17     2025-12-18 [1] CRAN (R 4.5.2)
    ellipsis             0.3.2      2021-04-29 [1] CRAN (R 4.5.0)
    evaluate             1.0.5      2025-08-27 [1] CRAN (R 4.5.0)
    farver               2.1.2      2024-05-13 [1] CRAN (R 4.5.0)
    fastmap              1.2.0      2024-05-15 [1] CRAN (R 4.5.0)
    fs                 * 2.0.1      2026-03-24 [1] CRAN (R 4.5.2)
    fuzzyjoin            0.1.8      2026-02-20 [1] CRAN (R 4.5.2)
    gargle               1.6.1      2026-01-29 [1] CRAN (R 4.5.2)
    generics             0.1.4      2025-05-09 [1] CRAN (R 4.5.0)
    geojsonsf            2.0.5      2025-11-26 [1] CRAN (R 4.5.2)
    ggplot2              4.0.2      2026-02-03 [1] CRAN (R 4.5.2)
    glue               * 1.8.0      2024-09-30 [1] CRAN (R 4.5.0)
    googledrive          2.1.2      2025-09-10 [1] CRAN (R 4.5.0)
    gtable               0.3.6      2024-10-25 [1] CRAN (R 4.5.0)
    here               * 1.0.2      2025-09-15 [1] CRAN (R 4.5.0)
    highcharter          0.9.4      2022-01-03 [1] CRAN (R 4.5.0)
    hms                  1.1.4      2025-10-17 [1] CRAN (R 4.5.0)
    htmltools            0.5.9      2025-12-04 [1] CRAN (R 4.5.2)
    htmlwidgets          1.6.4      2023-12-06 [1] CRAN (R 4.5.0)
    httpuv               1.6.17     2026-03-18 [1] CRAN (R 4.5.2)
    httr                 1.4.8      2026-02-13 [1] CRAN (R 4.5.2)
    httr2                1.2.2      2025-12-08 [1] CRAN (R 4.5.2)
    igraph               2.2.2      2026-02-12 [1] CRAN (R 4.5.2)
    isoband              0.3.0      2025-12-07 [1] CRAN (R 4.5.2)
    janitor              2.2.1      2024-12-22 [1] CRAN (R 4.5.0)
    jquerylib            0.1.4      2021-04-26 [1] CRAN (R 4.5.0)
    jsonlite           * 2.0.0      2025-03-27 [1] CRAN (R 4.5.0)
    KernSmooth           2.23-26    2025-01-01 [1] CRAN (R 4.5.2)
    knitr                1.51       2025-12-20 [1] CRAN (R 4.5.2)
    later                1.4.8      2026-03-05 [1] CRAN (R 4.5.2)
    lattice              0.22-9     2026-02-09 [1] CRAN (R 4.5.2)
    lazyeval             0.2.2      2019-03-15 [1] CRAN (R 4.5.0)
    leafem               0.2.5      2025-08-28 [1] CRAN (R 4.5.0)
    leaflet              2.2.3      2025-09-04 [1] CRAN (R 4.5.0)
    librarian            1.8.1      2021-07-12 [1] CRAN (R 4.5.0)
    lifecycle            1.0.5      2026-01-08 [1] CRAN (R 4.5.2)
    lubridate            1.9.5      2026-02-04 [1] CRAN (R 4.5.2)
    magrittr             2.0.4      2025-09-12 [1] CRAN (R 4.5.0)
    mapgl                0.4.5.9000 2026-03-31 [1] Github (bbest/mapgl@74df3b1)
    mapview              2.11.4     2025-09-08 [1] CRAN (R 4.5.0)
    markdown             2.0        2025-03-23 [1] CRAN (R 4.5.0)
    Matrix               1.7-5      2026-03-21 [1] CRAN (R 4.5.2)
    memoise              2.0.1      2021-11-26 [1] CRAN (R 4.5.0)
    mgcv                 1.9-4      2025-11-07 [1] CRAN (R 4.5.0)
    mime                 0.13       2025-03-17 [1] CRAN (R 4.5.0)
    nlme                 3.1-168    2025-03-31 [1] CRAN (R 4.5.2)
    otel                 0.2.0      2025-08-29 [1] CRAN (R 4.5.0)
    pillar               1.11.1     2025-09-17 [1] CRAN (R 4.5.0)
    pkgbuild             1.4.8      2025-05-26 [1] CRAN (R 4.5.0)
    pkgconfig            2.0.3      2019-09-22 [1] CRAN (R 4.5.0)
    pkgload              1.5.0      2026-02-03 [1] CRAN (R 4.5.2)
    plotly               4.12.0     2026-01-24 [1] CRAN (R 4.5.2)
    png                  0.1-9      2026-03-15 [1] CRAN (R 4.5.2)
    promises             1.5.0      2025-11-01 [1] CRAN (R 4.5.0)
    proxy                0.4-29     2025-12-29 [1] CRAN (R 4.5.2)
    purrr              * 1.2.1      2026-01-09 [1] CRAN (R 4.5.2)
    quantmod             0.4.28     2025-06-19 [1] CRAN (R 4.5.0)
    R6                   2.6.1      2025-02-15 [1] CRAN (R 4.5.0)
    rappdirs             0.3.4      2026-01-17 [1] CRAN (R 4.5.2)
    raster               3.6-32     2025-03-28 [1] CRAN (R 4.5.0)
    RColorBrewer         1.1-3      2022-04-03 [1] CRAN (R 4.5.0)
    Rcpp                 1.1.1      2026-01-10 [1] CRAN (R 4.5.2)
    readr                2.2.0      2026-02-19 [1] CRAN (R 4.5.2)
    rlang                1.1.7      2026-01-09 [1] CRAN (R 4.5.2)
    rlist                0.4.6.2    2021-09-03 [1] CRAN (R 4.5.0)
    rmarkdown            2.31       2026-03-26 [1] CRAN (R 4.5.2)
    rnaturalearth        1.2.0      2026-01-19 [1] CRAN (R 4.5.2)
    rnaturalearthhires   1.0.0.9000 2025-10-02 [1] Github (ropensci/rnaturalearthhires@e4736f6)
    RPostgres            1.4.10     2026-02-16 [1] CRAN (R 4.5.2)
    rprojroot            2.1.1      2025-08-26 [1] CRAN (R 4.5.0)
    rstudioapi           0.18.0     2026-01-16 [1] CRAN (R 4.5.2)
    S7                   0.2.1      2025-11-14 [1] CRAN (R 4.5.2)
    sass                 0.4.10     2025-04-11 [1] CRAN (R 4.5.0)
    satellite            1.0.6      2025-08-21 [1] CRAN (R 4.5.0)
    scales               1.4.0      2025-04-24 [1] CRAN (R 4.5.0)
    sessioninfo          1.2.3      2025-02-05 [1] CRAN (R 4.5.0)
    sf                   1.1-0      2026-02-24 [1] CRAN (R 4.5.2)
    shiny                1.13.0     2026-02-20 [1] CRAN (R 4.5.2)
    shinyWidgets         0.9.1      2026-03-09 [1] CRAN (R 4.5.2)
    snakecase            0.11.1     2023-08-27 [1] CRAN (R 4.5.0)
    sp                   2.2-1      2026-02-13 [1] CRAN (R 4.5.2)
    stars                0.7-1      2026-02-13 [1] CRAN (R 4.5.2)
    stringi              1.8.7      2025-03-27 [1] CRAN (R 4.5.0)
    stringr              1.6.0      2025-11-04 [1] CRAN (R 4.5.0)
    terra                1.9-11     2026-03-26 [1] CRAN (R 4.5.2)
    tibble             * 3.3.1      2026-01-11 [1] CRAN (R 4.5.2)
    tidyr                1.3.2      2025-12-19 [1] CRAN (R 4.5.2)
    tidyselect           1.2.1      2024-03-11 [1] CRAN (R 4.5.0)
    timechange           0.4.0      2026-01-29 [1] CRAN (R 4.5.2)
    TTR                  0.24.4     2023-11-28 [1] CRAN (R 4.5.0)
    tzdb                 0.5.0      2025-03-15 [1] CRAN (R 4.5.0)
    units                1.0-1      2026-03-11 [1] CRAN (R 4.5.2)
    usethis              3.2.1      2025-09-06 [1] CRAN (R 4.5.0)
    uuid                 1.2-2      2026-01-23 [1] CRAN (R 4.5.2)
    V8                   8.0.1      2025-10-10 [1] CRAN (R 4.5.0)
    vctrs                0.7.2      2026-03-21 [1] CRAN (R 4.5.2)
    viridisLite          0.4.3      2026-02-04 [1] CRAN (R 4.5.2)
    visNetwork           2.1.4      2025-09-04 [1] CRAN (R 4.5.0)
    withr                3.0.2      2024-10-28 [1] CRAN (R 4.5.0)
    xfun                 0.57       2026-03-20 [1] CRAN (R 4.5.2)
    xtable               1.8-8      2026-02-22 [1] CRAN (R 4.5.2)
    xts                  0.14.2     2026-02-28 [1] CRAN (R 4.5.2)
    yaml                 2.3.12     2025-12-10 [1] CRAN (R 4.5.2)
    zoo                  1.8-15     2025-12-15 [1] CRAN (R 4.5.2)

 [1] /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/library

 * ── Packages attached to the search path.
 V ── Loaded and on-disk version mismatch.
 P ── Loaded and on-disk path mismatch.

──────────────────────────────────────────────────────────────────────────────