Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@ Encoding: UTF-8
LazyData: true
Imports:
jsonlite,
httr
httr,
stats,
tools,
utils
Suggests:
knitr,
data.table,
Comment thread
Copilot marked this conversation as resolved.
nanoparquet,
rmarkdown,
rWCVP,
rWCVPdata,
sf,
terra,
testthat,
devtools,
BIEN,
TNRS,
vcr (>= 0.6.0)
Additional_repositories: https://matildabrown.github.io/drat
VignetteBuilder: knitr
RoxygenNote: 7.3.2
RoxygenNote: 7.3.3
5 changes: 5 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ export(NSR)
export(NSR_citations)
export(NSR_data_dictionary)
export(NSR_from_coordinates)
export(NSR_local)
export(NSR_local_build)
export(NSR_local_by_region)
export(NSR_local_remove)
export(NSR_local_status)
export(NSR_metadata)
export(NSR_political_divisions)
export(NSR_simple)
Expand Down
18 changes: 18 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,24 @@

## NEW FEATURES
* Added new function to check native status using species name and coordinates (NSR_from_coordinates)
* Added an offline implementation: NSR_local() answers without an internet connection from a
local cache built by NSR_local_build(), with NSR_local_status(), NSR_local_remove() and
NSR_local_by_region() alongside it. The checklist sources are acquired on the user's
machine; nothing derived ships with the package. The offline path needs data.table,
nanoparquet, terra, sf and TNRS, all of which are Suggests.
* NSR_local() gains exclude_extinct (default TRUE). WCVP flags a distribution extinct
without a date, so the cache keeps those records and the query decides: the default
answers about the present day, FALSE models a past distribution. Either way a region
a taxon has been lost from stays part of its native range, so such a record is
reported absent rather than introduced.
* NSR_local() answers from political division names by default. Coordinates are
echoed but not used unless use_coordinates = TRUE: placing every record by point
costs a raster lookup per call, and turning coordinates into political divisions is
what GVS already does.
* Where a record gives both coordinates and division names and the two resolve to
different places, NSR_local() no longer pools them. Neither is treated as
authoritative: place_conflict flags the record, native_status is UNK, and the two
bases are answered separately in native_status_coordinates and native_status_names.

## Bug fixes

Expand Down
740 changes: 740 additions & 0 deletions R/NSR_local.R

Large diffs are not rendered by default.

387 changes: 387 additions & 0 deletions R/NSR_local_set.R

Large diffs are not rendered by default.

69 changes: 69 additions & 0 deletions R/local_altdiv.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# ===========================================================================
# Divisions that belong to another division system
#
# A large part of the record stream declares a state that is not a GADM division:
# Swedish landskap and lappmarker, Watsonian vice-counties, Norwegian counties from
# after the 2018 and 2020 reforms. GNRS records these as units in their own right
# and, where it knows which GADM units they cover, writes that extent to the shared
# cache (altdiv-*). NSR reads those tables the same way it reads the GNRS reference:
# straight from the cache, without depending on the package.
#
# Only a unit whose extent is known is of any use here: it turns a declared name
# that matched nothing into the set of divisions the question is really about, so
# native status can be answered below country level. A unit with no extent yet adds
# no regions, which leaves the query where it was - answered at country level.
# ===========================================================================

#' The alternative divisions whose extent is known, keyed by country and name
#'
#' Internal. NULL when the component has not been built. Cached per directory
#' for the session, like the other reference tables.
#' @keywords internal
#' @noRd
nsr_altdiv <- function(dir) {
key <- paste0("altdiv:", dir)
if (!is.null(nsr_session[[key]])) return(nsr_session[[key]])
f <- function(x) file.path(dir, paste0("altdiv-", x, ".gz.parquet"))
if (!all(file.exists(f(c("units", "names", "extent"))))) return(NULL)
units <- as.data.frame(nanoparquet::read_parquet(f("units")))
names_ <- as.data.frame(nanoparquet::read_parquet(f("names")))
extent <- as.data.frame(nanoparquet::read_parquet(f("extent")))
# only exact names, and only units that have an extent: a regex system (the
# vice-counties) has none yet, and a unit without one adds no regions
names_ <- names_[names_$match == "exact" & names_$entity_key %in% extent$entity_key, , drop = FALSE]
iso <- units$country_iso[match(names_$entity_key, units$entity_key)]
out <- list(
key = paste(toupper(iso), tolower(trimws(names_$name)), sep = "\u001f"),
entity = names_$entity_key,
extent = split(extent$gid, extent$entity_key),
level = vapply(split(extent$level, extent$entity_key), function(x) as.integer(x[1]), integer(1))
)
nsr_session[[key]] <- out
out
}

#' Region keys for a declared division that belongs to another system
#'
#' Internal. Vectorised over rows; returns a list of character vectors, empty
#' where the declared name is not such a division or its extent is unknown.
#' @keywords internal
#' @noRd
nsr_altdiv_keys <- function(country_iso, name, dir) {
n <- length(name)
out <- vector("list", n)
for (i in seq_len(n)) out[[i]] <- character(0)
a <- nsr_altdiv(dir)
if (is.null(a)) return(out)
nm <- tolower(trimws(ifelse(is.na(name), "", name)))
cc <- toupper(ifelse(is.na(country_iso), "", country_iso))
hit <- match(paste(cc, nm, sep = "\u001f"), a$key)
for (i in which(!is.na(hit))) {
k <- a$entity[hit[i]]
gid <- a$extent[[k]]
# NSR's regions are countries and states; a county-level extent has nothing to map
# onto yet, so it is skipped rather than answered at the wrong level
if (!length(gid) || a$level[[k]] != 1L) next
out[[i]] <- paste0("gadm1:", gid)
}
out
}
Loading
Loading