% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/constrained_accessibility.R
\name{constrained_accessibility}
\alias{constrained_accessibility}
\title{Constrained accessibility}
\usage{
constrained_accessibility(
  travel_matrix,
  land_use_data,
  travel_cost,
  demand = NULL,
  supply = NULL,
  constraint,
  decay_function,
  active = NULL,
  error_threshold = 0.001,
  improvement_threshold = 1e-06,
  max_iterations = 1000,
  group_by = character(0),
  fill_missing_ids = TRUE,
  detailed_results = FALSE
)
}
\arguments{
\item{travel_matrix}{A data frame. The travel matrix describing the costs
(i.e. travel time, distance, monetary cost, etc.) between the origins and
destinations in the study area. Must contain the columns \code{from_id}, \code{to_id}
and any others specified in \code{travel_cost}.}

\item{land_use_data}{A data frame. The distribution of opportunities within
the study area cells. Must contain the columns \code{id} and any others
specified in \code{opportunity}.}

\item{travel_cost}{A string. The name of the column in \code{travel_matrix} with
the travel cost between origins and destinations. The notion of cost
here is generic over any kind of numeric travel cost, such as distance,
time and money.}

\item{demand}{A string. The name of the column in \code{land_use_data} with the
number of opportunity-demanders at each origin (e.g., people) that will be
considered.}

\item{supply}{A string. The name of the column in \code{land_use_data} with the
number of opportunity supply at each destination (e.g., jobs, school-seats)
that will be considered.}

\item{constraint}{A string. One of \code{"total"}, \code{"singly"}, or \code{"doubly"}. See
Details section for more information.}

\item{decay_function}{A \code{fuction} that converts travel cost into an
impedance factor used to weight opportunities. This function should take a
\code{numeric} vector and also return a \code{numeric} vector as output, with the
same length as the input. For convenience, the package currently includes
the following functions: \code{\link[=decay_binary]{decay_binary()}}, \code{\link[=decay_exponential]{decay_exponential()}},
\code{\link[=decay_power]{decay_power()}} and \code{\link[=decay_stepped]{decay_stepped()}}. See the documentation of each decay
function for more details.}

\item{active}{A logical. When \code{TRUE}, the function calculates active
accessibility (the quantity of opportunities that can be reached from
a given origin). When \code{FALSE}, it calculates passive accessibility (by
how many people each destination can be reached), which is equivalent
to the notion of market potential. This parameter only works for
\code{constraint} types \code{"total"} and \code{"singly"}. Ignored for
\code{constraint = "doubly"}.}

\item{error_threshold}{Numeric. Convergence criterion used only for
calibration in the doubly-constrained case (\code{constraint = "doubly"}).}

\item{improvement_threshold}{Numeric. Convergence criterion for improvement
used only for calibration in the doubly-constrained case
(\code{constraint = "doubly"}).}

\item{max_iterations}{Integer. Maximum iterations used only for calibration
in the doubly-constrained case (\code{constraint = "doubly"}).}

\item{group_by}{A \code{character} vector. When not \code{character(0)} (the default),
indicates the \code{travel_matrix} columns that should be used to group the
accessibility estimates by. For example, if \code{travel_matrix} includes a
departure time column, that specifies the departure time of each entry in
the data frame, passing \code{"departure_time"} to this parameter results in
accessibility estimates grouped by origin and by departure time.}

\item{fill_missing_ids}{A \code{logical}. When calculating grouped accessibility
estimates (i.e. when \code{by_col} is not \code{NULL}), some combinations of groups
and origins may be missing. For example, if a single trip can depart from
origin \code{A} at 7:15am and reach destination \code{B} within 55 minutes, but no
trips departing from \code{A} at 7:30am can be completed at all, this second
combination will not be included in the output. When \code{TRUE} (the default),
the function identifies which combinations would be left out and fills
their respective accessibility values with 0, which incurs in a
performance penalty.}

\item{detailed_results}{Logical. Whether to return detailed OD-level results.}
}
\description{
Calculates accessibility using constraints, as proposed in
\insertCite{soukhov2025family;textual}{accessibility}. Accessibility is
conceptualised as potential spatial interaction. This function covers three
constraint cases. Please see the Details section for more information.
}
\section{Details}{

This function covers the family of constrained accessibility measures
proposed in \insertCite{soukhov2025family;textual}{accessibility}.
\subsection{Total Constrained Accessibility}{

Allocates the system-wide total proportionally based on travel impedance
between origins and destinations. This measure uses the logic of a
total ~(or 'unconstrained' by Wilson's terms)~ constraint.

Use this measure when the total quantity of \strong{supply} OR \strong{demand} in the
system is known and representing accessibility as a proportion of this total
is meaningful.
\subsection{Requirement:}{
\itemize{
\item Either \code{demand} or \code{supply} must be provided (cannot provide both).
}
}

\subsection{Interpretation:}{
\itemize{
\item \code{active = TRUE} (\emph{active accessibility}): Results represent the total
number of  \strong{opportunities} (supply) accessible from each origin based on
region-relative travel impedance. The units are in 'supply' (e.g., jobs,
school seats).
\itemize{
\item If \code{detailed_results = FALSE}, outputs are aggregated and returned by
origin.
\item If \code{detailed_results = TRUE}, OD-level flows are returned. Summing flows
by origin equals the aggregated result.
}
\item \code{active = FALSE} (\emph{passive accessibility}, the notion of market potential):
Results represent the total number of \strong{population} (demand) that can reach
each destination based on region-relative travel impedance. The units are in
'demand' (e.g., population).
\itemize{
\item If \code{detailed_results = FALSE}, outputs are aggregated by destination.
\item If \code{detailed_results = TRUE}, OD-level flows are returned. Summing flows
by destination equals the aggregated result.
}
}
}

\subsection{Use cases:}{
\itemize{
\item Active accessibility (aggregated):
"How many jobs can be reached from origin zone A given its region-relative
travel impedance?"
\item Active accessibility (flow-level):
"How many jobs can be reached by flow A->1 given A->1's region-relative
travel impedance?"
\item Passive accessibility (aggregated):
"How many people can reach destination zone 1 given its region-relative
travel impedance?"
\item Passive accessibility (flow-level):
"How many people are reached by flow 1->A given 1->A's region-relative
travel impedance?"
}
}

}

\subsection{Singly Constrained Accessibility}{

Allocates opportunities at each destination (or population at each origin)
proportionally based on travel impedance and the opposite marginal. This
measure uses the logic of single constraint from
\insertCite{wilson1971family;textual}{accessibility}.

Use this measure when modeling \strong{competition}, where both demand and supply
are conceptualised to influence accessibility but only one side is fixed.
The measure distributes flows so that totals match the constrained side
while weighting by travel impedance and the unconstrained side.
\subsection{Requirements:}{
\itemize{
\item Both \code{demand} and \code{supply} must be provided (the logical for \code{active}
determines if either demand or supply is constrained).
}
}

\subsection{Interpretation:}{
\itemize{
\item \code{active = TRUE} (\emph{active accessibility}): constrains supply. Results
represent the total number of \strong{opportunities} (supply) accessible from each
origin based on region-relative travel impedance and population at the origin.
The units are in 'supply' (e.g., jobs, school seats).
\itemize{
\item If \code{detailed_results = FALSE}, outputs are aggregated and returned by origin.
\item If \code{detailed_results = TRUE}, OD-level flows are returned. Summing flows
by origin equals the aggregated result.
}
\item \code{active = FALSE} (\emph{passive accessibility}, the notion of market potential):
constrains demand. Results represent the total number of
\strong{population} (demand) that can reach each destination based on
region-relative travel impedance and opportunities at the destination. The
units are in 'demand' (e.g., population).
\itemize{
\item If \code{detailed_results = FALSE}, outputs are aggregated by destination.
\item If \code{detailed_results = TRUE}, OD-level flows are returned. Summing flows
by destination equals the aggregated result.
}
}
}

\subsection{Use cases:}{
\itemize{
\item Active accessibility (aggregated):
"How many jobs can be reached from origin zone A given its region-relative
travel impedance and demand?"
\item Active accessibility (flow-level):
"How many jobs can be reached by flow A->1 given A->1's region-relative
travel impedance and demand?"
\item Passive accessibility (aggregated):
"How many people can reach destination zone 1 given its region-relative
travel impedance and supply?"
\item Passive accessibility (flow-level):
"How many people are reached by flow 1->A given 1->A's region-relative
travel impedance and supply?"
}

\strong{NOTE:} the active form of this measure yields equivalent results to the
\code{spatial_availability()} function, through different logic.
}

}

\subsection{Doubly Constrained Accessibility}{

Allocates flows so supply at each destination matches demand at each origin.
It uses Wilson's \emph{doubly-constrained} gravity model \insertCite{wilson1971family;textual}{accessibility}.

The model uses iterative proportional fitting to update balancing factors in
order to calibrate OD flows on both margins (\code{A_i} for origins and \code{B_j} for
destinations) until convergence (i.e. the sum of \code{demand} and \code{supply} match).
This guarantees that flows satisfy both marginals while being weighted by
travel  impedance.
\subsection{Requirements:}{
\itemize{
\item Both \code{demand} and \code{supply} must be provided.
\item Unlike \code{total} and \code{singly}, \code{doubly} requires the sum of demand and supply
to match; otherwise, the model will not converge.
\item \code{active} must be \code{NULL}. Since supply must match demand, their units are
the same and there is no distinction between 'active' and 'passive' notions.
\item Only accepts\code{detailed_results = TRUE}.
}
}

\subsection{Interpretation:}{
\itemize{
\item Results include OD-level flows (\code{flow}) along with balancing factors (\code{A_i},
\code{B_j}) and travel impedance weights. The resulting flows represent the
distribution of demand and supply across all origin-destination pairs. NOTE:
OD flows are in flow units (jointly determined by demand and supply).
}
}

\subsection{Use cases:}{
\itemize{
\item flow-level:
"What is the count of A->1 flows given A->1's region-relative travel impedance,
demand and supply?"
}
}

}
}

\examples{
# Load demo data shipped with the package
data_dir <- system.file("extdata", package = "accessibility")
travel_matrix <- readRDS(file.path(data_dir, "travel_matrix.rds"))
land_use_data <- readRDS(file.path(data_dir, "land_use_data.rds"))

# Total-constrained (active accessibility, aggregated): returns units of
# accessible supply by origin (requires supply)
constrained_accessibility(
  travel_matrix   = travel_matrix,
  land_use_data   = land_use_data,
  travel_cost     = "travel_time",
  constraint      = "total",
  decay_function  = decay_exponential(0.1),
  demand          = NULL,
  supply          = "jobs",
  active          = TRUE,
  detailed_results = FALSE
)

# Total-constrained (passive accessibility, aggregated): returns units of
# accessible demand by destination  (requires demand)
constrained_accessibility(
  travel_matrix  = travel_matrix,
  land_use_data  = land_use_data,
  travel_cost    = "travel_time",
  constraint     = "total",
  decay_function = decay_exponential(0.1),
  demand         = "population",
  supply         = NULL,
  active         = FALSE,
  detailed_results = FALSE
)

# Singly-constrained (active accessibility, aggregated): returns units of
# accessible supply by origin (requires supply and demand)
constrained_accessibility(
  travel_matrix   = travel_matrix,
  land_use_data   = land_use_data,
  travel_cost     = "travel_time",
  constraint      = "singly",
  decay_function  = decay_exponential(0.1),
  demand          = "population",
  supply          = "jobs",
  active          = TRUE,
  detailed_results = FALSE
)

# Doubly-constrained: returns units of flow (requires both demand and supply
# (totals that match) and `detailed_results = TRUE`)

# Using a small toy dataset with matching totals.
tm_small <- data.table::data.table(
  expand.grid(from_id = c("1","2","3"), to_id = c("1","2","3"))
)
tm_small[, travel_time := c(10, 30, 15, 30, 10, 25, 15, 25, 10)]
lu_small <- data.table::data.table(
  id         = c("1","2","3"),
  population = c(4, 10, 6),   # sum = 20
  jobs       = c(7,  5,  8)   # sum = 20
)

constrained_accessibility(
  travel_matrix   = tm_small,
  land_use_data   = lu_small,
  travel_cost     = "travel_time",
  constraint      = "doubly",
  decay_function  = decay_exponential(0.1),
  demand          = "population",
  supply          = "jobs",
  detailed_results = TRUE
)

}
\references{
\insertAllCited{}
}
\seealso{
Other Constrained accessibility: 
\code{\link{spatial_availability}()}
}
\concept{Constrained accessibility}
