VasGBIF provides a reproducible, tracheophyte-optimized, and computationally efficient framework for transforming GBIF records into analysis-ready datasets. The package functions are organized into 8 steps. This basic example demonstrates the complete workflow using the built-in example dataset.
Each step below progressively filters records through taxonomic, quality, and coordinate checks, so the dataset shrinks from the raw download to a high-quality, analysis-ready set.
import_records() reads a GBIF occurrence download ZIP
(‘SIMPLE_CSV’ or Darwin Core Archive), extracts the occurrence table,
and returns an "import" data.table of the fields required
by the workflow. No records are filtered at this stage — the raw
issue column is preserved for the next step.
The built-in example dataset is a GBIF download of Medicago sativa (preserved specimens with coordinates, obtained 23 July 2026): https://doi.org/10.15468/dl.nt5exp.
gbif_file <- system.file(
"extdata", "0003386-260721160103020.zip",
package = "VasGBIF"
)
occ_import <- import_records(path = gbif_file)
VasGBIF_summary$initial_records <- nrow(occ_import)
VasGBIF_summary$initial_taxa <- uniqueN(occ_import$scientificName)
print(occ_import)The returned "import" object is a
data.table with the GBIF fields used by the workflow
(gbifID coerced to character, plus the raw
issue column).
check_taxon() submits species- and infraspecific-rank
names to the Taxonomic Name Resolution Service (TNRS; Boyle et al. 2013)
for resolution against the World Checklist of Vascular Plants (WCVP) or
World Flora Online (WFO). Synonyms are resolved to accepted names;
records that fail the match-score threshold or lack an accepted/synonym
status are excluded from the downstream table and reported in the
summary for manual review.
taxa_checked <- check_taxon(occ_import = occ_import, accuracy = 0.85)
VasGBIF_summary$after_taxon_resolved <- nrow(taxa_checked$occ_taxa_checked)
VasGBIF_summary$final_taxa <- uniqueN(taxa_checked$occ_taxa_checked$Accepted_name)
print(taxa_checked)The occ_taxa object contains:
occ_taxa_checked: records with resolved taxonomysummary: one row per submitted name, for reviewing
match qualityruntime: execution timecustomized_filter() joins the imported records with the
resolved taxonomy and the parsed issue flags, then applies the enabled
filter rules to retain only high-quality records. By default the
countryCode, coordinateUncertainty (<=
10,000 m), and gbif_issues_max (<= 5) rules are enabled;
date, identifiedBy, and
recordedBy are opt-in. Every step is recorded in the
per-rule summary table.
filtered <- customized_filter(
occ_import = occ_import,
taxa_checked = taxa_checked
)
VasGBIF_summary$after_customized_filter <- nrow(filtered$occ_filtered)
print(filtered)The customFiltered object contains:
occ_filtered: the filtered occurrence tableocc_marked: the records failed customized quality
rulessummary: per-rule rule,
dropped, and remaining countspar_clean_coordinates() validates coordinates with
CoordinateCleaner (Zizka et al. 2019) to flag spatial errors such as
centroids, capitals, marine coordinates, and zero coordinates, splitting
records into cleaned and problematic tables. Validation is parallelized
across user-specified threads.
cleaned_coordinates <- par_clean_coordinates(
input = filtered$occ_filtered,
species = 'Accepted_name_id',
latitude = 'decimalLatitude',
longitude = 'decimalLongitude',
threads = 8,tests = c("capitals", "centroids", "equal", "gbif", "institutions", "outliers", "seas", "zeros"))
VasGBIF_summary$cleaned <- nrow(cleaned_coordinates$CoordinateCleaned)
VasGBIF_summary$problematic <- nrow(cleaned_coordinates$CoordinateProblematic)
print(cleaned_coordinates)The CoordinateRefined object contains:
CoordinateCleaned: records with valid coordinatesCoordinateProblematic: records failing one or more
CoordinateCleaner tests (including those lack coordinates)runtime: execution timedetect_native_coord() and
detect_native_country() match each record against WCVP
distribution data (the internal Distributions dataset) via
WGSRPD Level 3 areas to classify it as native, introduced, extinct,
location_doubtful, or unknown. Records with validated coordinates are
matched spatially by detect_native_coord(); records without
coordinates or failed with cleaned_coordinates() are
matched through their country code by
detect_native_country(). Every classification records how
it was obtained in native_status_source.
native_detected_coord <- detect_native_coord(
input = cleaned_coordinates$CoordinateCleaned,
species = 'Accepted_name',
latitude = 'decimalLatitude',
longitude = 'decimalLongitude', buffer_km = 10, buffer_chunk_size = 2000)
native_detected_country <- detect_native_country(
input = cleaned_coordinates$CoordinateProblematic,
country = 'countryCode',
species = 'Accepted_name')
native_detected <- rbind(
native_detected_coord,
native_detected_country[is.na(decimalLatitude) | is.na(decimalLongitude),],
use.names = TRUE,
fill = TRUE
)
VasGBIF_summary$native <- native_detected[native_status == "native", .N]
VasGBIF_summary$introduced <- native_detected[native_status == "introduced", .N]
VasGBIF_summary$extinct <- native_detected[native_status == "extinct", .N]
VasGBIF_summary$location_doubtful <- native_detected[
native_status == "location_doubtful", .N
]
VasGBIF_summary$unknown <- native_detected[native_status == "unknown", .N]
print(native_detected)The nativeDetected objects are data.tables
keyed by gbifID. Each retains every column of the input
records and appends LEVEL3_COD, native_status,
native_status_source, and buffered. The
spatial result (native_detected_coord) carries coordinates
for every record and can be passed straight to
export_records() and map_records() without
joining back to cleaned_coordinates.
map_records() renders the records on an interactive map
via mapview, with geohash-based decluttering and
colour-coding by native status.
export_records() writes the classified records to disk
as two gzip-compressed CSV files:
all_records.csv.gz: all classified records with
validated coordinates, with their native statusnative_records.csv.gz: the subset classified as
nativeThe summary statistics provide a quantitative overview of data reduction through the pipeline:
| Statistic | Description |
|---|---|
initial_records |
Total GBIF occurrence records imported |
initial_taxa |
Unique scientific names before resolution |
after_taxon_resolved |
Records retained after TNRS resolution (score and status thresholds) |
after_customized_filter |
Records retained after the quality filter rules |
cleaned |
Records with valid coordinates after CoordinateCleaner |
problematic |
Records failing one or more coordinate tests |
native |
Records classified as native |
introduced |
Records classified as introduced |
extinct |
Records classified as extinct |
location_doubtful |
Records classified as location_doubtful |
unknown |
Records with no usable distribution evidence |
final_taxa |
Unique accepted taxon names in the final dataset |
The workflow progressively filters the dataset through taxonomic
resolution, quality rules, and coordinate validation. The drop from
initial_records to after_customized_filter
reflects unresolved names and records failing the quality rules; the
drop from after_customized_filter to cleaned
reflects records that failed the coordinate tests being set aside
(records without coordinates are not carried by
clean_coordinates()); and the transition from
initial_taxa to final_taxa reflects taxonomic
resolution.
VasGBIF achieves its speed through:
data.table, stringi, and
terraextract_issues() and native-status lookups in
detect_native_coord() and
detect_native_country() process entire columns in compiled
calls:=, set()) avoids unnecessary copiespar_clean_coordinates() distributes CoordinateCleaner
validation across threadsOn a standard laptop, VasGBIF compiles one million occurrence records within 15 minutes.
GBIF.org (23 July 2026) GBIF Occurrence Download https://doi.org/10.15468/dl.nt5exp