Vector Geometry Operations

Today

  1. Centroids β€” center of mass, and when it lies outside the polygon
  2. Affine transformations β€” shift, scale, rotate with sf arithmetic
  3. Simplification β€” Douglas-Peucker, topology problems, ms_simplify()
  4. Challenge β€” design a line simplification algorithm

Centroids

Center of mass

  • st_centroid() returns the center of mass β€” the balance point of the shape
  • Used for: distance calculations, representative points, labeling
luzern_centroid <- st_centroid(luzern)

  • For concave or C-shaped geometries, the centroid can fall outside the polygon
  • st_point_on_surface() always returns a point inside
  • Typical uses:
    • st_centroid(): Distance metrics, spatial analytics
    • st_point_on_surface(): Map labeling, feature sampling

Affine Transformations

Three elementary operations

  • Affine transformations preserve lines and parallelism β€” angles and lengths may change
  • In sf: applied directly to sfc objects using arithmetic operators
Operation sf syntax Effect
Shift sfc + c(dx, dy) Move without changing shape
Scale (sfc - p) * factor + p Grow or shrink around a point p
Rotate (sfc - p) * R(ΞΈ) + center Rotate around a point p

Shift and Scale

# Shift: move 10 km east
luzern_shifted <- luzern_sfc + c(10000, 0)

# Scale: shrink to 75% around centroid
luzern_scaled  <- (luzern_sfc - luzern_center) * 0.75 + luzern_center

Rotation

  • Shift to origin β†’ rotate β†’ shift back β€” otherwise the geometry rotates around (0, 0)
  • \(R(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix}\)
rotation <- function(deg) {
  r <- deg * pi / 180          # convert degrees to radians
  matrix(                      # 
    c(cos(r), -sin(r),          #   [ cos(r), -sin(r) ]
     sin(r), cos(r)),           #   [ sin(r),  cos(r) ]
    nrow = 2, ncol = 2, byrow = TRUE
  )
}
luzern_rotated <- (luzern_sfc - luzern_center) * rotation(15) + luzern_center

The same pattern applies to all three operations β€” only the transformation differs:

(sfc - center) + center          # identity (no-op)
(sfc - center) * 0.5 + center    # scale
(sfc - center) * rotation(45) + center  # rotate

Transformations compose: shift first, then scale, then rotate gives a different result than the reverse order.

Simplification

Why simplify?

  • Real datasets carry more vertices than any display scale needs
  • Excess vertices: slow rendering, large files, no visual benefit

Challenge: Line simplification Algorithm

  • Given: a polyline V₁, Vβ‚‚, …, Vβ‚™
  • Task: develop an algorithm that removes β€œunnecessary” vertices β€” those that add no meaningful shape information at scale Ξ΅

Closed book!

Do not use any external sources. Solve with your own ideas in groups

Douglas-Peucker

  • st_simplify() uses the Douglas-Peucker algorithm
  • Core idea: a point is unnecessary if it lies close to the line between its neighbours
  • Tolerance Ξ΅ controls how much deviation is acceptable

Step 1: draw baseline from first to last point

Step 2: find the farthest point β€” distance d = 3 > Ξ΅ β†’ keep it

Step 3: recurse on both halves independently

Step 4: continue recursion β€” more points kept or discarded

Step 5: result β€” only the essential vertices remain (blue)

The topology problem

  • st_simplify() processes each feature independently
  • Shared boundaries are simplified separately β†’ gaps and overlaps appear

Topology-preserving simplification

  • rmapshaper::ms_simplify() simplifies the whole layer at once β€” shared boundaries stay consistent
  • Uses the Visvalingam algorithm: iteratively removes the vertex that creates the smallest triangle area
library(rmapshaper)
# We specify the proportion of points to keep
n_ms <- ms_simplify(neighbors, keep = 0.5)

Summary

  • st_centroid() returns the geometric centre of mass β€” useful for distance calculations and analytics, but it can fall outside the shape for concave or C-shaped geometries. Use st_point_on_surface() when you need a point guaranteed to lie inside.

  • Affine transformations (shift, scale, rotate) preserve shape and angles. All three follow the same pattern in sf: shift to the origin, apply the transformation, shift back. sf lets you do this with ordinary arithmetic directly on sfc objects. Transformations are not commutative β€” order matters.

  • st_simplify() reduces vertex count using the Douglas-Peucker algorithm: it recursively removes vertices that deviate less than tolerance Ξ΅ from the local baseline. It operates feature by feature, so shared boundaries between adjacent polygons can develop gaps and overlaps.

  • ms_simplify() (rmapshaper) uses the Visvalingam algorithm and processes the entire layer at once, keeping shared edges consistent. Prefer it whenever topology matters.