spatpersist

R-CMD-check test-coverage

spatpersist creates persistent identifiers for polygon units observed over time. It is designed for datasets in which names, labels, and boundaries may change and reliable longitudinal IDs do not already exist.

Identity is determined from configurable spatial continuity rules. Names and other descriptive attributes are preserved in the result but are not used for matching.

spatpersist is under active development. Review transition diagnostics and validation results before using generated IDs in analysis.

Installation

Install the development version from GitHub:

# install.packages("remotes")
remotes::install_github("emre-cebeci/spatpersist")

Quick start

library(spatpersist)

polygons <- example_units()

result <- persist_ids(
  polygons,
  time = "year",
  threshold = 0.75,
  metric = "share_old"
)

sf::st_drop_geometry(result)[
  , c(
    "year",
    "name",
    "spatial_id",
    "spatial_version_id",
    "lineage_id",
    "parent_id",
    "transition_type"
  )
]

The result remains an sf object in its original row order.

Output identifiers

Column Meaning
spatial_id One continuing spatial identity. It occurs at most once per time period.
spatial_version_id A consecutive boundary spell within a spatial_id.
lineage_id A broader family connecting related identities through splits, mergers, and replacements.
parent_id The strongest predecessor when a related observation receives a new identity.
transition_type initial, continuation, new, split, merger, replacement, or complex.

Selected links also receive match_score, match_confidence, and match_ambiguous. Registry recovery is recorded in registry_matched.

Identifiers are dataset-local, not globally unique. Independent projects can both contain values such as SID000001; persistence is guaranteed only within one dataset and its registry chain. Add a project-specific key when combining outputs from unrelated datasets.

Matching controls

The core identity rule is controlled by:

For example, this rejects near ties instead of selecting one deterministically:

conservative <- persist_ids(
  polygons,
  time = "year",
  metric = "iou",
  threshold = 0.75,
  match_rule = "mutual_best",
  ambiguity_action = "new"
)

event_threshold, lineage_threshold, and version_threshold separately control event detection, lineage links, and geometry-version changes.

Audit and validate

Every positive-area overlap considered by the matching engine is available as a regular data frame:

transitions <- id_transitions(result)
lineages <- id_lineages(result, time = "year")
issues <- validate_ids(result, time = "year")

nrow(issues) # zero means all implemented checks passed

Diagnostics distinguish spatial eligibility, rule eligibility, ambiguity, mutual-best status, selection, confidence, and lineage membership.

Plot a lineage

plot_lineage(
  result,
  time = "year",
  lineage_id = "LID000004"
)

Solid edges are selected identity continuations. Dashed edges are additional lineage relationships, such as branches created by a split or merger.

Preserve IDs across reruns

A previous result can be supplied as an optional registry. Matching same-time geometries retain their issued IDs, while new identities are allocated above the registry’s existing range.

updated_result <- persist_ids(
  updated_polygons,
  time = "year",
  registry = result
)

Conflicting registry identity or geometry-version evidence stops reconciliation rather than silently renumbering identities. Lineages are different: if new spatial evidence connects observations anchored to multiple registry lineages, the complete connected component is consolidated under the lexicographically smallest existing lineage_id.

Input requirements

Invalid polygons can be repaired explicitly with geometry_action = "repair". Optional geometry precision and maximum time-gap controls are also available.

Current scope

See vignette("spatpersist-workflow") for a complete walkthrough.

Contributing

Bug reports and contributions are welcome. Please use only synthetic or public data in issues, examples, and pull requests; do not upload confidential or restricted datasets. See CONTRIBUTING.md for the development and reporting workflow.