Assessing and planning infrastructure and service networks, given a dispersed demand, limited capacity, accessibility targets, and concerns about spatial justice, is a central policy challenge. Problems of this type are commonly referred to as Maximal Coverage Location-Allocation (MCLA) spatial optimization problems.
locationallocation is an R package that provides tools for solving
MCLA problems with geospatial data. It builds on widely used spatial
libraries in R, follows tidyverse
principles,
and integrates seamlessly with the broader tidyverse
ecosystem. The package can generate travel-time
maps and optimize the placement of facilities or infrastructure
according to accessibility criteria, which can be weighted by one or
more variables or by a user-defined function.
Potential applications of the package extend to the domains of public infrastructure assessment and planning (public services provision, e.g. transport, social services, healthcare, parks), urban environmental and climate risk reduction interventions, logistics and hubs allocation, commercial and strategic decisions.
If you find this project useful, please consider giving it a star!
You can install locationallocation using the
remotes package:
# install.packages(remotes)
remotes::install_github("giacfalk/locationallocation")A CRAN version of the package is planned for the near future.
To use the package, start by loading it to your R session using the
library function:
library(locationallocation)As an example, we demonstrate how the package can address urban-scale climate risk through infrastructure assessment and geospatial planning. For this demonstration, we use the demo datasets included with the package.
These datasets include the coordinates of public drinking water
fountains (blue dots) in Naples, Italy
(naples_fountains);
a gridded population raster from the Global Human Settlement Layer
(GHSL) Population
Grid
(GHS-POP)
(naples_population);
a 100-meter resolution heat hazard map, representing the number of days
with Wet-Bulb Globe
Temperature
above 25°C during 2008–2017, obtained from the
UrbClim model
(naples_hot_days);
and the city’s administrative boundaries
(naples_shape).
We can use the
traveltime()
function to create a map of current accessibility to the facility points
(represented by the sf object
naples_fountains)
within the specified geographical boundaries. The function allows the
user to select a travel mode (walking or fastest route) and an output
spatial resolution in meters, achieved through dissevering spatial
downscaling techniques.
traveltime_data <-
naples_fountains |>
traveltime(
bb_area = naples_shape,
dowscaling_model_type = "lm",
mode = "walk",
res_output = 100
)Once
traveltime()
has completed, the resulting layer can be visualized using the
traveltime_plot()
function.
traveltime_data |>
traveltime_plot(
bb_area = naples_shape,
facilities = naples_fountains,
contour_traveltime = 15
)We can also generate a summary plot and compute statistics using the
output of the
traveltime()
function, together with a demand raster (e.g., population density) and a
specified time threshold, using the
traveltime_stats
function:
traveltime_data |>
traveltime_stats(
demand = naples_population,
breaks = c(5, 10, 15, 30),
objectiveminutes = 15
)
#> ℹ 85.45148% of coverage within the 15 minutes threshold.We can now use the
allocation()
function to optimize the placement of new water fountains, ensuring that
(virtually) everyone (i.e., the full extent of the raster layer
specified by the demand parameter) can reach one within 15 minutes, as
defined by the objectiveminutes parameter:
allocation_data <-
naples_population |>
allocation(
bb_area = naples_shape,
facilities = naples_fountains,
traveltime = traveltime_data,
weights = NULL,
objectiveminutes = 15,
objectiveshare = 0.99,
heur = "max",
approach = "norm",
exp_demand = 1,
exp_weights = 1
)#> ✔ Target coverage share of 99% attained with 28 facilities within the 15
#> minutes threshold. The achieved coverage share is 99.03953%.
allocation_data |> allocation_plot(naples_shape)Note that it is also possible to solve an allocation problem using the
weights parameter, which assigns greater relative importance or
priority to areas where demand overlaps with weighting factors defined
by another raster layer, such as exposure to hot days, as shown in the
following example:
allocation_data <-
naples_population |>
allocation(
bb_area = naples_shape,
facilities = naples_fountains,
traveltime = traveltime_data,
weights = naples_hot_days, # <--- Changed
objectiveminutes = 15,
objectiveshare = 0.99,
heur = "max",
approach = "norm",
exp_demand = 1,
exp_weights = 1
)#> ✔ Target coverage share of 99% attained with 25 facilities within the 15
#> minutes threshold. The achieved coverage share is 99.16903%.
allocation_data |> allocation_plot(naples_shape)It is also possible to apply normalization and exponentiation to
different demand and weighting layers, enhancing their relative
influence on the allocation, using the approach, exp_demand, and
exp_weights parameters (see the function
documentation
for details):
allocation_data <-
naples_population |>
allocation(
bb_area = naples_shape,
facilities = naples_fountains,
traveltime = traveltime_data,
weights = naples_hot_days,
objectiveminutes = 15,
objectiveshare = 0.99,
heur = "max",
approach = "norm",
exp_demand = 2, # <--- Changed
exp_weights = 1
)#> ✔ Target coverage share of 99% attained with 9 facilities within the 15
#> minutes threshold. The achieved coverage share is 99.15597%.
allocation_data |> allocation_plot(naples_shape)A variant of the allocation problem arises when the set of candidate locations for new facilities is discrete, rather than continuous across the study area as in the previous example.
In this case, the user must provide a set of candidate points via the
candidate parameter of the
allocation_discrete()
function, along with the maximum number of facilities that can be
selected (n_fac parameter). The function applies a quasi-optimality
heuristic based on a randomization approach, where the number of
replications (n_samples parameter) gradually approaches the global
optimum, although computational time increases linearly. As with the
continuous allocation problem, a weight layer and normalization and
exponentiation parameters can also be specified.
library(sf)
allocation_data <-
naples_population |>
allocation_discrete(
bb_area = naples_shape,
candidate = naples_shape |> st_sample(20),
facilities = naples_fountains,
n_fac = 5,
n_samples = 100,
traveltime = traveltime_data,
weights = NULL,
objectiveminutes = 15,
objectiveshare = NULL,
approach = "norm",
exp_demand = 1,
exp_weights = 1,
par = FALSE
)#> ℹ 5 facilities allocated within the 15 minutes threshold. The maximum
#> coverage share attained was 88.76062%.
allocation_data |> allocation_plot(naples_shape)Now consider a scenario where the user wants to choose up to n_fac
facilities to meet a target objectiveshare of the total demand,
assuming that level of coverage is actually attainable.
library(sf)
allocation_data <-
naples_population |>
allocation_discrete(
bb_area = naples_shape,
candidate = naples_shape |> st_sample(20),
facilities = naples_fountains,
n_fac = 5,
n_samples = 100,
traveltime = traveltime_data,
weights = NULL,
objectiveminutes = 15,
objectiveshare = 0.9,
approach = "norm",
exp_demand = 1,
exp_weights = 1,
par = FALSE
)#> ✔ Target coverage share of 90% attained with 2 facilities within the 15
#> minutes threshold. The achieved coverage share is 90.20134%.
allocation_data |> allocation_plot(naples_shape)Finally, consider a case with no preexisting facilities. The discrete location-allocation problem must then identify where to place new facilities to cover as much demand as possible, given the limited set of spatial options and the cap on how many facilities can be allocated.
library(sf)
allocation_data <-
naples_population |>
allocation_discrete(
bb_area = naples_shape,
candidate = naples_shape |> st_sample(20),
facilities = NULL,
n_fac = 5,
n_samples = 100,
traveltime = NULL,
weights = NULL,
objectiveminutes = 15,
objectiveshare = 0.45,
approach = "norm",
exp_demand = 1,
exp_weights = 1,
par = FALSE
)#> ✔ Target coverage share of 45% attained with 2 facilities within the 15
#> minutes threshold. The achieved coverage share is 46.40227%.
allocation_data |> allocation_plot(naples_shape)If you use this package in your research, please cite it to acknowledge the effort put into its development and maintenance. Your citation helps support its continued improvement.
citation("locationallocation")
#> To cite locationallocation in publications use:
#>
#> Falchetta, G. (2025). locationallocation: Solving Maximal Coverage
#> Location-Allocation geospatial infrastructure assessment and
#> planning problems [Preprint, manuscript submitted for
#> publication]. EarthArXiv. https://doi.org/10.31223/X5XQ69
#>
#> A BibTeX entry for LaTeX users is
#>
#> @Article{falchetta2025,
#> title = {locationallocation: Solving Maximal Coverage Location-Allocation geospatial infrastructure assessment and planning problems},
#> author = {Giacomo Falchetta},
#> year = {2025},
#> journal = {EarthArXiv},
#> doi = {10.31223/X5XQ69},
#> langid = {en},
#> note = {Preprint, manuscript submitted for publication},
#> }Copyright (C) 2025 Giacomo Falchetta
locationallocation is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
Contributions are welcome! Whether you want to report bugs, suggest features, or improve the code or documentation, your input is highly valued. Please check the issues tab for existing issues or to open a new one.









