MyNixOS website logo
Description

Weighted Spatial Tessellations in Constrained Polygon Domains.

Provides tools for weighted spatial tessellation using Euclidean and geodesic distances within constrained polygonal domains. The package can generate complete and connected spatial partitions that respect complex boundaries, heterogeneous point weights, and optional resistance or terrain effects. The methods extend weighted Voronoi tessellations to constrained domains and graph-based cost-distance surfaces. For background see Aurenhammer (1991) <doi:10.1145/116873.116880> and van Etten (2017) <doi:10.18637/jss.v076.i13>.

weightedVoronoi

weightedVoronoi generates weighted spatial partitions that respect boundaries, landscape structure, and heterogeneous point influence using Euclidean or geodesic distance.

🌐 Website: https://HarriRaven.github.io/weightedVoronoi/

Key Features

  1. Weighted Euclidean and geodesic tessellations inside arbitrary polygon domains

  2. Flexible weight semantics via weight_model and weight_power

  3. Custom resistance surfaces and barriers via compose_resistance() and add_barriers()

  4. Terrain-informed geodesic allocation via DEM/Tobler resistance

  5. Terrain-anisotropic geodesic tessellations

  6. Scalable multisource geodesic allocation for additive isotropic geodesics

  7. Uncertainty-aware tessellations with probability and entropy outputs

  8. Temporal tessellation stacks with change and persistence maps

Interactive demo

Explore weighted Voronoi tessellations interactively:

👉 https://harriraven.shinyapps.io/weightedVoronoi-demo/

  • Compare Euclidean vs geodesic allocation
  • Add resistance surfaces and terrain effects
  • Visualise how domain geometry constrains influence

Choosing a workflow

weightedVoronoi supports several spatial tessellation approaches depending on your assumptions about distance, landscape structure, and analysis goals.

Quick guide:

Straight-line influence (fastest) → distance = "euclidean"

Constrained by domain geometry (no crossing gaps/barriers) → distance = "geodesic"

Landscape affects movement (e.g. terrain, land cover) → provide resistance_rast or dem_rast

Uphill vs downhill matters (directional movement) → anisotropy = "terrain"

Repeated runs (uncertainty or time series) → use prepare_geodesic_context() + geodesic_engine = "multisource"

Uncertain weights → weighted_voronoi_uncertainty()

Time series of tessellations → weighted_voronoi_time()

For a more detailed guide, see the vignette.

Installation

install.packages("remotes")
remotes::install_github("HarriRaven/weightedVoronoi")

library(sf)
library(terra)
library(weightedVoronoi)

Use a projected CRS (units in metres)

crs_use \<- 32636

Domain polygon (simple rectangle for speed)

boundary_sf <- st_sf(
  geometry = st_sfc(
    st_polygon(list(rbind(
      c(0, 0),
      c(1000, 0),
      c(1000, 1000),
      c(0, 1000),
      c(0, 0)
    )))
  ),
  crs = crs_use
)

Generator points with weights

points_sf <- st_sf(
  village = paste0("V", 1:5),
  population = c(50, 200, 1000, 150, 400),
  geometry = st_sfc(
    st_point(c(200, 200)),
    st_point(c(800, 250)),
    st_point(c(500, 500)),
    st_point(c(250, 800)),
    st_point(c(750, 750))
  ),
  crs = crs_use
)

Weighted Euclidean tessellation

out_euc <- weighted_voronoi_domain(
  points_sf = points_sf,
  weight_col = "population",
  boundary_sf = boundary_sf,
  res = 20,
  weight_transform = log10,
  distance = "euclidean",
  # optional: alternative weight behaviour
  weight_model = "multiplicative",
  verbose = FALSE
)

Weighted geodesic tessellation (domain-constrained shortest path distance)

out_geo <- weighted_voronoi_domain(
  points_sf = points_sf,
  weight_col = "population",
  boundary_sf = boundary_sf,
  res = 20,
  weight_transform = log10,
  distance = "geodesic",
  close_mask = TRUE,
  close_iters = 1,
  verbose = FALSE
)

The package also supports uncertainty-aware and temporal workflows; see the vignette for worked examples.

Fast workflows with prepared context

ctx <- prepare_geodesic_context(...)

weighted_voronoi_domain(..., prepared = ctx)

Fast reuse currently supported in:

weighted_voronoi_domain(..., prepared = ctx)

Temporal and uncertainty workflows automatically reuse internal preparation and do not currently accept external prepared contexts

Reusing Geodetic Contexts
ctx <- prepare_geodesic_context(
  boundary_sf = boundary_sf,
  res = 20,
  geodesic_engine = "multisource"
)

# Now reuse for many runs (fast)
out1 <- weighted_voronoi_geodesic(
  points_sf = points_t1,
  weight_col = "population",
  prepared = ctx
)

out2 <- weighted_voronoi_geodesic(
  points_sf = points_t2,
  weight_col = "population",
  prepared = ctx
)

This avoids rebuilding the geodesic graph and can substantially speed up repeated runs (e.g. temporal or uncertainty workflows).

Custom Resistance and Barriers

Build a resistance surface on the same grid, optionally combine layers, then apply barriers before running a geodesic tessellation.

# Use the Euclidean allocation grid as a convenient template
template <- out_euc$allocation

# Base resistance (all 1)
R <- template
terra::values(R) <- 1

# Add a high-friction vertical band (e.g., dense vegetation)
xy <- terra::xyFromCell(R, 1:terra::ncell(R))
band <- xy[,1] > 450 & xy[,1] < 550
vals <- terra::values(R)
vals[band] <- 25
terra::values(R) <- vals

# Add a semi-permeable river barrier (vector line)
river <- st_sf(
  geometry = st_sfc(st_linestring(rbind(c(500, 0), c(500, 1000)))),
  crs = st_crs(boundary_sf)
)

# Tip: for coarse rasters, use width ~ res/2 or res
R2 <- add_barriers(R, river, permeability = "semi", cost_multiplier = 20, width = 20)

out_geo_res <- weighted_voronoi_domain(
  points_sf = points_sf,
  weight_col = "population",
  boundary_sf = boundary_sf,
  res = 20,
  weight_transform = log10,
  distance = "geodesic",
  resistance_rast = R2,
  verbose = FALSE
)

Terrain-aware tessellations

Environmental resistance and terrain can strongly influence spatial allocation.

The example below shows how terrain anisotropy (direction-dependent movement cost) can substantially alter geodesic tessellations compared to isotropic resistance.

Unlike isotropic resistance, terrain-anisotropic geodesic tessellations allow uphill and downhill movement to differ, producing direction-dependent allocation patterns.

Flat domain (no resistance)
Elevation-dependent resistance

Inspect outputs

names(out_euc)

head(out_euc$summary)

out_euc$diagnostics

Outputs

weighted_voronoi_domain() returns:

  • polygons: sf object with one polygon per generator (and attributes)

  • allocation: terra::SpatRaster assigning each raster cell to a generator

  • summary: generator-level summary table (area, share, weights, etc.)

  • diagnostics: diagnostics and settings (coverage, unreachable fraction for geodesic, etc.)

Notes

  • Inputs must be in a projected CRS with metric units (e.g. metres).

  • res controls the raster resolution and therefore the trade-off between speed and boundary fidelity.

  • Geodesic tessellations are typically slower than Euclidean tessellations because shortest-path distances are computed within the domain.

Citation

If you use weightedVoronoi, please cite the associated software note:

bibentry(
  bibtype = "Manual",
  title = "weightedVoronoi: Weighted Spatial Tessellations Using Euclidean and Geodesic Distances",
  author = person("Harri", "Ravenscroft"),
  year = "2026",
  note = "R package version 1.1.1",
  url = "https://github.com/HarriRaven/weightedVoronoi"
)

R-CMD-check

Metadata

Version

1.1.1

License

Unknown

Platforms (80)

    Darwin
    FreeBSD
    Genode
    GHCJS
    Linux
    MMIXware
    NetBSD
    none
    OpenBSD
    Redox
    Solaris
    uefi
    WASI
    Windows
Show all
  • aarch64-darwin
  • aarch64-freebsd
  • aarch64-genode
  • aarch64-linux
  • aarch64-netbsd
  • aarch64-none
  • aarch64-uefi
  • aarch64-windows
  • aarch64_be-none
  • arc-linux
  • arm-none
  • armv5tel-linux
  • armv6l-linux
  • armv6l-netbsd
  • armv6l-none
  • armv7a-linux
  • armv7a-netbsd
  • armv7l-linux
  • armv7l-netbsd
  • avr-none
  • i686-cygwin
  • i686-freebsd
  • i686-genode
  • i686-linux
  • i686-netbsd
  • i686-none
  • i686-openbsd
  • i686-windows
  • javascript-ghcjs
  • loongarch64-linux
  • m68k-linux
  • m68k-netbsd
  • m68k-none
  • microblaze-linux
  • microblaze-none
  • microblazeel-linux
  • microblazeel-none
  • mips-linux
  • mips-none
  • mips64-linux
  • mips64-none
  • mips64el-linux
  • mipsel-linux
  • mipsel-netbsd
  • mmix-mmixware
  • msp430-none
  • or1k-none
  • powerpc-linux
  • powerpc-netbsd
  • powerpc-none
  • powerpc64-linux
  • powerpc64le-linux
  • powerpcle-none
  • riscv32-linux
  • riscv32-netbsd
  • riscv32-none
  • riscv64-linux
  • riscv64-netbsd
  • riscv64-none
  • rx-none
  • s390-linux
  • s390-none
  • s390x-linux
  • s390x-none
  • sh4-linux
  • vc4-none
  • wasm32-wasi
  • wasm64-wasi
  • x86_64-cygwin
  • x86_64-darwin
  • x86_64-freebsd
  • x86_64-genode
  • x86_64-linux
  • x86_64-netbsd
  • x86_64-none
  • x86_64-openbsd
  • x86_64-redox
  • x86_64-solaris
  • x86_64-uefi
  • x86_64-windows