Figure 5.1 The 9-intersection model of topological relations

figures
code
R

This page attempts to show the various spatial predicates of the 9-intersection model presented in Figure 5.1 in code.

First we make some shapes.

Code
library(sf)
library(dplyr)
library(ggplot2)
library(ggrepel)
library(stringr)

# a circle
p1 <- st_point(c(-2, 0)) |> st_buffer(1, nQuadSegs = 90)
# an identical circle
p2 <- p1
# same circle shifted to the right so it is touching
p3 <- p1 + c(2, 0)
# shifted circle scaled to half size
p4 <- p3 * matrix(c(0.5, 0, 0, 0.5), 2, 2)
# and shifted down so it's touching p3 and overlapping p4 
p5 <- p4 + c(0, -0.5)
# a donut to the side
p6 <- st_difference(p3, p4) + c(2.1, 0)
# and a circle overlapping the donut
p7 <- p4 * matrix(c(1.5, 0, 0, 1.5), 2, 2) + c(2, -1)

polys <- st_sfc(list(p1, p2, p3, p4, p5, p6, p7)) |> 
  st_sf() |>
  mutate(ID = as.factor(1:7), 
         dx = c(0, 0, .5, 0, 0, 0, 0), 
         dy = c(-1, 1, 2, 0, 0, 0, 0))

Map and label these for reference.

Code
xy <- polys |> st_centroid() |>
  st_coordinates() |>
  as.data.frame() |>
  mutate(ID = polys$ID)

ggplot(polys) +
  geom_sf(aes(fill = ID), colour = "white", alpha = 0.5) +
  scale_fill_manual(
    values = c("red", "dodgerblue3", "darkgreen",
               "black", "yellow", "violet", "brown")) +
  geom_label_repel(data = xy, aes(x = X, y = Y, label = ID)) +
  theme_void()

Now we can run various tests against the spatial predicates in turn. For example st_disjoint tells us the following.

Code
polys |> st_disjoint()
Sparse geometry binary predicate list of length 7, where the predicate
was `disjoint'
 1: 3, 4, 5, 6, 7
 2: 3, 4, 5, 6, 7
 3: 1, 2, 6, 7
 4: 1, 2, 6, 7
 5: 1, 2, 6, 7
 6: 1, 2, 3, 4, 5
 7: 1, 2, 3, 4, 5

We can tabulate the result of applying all the spatial predicates to these polygons into a table.

The details of how this is done don’t matter greatly, but if you are interested click into the code below. The core of it is based on the various st_* functions such as st_intersects or st_contains. These correspond, more or less to the relations identified in Figure 5.1. The spatial predicates available in the sf package in R correspond to those implemented in spatial databases such as PostGIS.

Code
get_spatial_query_as_vector <- function(data, predicate) {
  data |> 
    predicate() |>
    lapply(str_c, collapse = " ") |>
    unlist()
}

de9im <- data.frame(
  ID = polys$ID,
  disjoint          = get_spatial_query_as_vector(polys, st_disjoint),
  touches           = get_spatial_query_as_vector(polys, st_touches),
  equals            = get_spatial_query_as_vector(polys, st_equals),
  intersects        = get_spatial_query_as_vector(polys, st_intersects),
  contains_properly = get_spatial_query_as_vector(polys, st_contains_properly),
  contains          = get_spatial_query_as_vector(polys, st_contains),
  within            = get_spatial_query_as_vector(polys, st_within),
  covers            = get_spatial_query_as_vector(polys, st_covers),
  covered_by        = get_spatial_query_as_vector(polys, st_covered_by)
)

knitr::kable(de9im)
ID disjoint touches equals intersects contains_properly contains within covers covered_by
1 3 4 5 6 7 1 2 1 2 1 2 1 2 1 2 1 2
2 3 4 5 6 7 1 2 1 2 1 2 1 2 1 2 1 2
3 1 2 6 7 3 3 4 5 4 3 4 5 3 3 4 5 3
4 1 2 6 7 4 3 4 5 4 3 4 4 3 4
5 1 2 6 7 5 3 4 5 5 3 5 5 3 5
6 1 2 3 4 5 6 6 7 6 6 6 6
7 1 2 3 4 5 7 6 7 7 7 7 7
Code
# License (MIT)
#
# Copyright (c) 2023 David O'Sullivan
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation
# files (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to  permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

© 2023-24 David O’Sullivan