% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/add_spatial_relations.R
\name{add_spatial_boundary}
\alias{add_spatial_boundary}
\title{Add spatial boundary-length relations}
\usage{
add_spatial_boundary(
  x,
  boundary = NULL,
  geometry = NULL,
  name = "boundary",
  weight_col = NULL,
  weight_multiplier = 1,
  include_self = TRUE,
  edge_factor = 1
)
}
\arguments{
\item{x}{A \code{Problem} object.}

\item{boundary}{Optional \code{data.frame} describing boundary lengths.
Accepted formats are:
\itemize{
\item \code{(id1, id2, boundary)}, or
\item \code{(pu1, pu2, weight)}.
}}

\item{geometry}{Optional \code{sf} object with planning-unit polygons and an
\code{id} column. If \code{NULL}, \code{x$data$pu_sf} is used.}

\item{name}{Character string giving the key under which the relation is
stored.}

\item{weight_col}{Optional character string giving the name of the weight
column in \code{boundary}. If \code{NULL}, the function tries to infer it
from \code{"boundary"} or \code{"weight"}.}

\item{weight_multiplier}{Positive numeric scalar applied to all boundary
weights.}

\item{include_self}{Logical. If \code{TRUE}, include diagonal entries
representing exposed boundary.}

\item{edge_factor}{Numeric scalar greater than or equal to zero. Multiplier
applied to exposed boundary when constructing diagonal entries.}
}
\value{
An updated \code{Problem} object with the stored relation in
\code{x$data$spatial_relations[[name]]}.
}
\description{
Build and register a boundary-length spatial relation between planning units.

Boundary relations represent shared edge length between adjacent polygons. In
contrast to queen adjacency, they only account for boundary segments of
positive length and ignore point-only contacts.
}
\details{
Use this function when spatial structure should be represented through shared
boundary length rather than binary contiguity or coordinate-based proximity.

Two input modes are supported:
\enumerate{
\item \strong{Boundary-table mode.} If \code{boundary} is supplied, it is
interpreted as a boundary table, for example a Marxan-style
\code{bound.dat}.

\item \strong{Geometry mode.} If \code{boundary = NULL}, boundary lengths
are derived from polygon geometry using \code{geometry} or
\code{x$data$pu_sf}.
}

Let \eqn{\omega_{ij} \ge 0} denote the shared boundary length between
planning units \eqn{i} and \eqn{j}, multiplied by
\code{weight_multiplier}.

For off-diagonal entries \eqn{i \neq j}, the stored weight is:
\deqn{
\omega_{ij} = \mathrm{\gamma} \times b_{ij},
}
where \eqn{b_{ij}} is the shared boundary length and \eqn{\gamma} is
the user-supplied \code{weight_multiplier}.

If \code{include_self = TRUE}, diagonal entries are also created. These are
not geometric self-neighbours in the graph sense; instead, they represent the
effective boundary exposed to the outside of the solution.

Let \eqn{p_i} be the total perimeter of planning unit \eqn{i}, and let
\eqn{\sum_{j \neq i} \omega_{ij}} be the total incident shared boundary
recorded for that planning unit. Then the exposed boundary is represented by
a diagonal term derived from the difference between total perimeter and shared
boundary, scaled by \code{edge_factor}.

These diagonal terms are useful in boundary-based compactness or
fragmentation objectives, because they encode the portion of each planning
unit's perimeter that would remain exposed if the unit were selected.

\strong{Boundary-table mode}

If \code{boundary} is provided, accepted formats are:
\itemize{
\item \code{(id1, id2, boundary)}, or
\item \code{(pu1, pu2, weight)}.
}

If the table contains diagonal rows \eqn{(i,i)}, these are interpreted as
total perimeter values in boundary-table mode.

\strong{Geometry mode}

If \code{boundary = NULL}, shared boundary lengths are derived directly from
polygon geometry. Only positive-length intersections are retained. Point
touches are ignored.

\strong{Storage}

The final relation is stored through \code{\link{add_spatial_relations}},
typically as an undirected relation with optional diagonal entries.
}
\examples{
pu_tbl <- data.frame(
  id = 1:4,
  cost = c(1, 2, 3, 4)
)

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)
)

bound_df <- data.frame(
  id1 = c(1, 1, 2, 1, 2, 3, 4),
  id2 = c(1, 2, 2, 3, 4, 4, 4),
  boundary = c(4, 1, 4, 1, 1, 1, 4)
)

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

p <- add_spatial_boundary(
  x = p,
  boundary = bound_df,
  name = "boundary",
  include_self = TRUE,
  edge_factor = 1
)

p$data$spatial_relations$boundary

}
\seealso{
\code{\link{add_spatial_relations}},
\code{\link{add_objective_min_fragmentation_pu}},
\code{\link{add_objective_min_fragmentation_action}}
}
