Management Zones

Introduction

One of the main aims in conservation planning is to identify the most cost-effective set of areas to manage biodiversity (Margules & Pressey 2000). To achieve this, prioritizations are generally created to identify areas for expanding protected area systems. However, many real-world conservation problems do not simply involve deciding if an area should be protected or not (e.g. Klein et al. 2009; Stigner et al. 2016). Instead, many real-world problems involve a range of different management categories and the goal is to determine which areas should be allocated to which management category. For example, a manager might have a range of different methods (e.g. baiting or trapping at various intensities) for controlling invasive pests in a set of different areas (e.g. Cattarino et al. 2018). They would need a prioritization that shows which control methods should be implemented in which areas. In this particular case, a binary prioritization showing which areas contain the most biodiversity is simply not helpful. Furthermore, many real-world problems require decisions that meet multiple, and sometimes conflicting, objectives from different stakeholders. For example, a manager might need to implement a set of no-take and partial-take areas to prevent overfishing, but also ensure that there still remain plenty of areas for fishing activities (e.g. Wilson et al. 2010; Klein et al. 2013). Popularized by Marxan with Zones (Watts et al. 2009), this concept has become known as “zones” and is becoming increasingly important in conservation planning.

The aim of this vignette is to showcase the zones functionality of the prioritizr R package. It will assume a certain level of familiarity with conservation planning terminology and the prioritizr R package. We recommend reading the prioritizr vignette first if you don’t have much experience in either of these topics.

Usage

Simple minimum set problem

In the prioritizr R package, all conservation planning problems—including those which contain multiple management zones or actions—are initialized using the problem function. To refresh our memory on how we can construct problems using the prioritizr R package, let us quickly construct a simple conservation planning problem. This problem will use the simulated built-in planning unit and feature data distributed with the package. It will have a minimum set objective, targets which require that solution secure to 10 % of the habitat in the study area for each feature, and binary decision variables indicating that planning units are selected or not selected for protection.

# load prioritizr package
library(prioritizr)

# load data
data(sim_pu_raster, sim_features)

# create targets for each of the five features
t1 <- rep(0.1, 5)

# build single-zone problem
p1 <- problem(sim_pu_raster, sim_features) %>%
      add_min_set_objective() %>%
      add_relative_targets(t1) %>%
      add_binary_decisions()

# print problem
print(p1)
## Conservation Problem
##   planning units: RasterLayer (90 units)
##   cost:           min: 190.13276, max: 215.86384
##   features:       layer.1, layer.2, layer.3, ... (5 features)
##   objective:      Minimum set objective 
##   targets:        Relative targets [targets (min: 0.1, max: 0.1)]
##   decisions:      Binary decision 
##   constraints:    <none>
##   penalties:      <none>
##   portfolio:      default
##   solver:         default
# solve problem
s1 <- solve(p1)
## Gurobi Optimizer version 9.0.2 build v9.0.2rc0 (win64)
## Optimize a model with 5 rows, 90 columns and 450 nonzeros
## Model fingerprint: 0x6442bf6e
## Variable types: 0 continuous, 90 integer (90 binary)
## Coefficient statistics:
##   Matrix range     [2e-01, 9e-01]
##   Objective range  [2e+02, 2e+02]
##   Bounds range     [1e+00, 1e+00]
##   RHS range        [3e+00, 8e+00]
## Found heuristic solution: objective 2337.9617505
## Presolve time: 0.00s
## Presolved: 5 rows, 90 columns, 450 nonzeros
## Variable types: 0 continuous, 90 integer (90 binary)
## Presolved: 5 rows, 90 columns, 450 nonzeros
## 
## 
## Root relaxation: objective 1.931582e+03, 12 iterations, 0.00 seconds
## 
##     Nodes    |    Current Node    |     Objective Bounds      |     Work
##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
## 
##      0     0 1931.58191    0    4 2337.96175 1931.58191  17.4%     -    0s
## H    0     0                    1987.3985265 1931.58191  2.81%     -    0s
## 
## Explored 1 nodes (12 simplex iterations) in 0.00 seconds
## Thread count was 1 (of 48 available processors)
## 
## Solution count 2: 1987.4 2337.96 
## 
## Optimal solution found (tolerance 1.00e-01)
## Best objective 1.987398526526e+03, best bound 1.931581908865e+03, gap 2.8085%
# calculate feature representation
r1 <- feature_representation(p1, s1)
print(r1)
## # A tibble: 5 x 3
##   feature absolute_held relative_held
##   <chr>           <dbl>         <dbl>
## 1 layer.1          8.91         0.107
## 2 layer.2          3.13         0.100
## 3 layer.3          7.34         0.102
## 4 layer.4          4.35         0.102
## 5 layer.5          6.01         0.106
# plot solution
plot(s1, main = "solution", axes = FALSE, box = FALSE)

Adding management zones

Now let us imagine that instead of having a single management zone (e.g. protected area), we have two management zones. Similar to the example above, we require a solution that secures 10 % of the habitat in the study area for each feature in the first management zone. But we also require a solution that secures 5 % of the habitat in the study area for each feature in the second management zone. Each planning unit must be allocated to either zone or not selected for management at all. In this example, each planning unit costs the same when it is allocated to either of the two zones. We can formulate and solve this problem using the following code.

# create a matrix with the targets
# here each column corresponds to a different zone,
# each row corresponds to a different feature, and
# each cell value corresponds to the target
t2 <- matrix(NA, ncol = 2, nrow = 5)
t2[, 1] <- 0.1
t2[, 2] <- 0.05

# print targets
print(t2)
##      [,1] [,2]
## [1,]  0.1 0.05
## [2,]  0.1 0.05
## [3,]  0.1 0.05
## [4,]  0.1 0.05
## [5,]  0.1 0.05
# create a zones object that contains the amount of each feature
# in each planning unit when it is allocated to each zone
# since our zones pertain to the same habitat data, we will
# to specify the same habitat data for each zone
z2 <- zones("zone 1" = sim_features, "zone 2" = sim_features)

# print zones
print(z2)
## Zones
##   zones: zone 1, zone 2 (2 zones)
##   features: 1, 2, 3, ... (5 features)
##   data type: RasterStack
# create a raster stack with the planning unit data
# since our planning unit costs are the same for each zone,
# we will create a stack with two replicates of the cost data
pu2 <- stack(sim_pu_raster, sim_pu_raster)

# print stack
print(pu2)
## class      : RasterStack 
## dimensions : 10, 10, 100, 2  (nrow, ncol, ncell, nlayers)
## resolution : 0.1, 0.1  (x, y)
## extent     : 0, 1, 0, 1  (xmin, xmax, ymin, ymax)
## crs        : NA 
## names      :  layer.1,  layer.2 
## min values : 190.1328, 190.1328 
## max values : 215.8638, 215.8638
# build two-zone problem
p2 <- problem(pu2, z2) %>%
      add_min_set_objective() %>%
      add_relative_targets(t2) %>%
      add_binary_decisions()

# print problem
print(p2)
## Conservation Problem
##   zones:          zone 1, zone 2 (2 zones)
##   planning units: RasterStack (90 units)
##   cost:           min: 190.13276, max: 215.86384
##   features:       1, 2, 3, ... (5 features)
##   objective:      Minimum set objective 
##   targets:        Relative targets [targets (min: 0.05, max: 0.1)]
##   decisions:      Binary decision 
##   constraints:    <none>
##   penalties:      <none>
##   portfolio:      default
##   solver:         default
# solve problem
s2 <- solve(p2)
## Gurobi Optimizer version 9.0.2 build v9.0.2rc0 (win64)
## Optimize a model with 100 rows, 180 columns and 1080 nonzeros
## Model fingerprint: 0xbdab1647
## Variable types: 0 continuous, 180 integer (180 binary)
## Coefficient statistics:
##   Matrix range     [2e-01, 1e+00]
##   Objective range  [2e+02, 2e+02]
##   Bounds range     [1e+00, 1e+00]
##   RHS range        [1e+00, 8e+00]
## Found heuristic solution: objective 3568.4931394
## Presolve time: 0.00s
## Presolved: 100 rows, 180 columns, 1080 nonzeros
## Variable types: 0 continuous, 180 integer (180 binary)
## Presolved: 100 rows, 180 columns, 1080 nonzeros
## 
## 
## Root relaxation: objective 2.911333e+03, 73 iterations, 0.00 seconds
## 
##     Nodes    |    Current Node    |     Objective Bounds      |     Work
##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
## 
##      0     0 2911.33335    0    9 3568.49314 2911.33335  18.4%     -    0s
## H    0     0                    3174.6681958 2911.33335  8.29%     -    0s
## 
## Explored 1 nodes (73 simplex iterations) in 0.00 seconds
## Thread count was 1 (of 48 available processors)
## 
## Solution count 2: 3174.67 3568.49 
## 
## Optimal solution found (tolerance 1.00e-01)
## Best objective 3.174668195776e+03, best bound 2.911333350606e+03, gap 8.2949%
# calculate feature representation
r2 <- feature_representation(p2, s2)
print(r2)
## # A tibble: 10 x 4
##    feature zone   absolute_held relative_held
##    <chr>   <chr>          <dbl>         <dbl>
##  1 1       zone 1          8.93        0.107 
##  2 2       zone 1          3.13        0.100 
##  3 3       zone 1          7.44        0.103 
##  4 4       zone 1          4.27        0.100 
##  5 5       zone 1          5.97        0.105 
##  6 1       zone 2          5.15        0.0618
##  7 2       zone 2          1.84        0.0590
##  8 3       zone 2          4.08        0.0567
##  9 4       zone 2          2.52        0.0590
## 10 5       zone 2          3.67        0.0648
# plot solution
# here we use the category layer function to generate raster showing the zone
# that each planning unit was allocated. Specifically, pixels with the
# value 1 are allocated to "zone 1" and pixels with the value 2 are allocated
# to "zone 2". Thus units depicted in gray are not allocated
# to any zone, units depicted in yellow are allocated to zone 1, and units
# depicted in green are allocated to zone 2
plot(category_layer(s2), main = "solution", axes = FALSE, box = FALSE)

Multiple zones with varying costs

Real-world problems often have different costs for managing planning units under different zones. These problems also tend to have different expected amounts of each feature when planning units are managed differently. So let us consider a slightly more complex example. Similar to before we will have two management zones. But this time, the cost of managing each planning unit is different depending on which management zone it is assigned to in the solution. Furthermore, when we assign a planning unit to the second zone, we only expect to end up with half of the habitat we would get if we managed the unit in the first zone (e.g. because the second zone is a partial-take zone and the first zone is a no-take zone). We will use the same target data as in the previous example.

# create new planning unit and cost data
data(sim_pu_zones_stack)
pu3 <- sim_pu_zones_stack[[1:2]]

# plot cost data
plot(pu3, main = c("zone 1", "zone 2"), axes = FALSE, box = FALSE)

# create problem
p3 <- problem(pu3, zones(sim_features, sim_features * 0.5)) %>%
      add_min_set_objective() %>%
      add_relative_targets(t2) %>%
      add_binary_decisions()

# print problem
print(p3)
## Conservation Problem
##   zones:          1, 2 (2 zones)
##   planning units: RasterStack (90 units)
##   cost:           min: 182.60173, max: 221.36296
##   features:       1, 2, 3, ... (5 features)
##   objective:      Minimum set objective 
##   targets:        Relative targets [targets (min: 0.05, max: 0.1)]
##   decisions:      Binary decision 
##   constraints:    <none>
##   penalties:      <none>
##   portfolio:      default
##   solver:         default
# solve problem
s3 <- solve(p3)
## Gurobi Optimizer version 9.0.2 build v9.0.2rc0 (win64)
## Optimize a model with 100 rows, 180 columns and 1080 nonzeros
## Model fingerprint: 0x07455a48
## Variable types: 0 continuous, 180 integer (180 binary)
## Coefficient statistics:
##   Matrix range     [1e-01, 1e+00]
##   Objective range  [2e+02, 2e+02]
##   Bounds range     [1e+00, 1e+00]
##   RHS range        [8e-01, 8e+00]
## Found heuristic solution: objective 3667.7708672
## Presolve time: 0.00s
## Presolved: 100 rows, 180 columns, 1080 nonzeros
## Variable types: 0 continuous, 180 integer (180 binary)
## Presolved: 100 rows, 180 columns, 1080 nonzeros
## 
## 
## Root relaxation: objective 2.884872e+03, 38 iterations, 0.00 seconds
## 
##     Nodes    |    Current Node    |     Objective Bounds      |     Work
##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
## 
##      0     0 2884.87200    0    8 3667.77087 2884.87200  21.3%     -    0s
## H    0     0                    2958.1201508 2884.87200  2.48%     -    0s
## 
## Explored 1 nodes (38 simplex iterations) in 0.00 seconds
## Thread count was 1 (of 48 available processors)
## 
## Solution count 2: 2958.12 3667.77 
## 
## Optimal solution found (tolerance 1.00e-01)
## Best objective 2.958120150795e+03, best bound 2.884871999973e+03, gap 2.4762%
# calculate feature representation
r3 <- feature_representation(p3, s3)
print(r3)
## # A tibble: 10 x 4
##    feature zone  absolute_held relative_held
##    <chr>   <chr>         <dbl>         <dbl>
##  1 1       1             8.94         0.107 
##  2 2       1             3.13         0.100 
##  3 3       1             7.42         0.103 
##  4 4       1             4.29         0.101 
##  5 5       1             6.01         0.106 
##  6 1       2             2.19         0.0527
##  7 2       2             0.794        0.0509
##  8 3       2             1.87         0.0519
##  9 4       2             1.08         0.0505
## 10 5       2             1.44         0.0509
# plot solution
plot(category_layer(s3), main = "solution", axes = FALSE, box = FALSE)

Multiple zones with complex targets

So far, we have dealt with problems where each feature has a target that pertains to a single zone. But sometimes we have targets that pertain to multiple zones. For example, what if we were in charge of managing pest control in a set of areas and we had three different pest control methods we could implement in any given planning unit. We could (1) set up a few traps in a given planning unit and make 10 % of the habitat in the unit pest-free, (2) set up a lot of traps and make 50 % of the habitat in the unit pest-free, or (3) drop baits over a given planning unit and make 80 % of the planning unit pest-free. Each of these different actions has a different cost, with a few low intensity trapping costing $100 per planning unit, a high intensity trapping costing $300, and baiting costing $200 (please note these costs aren’t meant to be realistic). After defining our management actions and costs, we require a solution that will yield 8 units of pest free habitat per feature. It’s important to note that unlike the previous examples, here we don’t have targets for each feature in each zone, but rather our targets are for each feature and across multiple zones. In other words, we don’t really care which management actions we implement, we just want the set of actions that will meet our targets for minimum expenditure. We can formulate and solve this problem using the following code.

# create planning unit data with costs
pu4 <- stack(Which(!is.na(sim_pu_raster)) * 100,
             Which(!is.na(sim_pu_raster)) * 300,
             Which(!is.na(sim_pu_raster)) * 200)
names(pu4) <- c("few.traps", "many.traps", "baiting")

# plot planning unit data
plot(pu4, nr = 1, axes = FALSE, box = FALSE)

# create targets
t4 <- tibble::tibble(feature = names(sim_features),
                     zone = list(names(pu4))[rep(1, 5)],
                     target = rep(8, 5),
                     type = rep("absolute", 5))

# print targets
print(t4)
## # A tibble: 5 x 4
##   feature zone      target type    
##   <chr>   <list>     <dbl> <chr>   
## 1 layer.1 <chr [3]>      8 absolute
## 2 layer.2 <chr [3]>      8 absolute
## 3 layer.3 <chr [3]>      8 absolute
## 4 layer.4 <chr [3]>      8 absolute
## 5 layer.5 <chr [3]>      8 absolute
# create problem
p4 <- problem(pu4, zones(few.traps = sim_features * 0.1,
                         many.traps = sim_features * 0.5,
                         baiting = sim_features * 0.8,
                         feature_names = names(sim_features))) %>%
      add_min_set_objective() %>%
      add_manual_targets(t4) %>%
      add_binary_decisions()

# print problem
print(p4)
## Conservation Problem
##   zones:          few.traps, many.traps, baiting (3 zones)
##   planning units: RasterStack (100 units)
##   cost:           min: 0, max: 300
##   features:       layer.1, layer.2, layer.3, ... (5 features)
##   objective:      Minimum set objective 
##   targets:        Absolute targets [targets (min: 8, max: 8)]
##   decisions:      Binary decision 
##   constraints:    <none>
##   penalties:      <none>
##   portfolio:      default
##   solver:         default
# solve problem
s4 <- solve(p4)
## Gurobi Optimizer version 9.0.2 build v9.0.2rc0 (win64)
## Optimize a model with 105 rows, 300 columns and 1800 nonzeros
## Model fingerprint: 0xd67e7fdc
## Variable types: 0 continuous, 300 integer (300 binary)
## Coefficient statistics:
##   Matrix range     [2e-02, 1e+00]
##   Objective range  [1e+02, 3e+02]
##   Bounds range     [1e+00, 1e+00]
##   RHS range        [1e+00, 8e+00]
## Found heuristic solution: objective 9400.0000000
## Presolve removed 14 rows and 120 columns
## Presolve time: 0.00s
## Presolved: 91 rows, 180 columns, 360 nonzeros
## Found heuristic solution: objective 4800.0000000
## Variable types: 0 continuous, 180 integer (180 binary)
## Presolved: 91 rows, 180 columns, 360 nonzeros
## 
## 
## Root relaxation: objective 3.663237e+03, 1 iterations, 0.00 seconds
## 
##     Nodes    |    Current Node    |     Objective Bounds      |     Work
##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
## 
##      0     0 3663.23657    0    1 4800.00000 3663.23657  23.7%     -    0s
## H    0     0                    3800.0000000 3663.23657  3.60%     -    0s
## 
## Explored 1 nodes (1 simplex iterations) in 0.00 seconds
## Thread count was 1 (of 48 available processors)
## 
## Solution count 3: 3800 4800 9400 
## 
## Optimal solution found (tolerance 1.00e-01)
## Best objective 3.800000000000e+03, best bound 3.700000000000e+03, gap 2.6316%
# calculate feature representation
r4 <- feature_representation(p4, s4)
print(r4)
## # A tibble: 15 x 4
##    feature zone       absolute_held relative_held
##    <chr>   <chr>              <dbl>         <dbl>
##  1 layer.1 few.traps           0            0    
##  2 layer.2 few.traps           0            0    
##  3 layer.3 few.traps           0            0    
##  4 layer.4 few.traps           0            0    
##  5 layer.5 few.traps           0            0    
##  6 layer.1 many.traps          0            0    
##  7 layer.2 many.traps          0            0    
##  8 layer.3 many.traps          0            0    
##  9 layer.4 many.traps          0            0    
## 10 layer.5 many.traps          0            0    
## 11 layer.1 baiting            18.3          0.275
## 12 layer.2 baiting             8.19         0.328
## 13 layer.3 baiting            15.2          0.264
## 14 layer.4 baiting            11.6          0.340
## 15 layer.5 baiting            12.7          0.281
# plot solution
plot(category_layer(s4), main = "solution", axes = FALSE, box = FALSE)

Multiple zones with extra constraints

So it looks like baiting is the way to go! Except that we might recall that we can’t use baits in most of the planning units because they contain native species that are susceptible to baits. So now we need to specify that which of our planning units cannot be assigned to the third zone (baiting) to obtain a more useful solution.

# create data.frame to specify that we cannot bait in the first 40 units
l5 <- data.frame(pu = seq(1, 80),
                 zone = "baiting",
                 status = 0)

# preview locked data
head(l5)
##   pu    zone status
## 1  1 baiting      0
## 2  2 baiting      0
## 3  3 baiting      0
## 4  4 baiting      0
## 5  5 baiting      0
## 6  6 baiting      0
# create problem
p5 <- problem(pu4, zones(few.traps = sim_features * 0.1,
                         many.traps = sim_features * 0.5,
                         baiting = sim_features * 0.8,
                         feature_names = names(sim_features))) %>%
      add_min_set_objective() %>%
      add_manual_targets(t4) %>%
      add_manual_locked_constraints(l5) %>%
      add_binary_decisions()

# print problem
print(p5)
## Conservation Problem
##   zones:          few.traps, many.traps, baiting (3 zones)
##   planning units: RasterStack (100 units)
##   cost:           min: 0, max: 300
##   features:       layer.1, layer.2, layer.3, ... (5 features)
##   objective:      Minimum set objective 
##   targets:        Absolute targets [targets (min: 8, max: 8)]
##   decisions:      Binary decision 
##   constraints:    <Manually locked planning units [80 locked units]>
##   penalties:      <none>
##   portfolio:      default
##   solver:         default
# solve problem
s5 <- solve(p5)
## Gurobi Optimizer version 9.0.2 build v9.0.2rc0 (win64)
## Optimize a model with 105 rows, 300 columns and 1800 nonzeros
## Model fingerprint: 0x4ad99e4b
## Variable types: 0 continuous, 300 integer (300 binary)
## Coefficient statistics:
##   Matrix range     [2e-02, 1e+00]
##   Objective range  [1e+02, 3e+02]
##   Bounds range     [1e+00, 1e+00]
##   RHS range        [1e+00, 8e+00]
## Found heuristic solution: objective 13600.000000
## Presolve removed 14 rows and 120 columns
## Presolve time: 0.00s
## Presolved: 91 rows, 180 columns, 360 nonzeros
## Found heuristic solution: objective 9600.0000000
## Variable types: 0 continuous, 180 integer (180 binary)
## Presolved: 91 rows, 180 columns, 360 nonzeros
## 
## 
## Root relaxation: objective 6.938068e+03, 1 iterations, 0.00 seconds
## 
##     Nodes    |    Current Node    |     Objective Bounds      |     Work
##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
## 
##      0     0 6938.06818    0    1 9600.00000 6938.06818  27.7%     -    0s
## H    0     0                    7000.0000000 6938.06818  0.88%     -    0s
##      0     0 6938.06818    0    1 7000.00000 6938.06818  0.88%     -    0s
## 
## Explored 1 nodes (1 simplex iterations) in 0.00 seconds
## Thread count was 1 (of 48 available processors)
## 
## Solution count 3: 7000 9600 13600 
## 
## Optimal solution found (tolerance 1.00e-01)
## Best objective 7.000000000000e+03, best bound 7.000000000000e+03, gap 0.0000%
# calculate feature representation
r5 <- feature_representation(p5, s5)
print(r5)
## # A tibble: 15 x 4
##    feature zone       absolute_held relative_held
##    <chr>   <chr>              <dbl>         <dbl>
##  1 layer.1 few.traps           0            0    
##  2 layer.2 few.traps           0            0    
##  3 layer.3 few.traps           0            0    
##  4 layer.4 few.traps           0            0    
##  5 layer.5 few.traps           0            0    
##  6 layer.1 many.traps          7.40         0.178
##  7 layer.2 many.traps          3.42         0.219
##  8 layer.3 many.traps          6.00         0.167
##  9 layer.4 many.traps          4.84         0.227
## 10 layer.5 many.traps          5.26         0.186
## 11 layer.1 baiting            14.4          0.217
## 12 layer.2 baiting             4.62         0.185
## 13 layer.3 baiting            11.7          0.203
## 14 layer.4 baiting             6.05         0.177
## 15 layer.5 baiting             9.98         0.220
# plot solution
plot(category_layer(s5), main = "solution", axes = FALSE, box = FALSE)

Multiple zones with fragmentation penalties

So now the best strategy seems to be a combination of high intensity trapping and baiting. But we can also see that this solution is fairly fragmented, so we can add penalties to cluster managed planning units together. Here we will add penalties that will cluster the planning units allocated to the two trapping zones together, and penalties that will cluster the planning units allocated to the baiting zone together. We will also set an overall penalty factor to 640 to strongly penalize fragmented solutions.

# create matrix that describe boundary penalties between planning units
# allocated to different zones
z6 <- diag(3)
z6[1, 2] <- 1
z6[2, 1] <- 1
colnames(z6) <- c("few.traps", "many.traps", "baiting")
rownames(z6) <- colnames(z6)

# print matrix
print(z6)
##            few.traps many.traps baiting
## few.traps          1          1       0
## many.traps         1          1       0
## baiting            0          0       1
# create problem
p6 <- problem(pu4, zones("few.traps" = sim_features * 0.1,
                         "many.traps" = sim_features * 0.5,
                         "baiting" = sim_features * 0.8,
                         feature_names = names(sim_features))) %>%
      add_min_set_objective() %>%
      add_manual_targets(t4) %>%
      add_manual_locked_constraints(l5) %>%
      add_boundary_penalties(penalty = 640, zones = z6) %>%
      add_binary_decisions()

# print problem
print(p6)
## Conservation Problem
##   zones:          few.traps, many.traps, baiting (3 zones)
##   planning units: RasterStack (100 units)
##   cost:           min: 0, max: 300
##   features:       layer.1, layer.2, layer.3, ... (5 features)
##   objective:      Minimum set objective 
##   targets:        Absolute targets [targets (min: 8, max: 8)]
##   decisions:      Binary decision 
##   constraints:    <Manually locked planning units [80 locked units]>
##   penalties:      <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (640), zones]>
##   portfolio:      default
##   solver:         default
# solve problem
s6 <- solve(p6)
## Gurobi Optimizer version 9.0.2 build v9.0.2rc0 (win64)
## Optimize a model with 1905 rows, 1200 columns and 5400 nonzeros
## Model fingerprint: 0xb44cc86f
## Variable types: 0 continuous, 1200 integer (1200 binary)
## Coefficient statistics:
##   Matrix range     [2e-02, 1e+00]
##   Objective range  [1e+02, 6e+02]
##   Bounds range     [1e+00, 1e+00]
##   RHS range        [1e+00, 8e+00]
## Found heuristic solution: objective 20096.000000
## Presolve removed 308 rows and 232 columns
## Presolve time: 0.01s
## Presolved: 1597 rows, 968 columns, 4872 nonzeros
## Variable types: 0 continuous, 968 integer (968 binary)
## Presolve removed 720 rows and 0 columns
## Presolved: 877 rows, 968 columns, 2712 nonzeros
## 
## 
## Root relaxation: objective 1.005410e+04, 546 iterations, 0.01 seconds
## 
##     Nodes    |    Current Node    |     Objective Bounds      |     Work
##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
## 
##      0     0 10054.0986    0   57 20096.0000 10054.0986  50.0%     -    0s
## H    0     0                    11300.000000 10054.0986  11.0%     -    0s
## H    0     0                    10264.000000 10054.0986  2.05%     -    0s
## 
## Explored 1 nodes (825 simplex iterations) in 0.05 seconds
## Thread count was 1 (of 48 available processors)
## 
## Solution count 3: 10264 11300 20096 
## 
## Optimal solution found (tolerance 1.00e-01)
## Best objective 1.026400000000e+04, best bound 1.005600000000e+04, gap 2.0265%
# calculate feature representation
r6 <- feature_representation(p6, s6)
print(r6)
## # A tibble: 15 x 4
##    feature zone       absolute_held relative_held
##    <chr>   <chr>              <dbl>         <dbl>
##  1 layer.1 few.traps           0            0    
##  2 layer.2 few.traps           0            0    
##  3 layer.3 few.traps           0            0    
##  4 layer.4 few.traps           0            0    
##  5 layer.5 few.traps           0            0    
##  6 layer.1 many.traps          7.51         0.180
##  7 layer.2 many.traps          3.38         0.217
##  8 layer.3 many.traps          5.75         0.160
##  9 layer.4 many.traps          4.99         0.234
## 10 layer.5 many.traps          5.46         0.192
## 11 layer.1 baiting            14.4          0.217
## 12 layer.2 baiting             4.62         0.185
## 13 layer.3 baiting            11.7          0.203
## 14 layer.4 baiting             6.05         0.177
## 15 layer.5 baiting             9.98         0.220
# plot solution
plot(category_layer(s6), main = "solution", axes = FALSE, box = FALSE)

Finally, it appears we might have a viable solution for this made-up conservation problem.

Multiple zones with fragmentation penalties and mandatory allocations

Finally, we might be interested in conservation planning scenarios where every single planning unit must be allocated to a management zone. This is often the case when developing land-use plans where every single planning unit needs to be allocated to a specific management zone. Though it makes less sense here, let’s see what happens to the solution if we needed to do at least one form of control in every single planning unit.

# create matrix that describe boundary penalties between planning units
# allocated to different zones
p7 <- problem(pu4, zones("few.traps" = sim_features * 0.1,
                         "many.traps" = sim_features * 0.5,
                         "baiting" = sim_features * 0.8,
                         feature_names = names(sim_features))) %>%
      add_min_set_objective() %>%
      add_manual_targets(t4) %>%
      add_mandatory_allocation_constraints() %>%
      add_manual_locked_constraints(l5) %>%
      add_boundary_penalties(penalty = 640, zones = z6) %>%
      add_binary_decisions()

# print problem
print(p7)
## Conservation Problem
##   zones:          few.traps, many.traps, baiting (3 zones)
##   planning units: RasterStack (100 units)
##   cost:           min: 0, max: 300
##   features:       layer.1, layer.2, layer.3, ... (5 features)
##   objective:      Minimum set objective 
##   targets:        Absolute targets [targets (min: 8, max: 8)]
##   decisions:      Binary decision 
##   constraints:    <Mandatory allocation constraints [apply constraints? (1)]
##                    Manually locked planning units [80 locked units]>
##   penalties:      <Boundary penalties [edge factor (min: 0.5, max: 0.5), penalty (640), zones]>
##   portfolio:      default
##   solver:         default
# solve problem
s7 <- solve(p7)
## Gurobi Optimizer version 9.0.2 build v9.0.2rc0 (win64)
## Optimize a model with 1905 rows, 1200 columns and 5400 nonzeros
## Model fingerprint: 0x89a9dafb
## Variable types: 0 continuous, 1200 integer (1200 binary)
## Coefficient statistics:
##   Matrix range     [2e-02, 1e+00]
##   Objective range  [1e+02, 6e+02]
##   Bounds range     [1e+00, 1e+00]
##   RHS range        [1e+00, 8e+00]
## Found heuristic solution: objective 19600.000000
## Presolve removed 392 rows and 316 columns
## Presolve time: 0.01s
## Presolved: 1513 rows, 884 columns, 4608 nonzeros
## Variable types: 0 continuous, 884 integer (884 binary)
## Presolve removed 728 rows and 8 columns
## Presolved: 785 rows, 876 columns, 2448 nonzeros
## 
## 
## Root relaxation: objective 1.323093e+04, 271 iterations, 0.00 seconds
## 
##     Nodes    |    Current Node    |     Objective Bounds      |     Work
##  Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time
## 
##      0     0 13230.9255    0  102 19600.0000 13230.9255  32.5%     -    0s
## H    0     0                    13260.000000 13230.9255  0.22%     -    0s
## 
## Explored 1 nodes (418 simplex iterations) in 0.02 seconds
## Thread count was 1 (of 48 available processors)
## 
## Solution count 2: 13260 19600 
## 
## Optimal solution found (tolerance 1.00e-01)
## Best objective 1.326000000000e+04, best bound 1.323200000000e+04, gap 0.2112%
# calculate feature representation
r7 <- feature_representation(p7, s7)
print(r7)
## # A tibble: 15 x 4
##    feature zone       absolute_held relative_held
##    <chr>   <chr>              <dbl>         <dbl>
##  1 layer.1 few.traps           5.92        0.711 
##  2 layer.2 few.traps           2.33        0.745 
##  3 layer.3 few.traps           5.25        0.730 
##  4 layer.4 few.traps           3.18        0.745 
##  5 layer.5 few.traps           4.00        0.706 
##  6 layer.1 many.traps          3.02        0.0725
##  7 layer.2 many.traps          1.09        0.0700
##  8 layer.3 many.traps          2.41        0.0670
##  9 layer.4 many.traps          1.66        0.0779
## 10 layer.5 many.traps          2.11        0.0743
## 11 layer.1 baiting            14.4         0.217 
## 12 layer.2 baiting             4.62        0.185 
## 13 layer.3 baiting            11.7         0.203 
## 14 layer.4 baiting             6.05        0.177 
## 15 layer.5 baiting             9.98        0.220
# plot solution
plot(category_layer(s7), main = "solution", axes = FALSE, box = FALSE)

Conclusion

Hopefully, this vignette has provided an informative introduction to building and solving problems with multiple zones using the prioritizr R package. Although we have examined only a few different functions here, almost every single function for modifying conservation planning problems is compatible with problems that contain zones. It’s worth noting that working with multiple zones is a lot trickier than working with a single zone, so we would recommend playing around with the code in the Examples sections of the help pages to get a feel for what different functions do when they are applied to problems with multiple zones.

References

Cattarino, L., Hermoso, V., Carwardine, J., Adams, V.M., Kennard, M.J. & Linke, S. (2018). Information uncertainty influences conservation outcomes when prioritizing multi-action management efforts. Journal of Applied Ecology, Available at: https://doi.org/10.1111/1365–2664.13147.

Klein, C.J., Tulloch, V.J., Halpern, B.S., Selkoe, K.A., Watts, M.E., Steinback, C., Scholz, A. & Possingham, H.P. (2013). Tradeoffs in marine reserve design: Habitat condition, representation, and socioeconomic costs. Conservation Letters, 6, 324–332.

Klein, C., Wilson, K., Watts, M., Stein, J., Berry, S., Carwardine, J., Smith, M.S., Mackey, B. & Possingham, H. (2009). Incorporating ecological and evolutionary processes into continental-scale conservation planning. Ecological Applications, 19, 206–217.

Margules, C.R. & Pressey, R.L. (2000). Systematic conservation planning. Nature, 405, 243–253.

Stigner, M.G., Beyer, H.L., Klein, C.J. & Fuller, R.A. (2016). Reconciling recreational use and conservation values in a coastal protected area. Journal of Applied Ecology, 53, 1206–1214.

Watts, M.E., Ball, I.R., Stewart, R.S., Klein, C.J., Wilson, K., Steinback, C., Lourival, R., Kircher, L. & Possingham, H.P. (2009). Marxan with Zones: Software for optimal conservation based land- and sea-use zoning. Environmental Modelling & Software, 24, 1513–1521.

Wilson, K.A., Meijaard, E., Drummond, S., Grantham, H.S., Boitani, L., Catullo, G., Christie, L., Dennis, R., Dutton, I., Falcucci, A. & others. (2010). Conserving biodiversity in production landscapes. Ecological Applications, 20, 1721–1732.