---
title: "Anonymisation of Spatial Point Patterns and Raster Objects."
author: "Malone, B."
date: "`r Sys.Date()`"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Anonymisation of Spatial Point Patterns and Raster Objects.}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup_reset, include=FALSE}
# Save original state
old_opts    <- options()
old_par     <- par(no.readonly = TRUE)
old_wd      <- getwd()
orig_search <- search()

# On exit, restore state and detach any new packages
on.exit({
  options(old_opts)
  par(old_par)
  setwd(old_wd)
  new_pkgs <- setdiff(search(), orig_search)
  for (pkg in new_pkgs) {
    if (grepl("^package:", pkg)) {
      detach(pkg, character.only = TRUE, unload = TRUE)
    }
  }
}, add = TRUE)
```


## Introduction
This demo provides context and use cases for the three core functions of the `tangles` R package:

* `tangles` – Anonymises spatial point patterns and raster objects.
* `tangler` – re-applies a known Anonymisation to other data using a detangler object.
* `detangles` – restores Anonymised data back to original coordinates using a valid detangler.

The main driver behind this package is confidentiality. While modern science is becoming increasingly open and collaborative, some spatial data—especially that which relates to sensitive locations—must be shared carefully. Anonymisation allows us to retain spatial structure and support robust analysis while obscuring real-world locations.

`tangles` Anonymises data through randomized spatial transformations:

* Vertical shifts
* Horizontal shifts
* Rotational shifts

Each transformation is stored with a hash-verified detangler, enabling reversible workflows and consistent Anonymisation across related datasets.

## Setup

`tangles` accepts spatial data in the form of:

- Two-column matrices or data frames (X, Y coordinates)
- `sf` POINT objects
- `terra` raster layers or stacks

It also allows exporting of Anonymised point data to shapefiles if needed.

```{r libraries, echo=TRUE, message=FALSE, warning=FALSE}
# Load required libraries
library(tangles)
library(digest)
library(terra)
library(sf)

# Load point data
data("HV_subsoilpH")

# Load raster data from files
ext_path <- system.file("extdata", package = "tangles")
rast.files <- list.files(path = ext_path, full.names = TRUE)
rasters <- terra::rast(rast.files)
```

## Tangling Point Data

`tangles()` can be used directly on `sf` objects too. Here's an example with shapefile export:

```{r tangles-point, echo=TRUE, message=FALSE, warning=FALSE}
xyData <- as.matrix(HV_subsoilpH[, 1:2])
tangles.out <- tangles(
  data = xyData,
  depth = 3,
  rasterdata = FALSE,
  raster_object = FALSE,
  saveTangles = TRUE,
  exportShapefile = TRUE,
  path = tempdir()
)

# Using sf input
df <- HV_subsoilpH[, 1:2]
sf_pts <- st_as_sf(df, coords = c("X", "Y"))
tangles.sf.out <- tangles(
  data = sf_pts,
  depth = 3,
  saveTangles = TRUE,
  exportShapefile = TRUE,
  path = tempdir()
)
```

## Tangling Raster Data

You can also use `tangles()` directly on raster stacks to Anonymise them, particularly when you want to generate a new detangler object.

```{r tangles-raster, echo=TRUE, message=FALSE, warning=FALSE}
tangles.ras.out <- tangles(
  data = rasters,
  depth = 3,
  rasterdata = TRUE,
  raster_object = TRUE,
  saveTangles = TRUE,
  path = tempdir()
)
```

## Tangling Point and Raster Data Together

When tangling both point and raster data using the same transformation, it is important to constrain the rotation angles to preserve raster alignment. This can be done by setting `rasterdata = TRUE` in the `tangles()` call for point data. This ensures that any rotation is limited to 90°, 180°, or 270°, which maintains compatibility with grid-based raster structures.

```{r tangling-together, echo=TRUE, message=FALSE, warning=FALSE}
# 1. Tangling the point data
xyData <- as.matrix(HV_subsoilpH[, 1:2])
tangles.out <- tangles(
  data = xyData,
  depth = 4,
  rasterdata = TRUE,
  raster_object = FALSE,
  saveTangles = FALSE
)

# 2. Tangling the raster data using the same detangler
tangler.out <- tangler(
  data = rasters,
  tanglerInfo = tangles.out[[2]],
  raster_object = TRUE,
  stub = "combined",
  saveTangles = FALSE
)

# 3. Convert points to sf objects
original_pts <- st_as_sf(HV_subsoilpH, coords = c("X", "Y"))
tangled_pts <- st_as_sf(as.data.frame(tangles.out[[1]]), coords = c("X", "Y"))

# 4. Plot both
par(mfrow = c(1, 2))
plot(rasters[[1]], main = "Original Raster + Points")
plot(original_pts, add = TRUE, pch = 16, col = "blue")

plot(tangler.out[[1]][[1]], main = "Tangled Raster + Points")
plot(tangled_pts, add = TRUE, pch = 16, col = "red")
par(mfrow = c(1, 1))
```

## Detangling Back to Original

To restore Anonymised data to its original spatial configuration, use the `detangles()` function with the correct `tanglerInfo` and `hash_key`. This will reverse all transformations in the correct order.

Note:
- For successful restoration, the `hash_key` must match the one embedded in the detangler object.
- If working with raster data, ensure the same constraints (e.g., right-angle rotations) were applied during tangling.
- You can optionally export the restored point data as shapefiles using `exportShapefile = TRUE`, which is useful for downstream spatial analysis or visualization.

```{r detangles, echo=FALSE, eval=FALSE, message=FALSE, warning=FALSE}
# Detangle points
detangled_points <- detangles(
  data = tangles.out[[1]],
  tanglerInfo = tangles.out[[2]],
  raster_object = FALSE,
  stub = "demo_points",
  hash_key = tangles.out[[2]]$hash,
  saveTangles = TRUE,
  path = tempdir()
)

# Detangle rasters
detangled_rasters <- detangles(
  data = tangler.out[[1]],
  tanglerInfo = tangles.out[[2]],
  raster_object = TRUE,
  stub = "demo_raster",
  hash_key = tangles.out[[2]]$hash,
  saveTangles = TRUE,
  path = tempdir()
)
```

## Final Notes

The `tangles` package provides a reversible and flexible method for Anonymising spatial data. It supports point and raster formats, and can be integrated into privacy-preserving data sharing workflows. While the Anonymised coordinates are spatially transformed, internal spatial relationships (e.g., autocorrelation) remain intact, allowing robust analysis without revealing exact locations.

For best results:
- Use non-right-angle rotations only on point data.
- For rasters, limit rotations to 90°, 180°, or 270°.
- Consider exporting Anonymised points as shapefiles when sharing with collaborators.