Skip to contents

Build and register a k-nearest-neighbours graph between planning units using coordinates.

This constructor does not require polygon geometry. It uses planning-unit coordinates supplied explicitly or stored in the Problem object.

Usage

add_spatial_knn(
  x,
  coords = NULL,
  k = 8,
  name = "knn",
  weight_mode = c("constant", "inverse", "inverse_sq"),
  distance_eps = 1e-09
)

Arguments

x

A Problem object created with create_problem.

coords

Optional coordinates specification. This may be:

  • a data.frame(id, x, y), or

  • a numeric matrix with two columns (x, y) aligned to the order of planning units.

If NULL, coordinates are taken from x$data$pu_coords or from columns x$data$pu$x and x$data$pu$y.

k

Integer giving the number of neighbours per planning unit. Must be at least 1 and strictly less than the number of planning units.

name

Character string giving the key under which the relation is stored.

weight_mode

Character string indicating how distance is converted to weight. Must be one of "constant", "inverse", or "inverse_sq".

distance_eps

Small positive numeric constant used to avoid division by zero in inverse-distance weighting.

Value

An updated Problem object.

Details

Use this function when neighbourhood should be defined by a fixed number of nearby planning units rather than by polygon topology or a fixed distance threshold.

Let \(s_i = (x_i, y_i)\) denote the coordinates of planning unit \(i\). For each planning unit, this function identifies the k nearest distinct planning units under Euclidean distance.

If \(d_{ij}\) denotes the Euclidean distance between units \(i\) and \(j\), then the k-nearest-neighbours relation is constructed by adding an edge from \(i\) to each of its k nearest neighbours.

Edge weights are then assigned according to weight_mode:

  • "constant": $$\omega_{ij} = 1,$$

  • "inverse": $$\omega_{ij} = \frac{1}{\max(d_{ij}, \varepsilon)},$$

  • "inverse_sq": $$\omega_{ij} = \frac{1}{\max(d_{ij}, \varepsilon)^2},$$

where \(\varepsilon\) = distance_eps is a small constant to avoid division by zero.

The raw k-nearest-neighbours structure is directional by construction, but the stored relation is registered as undirected by default through add_spatial_relations, which collapses duplicate unordered pairs.

If the RANN package is available, it is used for efficient nearest neighbour search. Otherwise, a full distance matrix is computed.

Examples

pu_tbl <- data.frame(
  id = 1:4,
  cost = c(1, 2, 3, 4),
  x = c(0, 1, 0, 1),
  y = c(0, 0, 1, 1)
)

feat_tbl <- data.frame(
  id = 1:2,
  name = c("feature_1", "feature_2")
)

dist_feat_tbl <- data.frame(
  pu = c(1, 1, 2, 3, 4),
  feature = c(1, 2, 2, 1, 2),
  amount = c(5, 2, 3, 4, 1)
)

p <- create_problem(
  pu = pu_tbl,
  features = feat_tbl,
  dist_features = dist_feat_tbl,
  cost = "cost"
)

p <- add_spatial_knn(
  x = p,
  k = 2,
  name = "knn2",
  weight_mode = "constant"
)

p$data$spatial_relations$knn2
#>   internal_pu1 internal_pu2 weight pu1 pu2 distance       source relation_name
#> 2            1            2      1   1   2        1 knn_constant          knn2
#> 1            1            3      1   1   3        1 knn_constant          knn2
#> 3            2            4      1   2   4        1 knn_constant          knn2
#> 6            3            4      1   3   4        1 knn_constant          knn2