Package {spatpersist}


Title: Create Persistent Identifiers for Longitudinal Spatial Data
Version: 0.1.0
Description: Creates dataset-local persistent identifiers for polygon units observed across time. Configurable overlap metrics, thresholds, and one-to-one matching rules identify continuity while separate identifiers track boundary versions and broader lineages through splits, mergers, and replacements. Candidate-link diagnostics, validation checks, registry reconciliation, and lineage summaries keep the resulting decisions inspectable and reproducible.
License: MIT + file LICENSE
URL: https://github.com/emre-cebeci/spatpersist
BugReports: https://github.com/emre-cebeci/spatpersist/issues
Encoding: UTF-8
Imports: sf
Suggests: knitr, rmarkdown, testthat (≥ 3.0.0)
VignetteBuilder: knitr
Config/testthat/edition: 3
Config/roxygen2/version: 8.1.0
NeedsCompilation: no
Packaged: 2026-08-24 15:38:56 UTC; emrecebeci
Author: Emre Cebeci [aut, cre]
Maintainer: Emre Cebeci <cebeciemre1@gmail.com>
Repository: CRAN
Date/Publication: 2026-09-05 13:50:02 UTC

Persistent identifiers for longitudinal polygon data

Description

spatpersist links polygon features observed at different times while keeping four related concepts separate: the persistent identity of a unit, versions of its geometry, its broader lineage, and its immediate parent following a structural transition. Matching is based on polygon overlap and is designed to remain auditable through explicit scores, confidence flags, transition tables, and validation checks.

Main workflow

Use persist_ids() to assign identifiers to a time-indexed sf object. Inspect the result with id_transitions() and id_lineages(), and run validate_ids() before relying on the identifiers downstream. plot_lineage() provides a compact visual check of one lineage through time.

Reproducible reruns

A registry written by persist_ids() can be supplied to a later run so that already-observed units retain their identifiers. Registry matches are reported in the registry_matched output column. If new spatial evidence connects multiple prior lineages, they consolidate under the smallest existing lineage identifier.

Interpretation

A persistent ID represents continuity under the selected overlap rule; it is not a claim that the polygon is geometrically unchanged. Geometry changes receive new version IDs. Splits, merges, births, deaths, and ambiguous candidates remain visible in the audit outputs rather than being silently collapsed. All identifiers are dataset-local, not globally unique: their persistence is scoped to one dataset and its registry chain.

Author(s)

Maintainer: Emre Cebeci cebeciemre1@gmail.com

Authors:

See Also

vignette("spatpersist-workflow", package = "spatpersist")


Create an example longitudinal spatial dataset

Description

Generates synthetic polygon data showing common spatial continuity problems: stable units, boundary changes, renames, and splits.

Usage

example_units()

Value

An sf object containing synthetic polygons.

Examples

data <- example_units()
plot(data["name"])

Summarize persistent spatial identities and lineages

Description

Creates one row per spatial_id with its lineage, parent, observed time span, number of observations and geometry versions, registry recovery count, ambiguity count, and minimum non-missing match confidence.

Usage

id_lineages(data, time)

Arguments

data

A result returned by persist_ids().

time

A single character string naming the time column in data.

Value

A data frame with one row per persistent spatial identity.

Examples

result <- persist_ids(example_units(), time = "year")
id_lineages(result, time = "year")


Inspect spatial transition diagnostics

Description

Retrieves the candidate-link diagnostics created by persist_ids(). The table contains every positive-area overlap considered across successive observed periods, including its overlap statistics, mutual-best and ambiguity flags, candidate status, selection, and confidence.

Usage

id_transitions(x)

Arguments

x

An sf result returned by persist_ids().

Value

A data frame of transition diagnostics.

Examples

result <- persist_ids(example_units(), time = "year")
id_transitions(result)


Create persistent identifiers for longitudinal polygons

Description

Assigns persistent identifiers by comparing polygons in each pair of successive observed time periods. A polygon continues an earlier identity when its selected overlap statistic meets threshold. Candidate links are selected one-to-one, so a persistent identifier cannot occur more than once in the same time period.

Usage

persist_ids(
  data,
  time,
  threshold = 0.75,
  metric = c("share_old", "share_new", "iou"),
  match_rule = c("greedy", "mutual_best"),
  ambiguity_tolerance = 0.05,
  ambiguity_action = c("flag", "new", "error"),
  event_threshold = 0.1,
  lineage_threshold = 0.6,
  version_threshold = 1,
  geometry_action = c("error", "repair"),
  geometry_precision = NULL,
  max_time_gap = Inf,
  registry = NULL,
  registry_threshold = 0.999999
)

Arguments

data

An sf object containing polygon geometries observed over time.

time

A single character string naming the time column in data.

threshold

A number from 0 to 1 giving the minimum overlap required for continuity.

metric

The overlap statistic used for matching: "share_old", "share_new", or "iou".

match_rule

Candidate selection rule: "greedy" considers every eligible link, while "mutual_best" considers only links that are best for both their predecessor and successor.

ambiguity_tolerance

Maximum difference between eligible top scores that is treated as a near tie.

ambiguity_action

How near ties should be handled: "flag" selects deterministically and marks the result, "new" rejects ambiguous links, and "error" stops before assigning IDs.

event_threshold

A number from 0 to 1 giving the minimum share of a predecessor or successor used when detecting splits and mergers.

lineage_threshold

A number from 0 to 1 giving the minimum share of the smaller polygon that must overlap to connect two observations in the same lineage. Accepted identity matches are always connected.

version_threshold

A number from 0 to 1 giving the minimum IoU needed for a continuing identity to retain its current geometry version. The default of 1 creates a new version for any boundary change.

geometry_action

How invalid polygon geometries should be handled: "error" or "repair". Repair uses sf::st_make_valid() and returns the repaired geometries.

geometry_precision

Optional positive precision scale passed to sf::st_set_precision() before spatial operations. Coordinates are rounded as round(x * geometry_precision) / geometry_precision; for example, use 100 for two decimal places. NULL preserves input precision.

max_time_gap

Maximum gap across which identities may continue. Defaults to Inf. Finite values require a numeric, Date, or POSIXt time column; Date gaps use days and POSIXt gaps use seconds.

registry

Optional prior sf result from persist_ids(). Same-time geometries are used to preserve previously issued spatial and geometry-version identifiers across reruns. Existing lineage identifiers are also retained unless new spatial evidence connects multiple registry lineages; the connected component then uses the lexicographically smallest existing lineage identifier.

registry_threshold

Minimum same-time IoU required to recover an observation from registry.

Details

Names and other non-spatial attributes are not used for matching. Unmatched polygons receive new identifiers. Strong spatial links connect related identities in a shared lineage and are used to describe splits, mergers, replacements, and complex reorganizations.

Candidate-link diagnostics are stored with the result and can be retrieved with id_transitions().

Identifiers are dataset-local. Values such as SID000001 and LID000001 are persistent only within one dataset and its registry chain; they are not globally unique across independent projects. Exact same-period coextensive geometries are rejected because geometry-only matching cannot distinguish their identities reproducibly.

Value

The input sf object with identifier and transition columns, including spatial_id, spatial_version_id, lineage_id, parent_id, transition_type, match_score, match_confidence, and match_ambiguous. registry_matched indicates recovered observations.

Examples

data <- example_units()
result <- persist_ids(data, time = "year", threshold = 0.75)
result[, c("year", "name", "spatial_id")]


Plot spatial identity lineages over time

Description

Draws observations on a time-by-identity layout. Solid links are selected identity continuations; dashed links are additional spatial lineage relationships such as split and merger branches.

Usage

plot_lineage(
  data,
  time,
  lineage_id = NULL,
  show_legend = TRUE,
  node_cex = 1.2,
  ...
)

Arguments

data

A result returned by persist_ids().

time

A single character string naming the time column in data.

lineage_id

Optional character vector of lineage IDs to display. NULL displays every lineage.

show_legend

Logical; display a transition-type legend.

node_cex

Numeric node size.

...

Additional arguments passed to graphics::plot.default().

Value

Invisibly, a list containing the plotted nodes and edges data.

Examples

result <- persist_ids(example_units(), time = "year")
plot_lineage(result, time = "year", lineage_id = "LID000004")


Validate persistent spatial identifiers

Description

Checks a result for structural inconsistencies, including missing output values, duplicate identities within a time period, identities assigned to multiple lineages, invalid geometry-version sequences, invalid transition labels, invalid match scores, and broken parent or continuation links.

Usage

validate_ids(data, time)

Arguments

data

A data frame or sf object containing spatial ID output.

time

A single character string naming the time column in data.

Value

A data frame describing validation issues. A result with zero rows passed all implemented checks.

Examples

result <- persist_ids(example_units(), time = "year")
validate_ids(result, time = "year")