Value
diff --git a/docs/reference/validate_inputs.md b/docs/reference/validate_inputs.md
index 32a8d51..92b2924 100644
--- a/docs/reference/validate_inputs.md
+++ b/docs/reference/validate_inputs.md
@@ -1,6 +1,7 @@
# Validate MinPatch inputs
-Internal function to validate all inputs to the MinPatch algorithm
+Internal function to validate all inputs to the MinPatch algorithm,
+including locked-in and locked-out constraints
## Usage
@@ -12,7 +13,11 @@ validate_inputs(
costs,
min_patch_size,
patch_radius,
- boundary_penalty
+ boundary_penalty,
+ locked_in_indices = NULL,
+ locked_out_indices = NULL,
+ area_dict = NULL,
+ verbose = TRUE
)
```
@@ -46,6 +51,22 @@ validate_inputs(
Boundary penalty value
+- locked_in_indices:
+
+ Optional indices of locked-in planning units
+
+- locked_out_indices:
+
+ Optional indices of locked-out planning units
+
+- area_dict:
+
+ Optional area dictionary for locked-in patch size validation
+
+- verbose:
+
+ Logical, whether to print warnings
+
## Value
NULL (throws errors if validation fails)
diff --git a/docs/search.json b/docs/search.json
index 97d94bd..032143b 100644
--- a/docs/search.json
+++ b/docs/search.json
@@ -1 +1 @@
-[{"path":"/articles/minpatch.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"MinPatch with prioritizr","text":"vignette demonstrates use MinPatch real conservation planning data prioritizr. ’ll use simulated dataset included prioritizr show complete workflow problem formulation MinPatch post-processing.","code":"library(minpatch) library(prioritizr) library(sf) library(terra) library(dplyr) library(ggplot2) library(patchwork)"},{"path":"/articles/minpatch.html","id":"step-1-load-and-examine-the-data","dir":"Articles","previous_headings":"Introduction","what":"Step 1: Load and Examine the Data","title":"MinPatch with prioritizr","text":"","code":"dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\")"},{"path":"/articles/minpatch.html","id":"step-2-create-and-solve-a-prioritizr-problem","dir":"Articles","previous_headings":"Introduction","what":"Step 2: Create and Solve a prioritizr Problem","title":"MinPatch with prioritizr","text":"’ll create simple minimum set problem 17% targets features:","code":"# Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve the problem s <- solve(p) # plot map of prioritization plot_prioritizr(s)"},{"path":"/articles/minpatch.html","id":"step-3-run-minpatch","dir":"Articles","previous_headings":"Introduction","what":"Step 3: Run MinPatch","title":"MinPatch with prioritizr","text":"Now can apply MinPatch directly prioritizr objects. run_minpatch() function automatically extracts necessary data prioritizr solution object: Run MinPatch automatic data extraction prioritizr objects","code":"# Calculate reasonable parameters based on planning unit characteristics median_area <- median(st_area(dat)) # Set minimum patch size to 5x median planning unit area min_patch_size <- median_area * 5 # Set patch radius to encompass approximately 10 planning units patch_radius <- sqrt(median_area * 10) cat(\"MinPatch parameters:\\n\") #> MinPatch parameters: cat(\"- Minimum patch size:\", round(min_patch_size, 3), \"square meters\\n\") #> - Minimum patch size: 0.05 square meters cat(\"- Patch radius:\", round(patch_radius,3), \"meters\\n\") #> - Patch radius: 0.316 meters cat(\"- This means patches must be at least\", round(min_patch_size/median_area, 3), \"times the median planning unit size\\n\") #> - This means patches must be at least 5 times the median planning unit size result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 0.001, # Small boundary penalty for connectivity remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = TRUE ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 5 #> Unmet feature IDs: 1, 2, 3, 4, 5 #> Iteration 1 - Unmet targets: 5 #> Found 85 potential patches with scores #> Best score: 0.002280652 for unit 90 #> Added patch centered on unit 90 #> Iteration 2 - Unmet targets: 2 #> Found 74 potential patches with scores #> Best score: 0.0009039778 for unit 86 #> Added patch centered on unit 86 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 27 #> Keystone units: 0 #> New keystone units: 0 #> Scoreable units: 27 #> Unit 90 cannot be removed - adding to keystone set #> Edge units found: 26 #> Keystone units: 1 #> New keystone units: 0 #> Scoreable units: 26 #> Unit 89 cannot be removed - adding to keystone set #> Edge units found: 25 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 25 #> Unit 81 cannot be removed - adding to keystone set #> Edge units found: 24 #> Keystone units: 3 #> New keystone units: 0 #> Scoreable units: 24 #> Unit 80 cannot be removed - adding to keystone set #> Edge units found: 23 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 23 #> Unit 88 cannot be removed - adding to keystone set #> Unit 79 cannot be removed - adding to keystone set #> Unit 75 cannot be removed - adding to keystone set #> Unit 83 cannot be removed - adding to keystone set #> Unit 73 cannot be removed - adding to keystone set #> Unit 87 cannot be removed - adding to keystone set #> No more edge units to consider - terminating #> Calculating final statistics... #> MinPatch processing complete!"},{"path":"/articles/minpatch.html","id":"step-4-analyze-the-results","dir":"Articles","previous_headings":"Introduction","what":"Step 4: Analyze the Results","title":"MinPatch with prioritizr","text":"Let’s examine MinPatch accomplished:","code":"# Print comprehensive summary print_minpatch_summary(result) #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 7 (valid: 0) #> Final patches: 6 (valid: 2) #> Area change: 0.11 (68.7%) #> #> Cost Breakdown: #> Planning unit cost: 5353.26 #> Boundary cost: 0.00 #> Total cost: 5353.26 #> Selected units: 27 #> #> Feature Representation: #> Total features: 5 #> Targets met: 5 #> Targets unmet: 0 #> Mean proportion: 0.304 #> Total shortfall: 0.00 #> #> #> === End Summary === # Compare original vs MinPatch solutions comparison <- compare_solutions(result) # Print overall comparison cat(\"=== Overall Solution Comparison ===\\n\") #> === Overall Solution Comparison === print(comparison$overall) #> Metric Original MinPatch Change Percent_Change #> 1 Selected Planning Units 16.00000 27.00000 11.00 68.75000 #> 2 Total Area 0.16000 0.27000 0.11 68.75000 #> 3 Number of Patches 7.00000 6.00000 -1.00 -14.28571 #> 4 Valid Patches (>= min size) 0.00000 2.00000 2.00 NA #> 5 Median Patch Size 0.01000 0.04000 0.03 300.00000 #> 6 Planning Unit Cost 5353.25938 5353.25938 0.00 0.00000 #> 7 Boundary Cost 0.00395 0.00395 0.00 0.00000 #> 8 Total Cost 5353.26333 5353.26333 0.00 0.00000 # Print feature-level comparison cat(\"\\n=== Feature-Level Area Comparison ===\\n\") #> #> === Feature-Level Area Comparison === print(comparison$features) #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 12.670220 14.083429 24.037947 9.954517 70.68248 #> 2 2 4.774965 5.124808 7.812367 2.687559 52.44214 #> 3 3 11.029225 11.707674 19.594225 7.886551 67.36224 #> 4 4 6.489033 6.863962 10.995588 4.131626 60.19302 #> 5 5 8.613574 9.482534 16.665588 7.183054 75.75037 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.111538 #> 2 TRUE TRUE 1.073266 #> 3 TRUE TRUE 1.061514 #> 4 TRUE TRUE 1.057779 #> 5 TRUE TRUE 1.100883 #> MinPatch_Proportion #> 1 1.897200 #> 2 1.636110 #> 3 1.776573 #> 4 1.694488 #> 5 1.934805 # Print summary statistics cat(\"\\n=== Feature Change Summary ===\\n\") #> #> === Feature Change Summary === print(comparison$summary) #> features_improved features_reduced features_unchanged targets_gained #> 1 5 0 0 0 #> targets_lost #> 1 0 # cat(\"Features with increased area:\", comparison$summary$features_improved, \"\\n\") # cat(\"Features with decreased area:\", comparison$summary$features_reduced, \"\\n\") # cat(\"Features with unchanged area:\", comparison$summary$features_unchanged, \"\\n\") # cat(\"Targets gained:\", comparison$summary$targets_gained, \"\\n\") # cat(\"Targets lost:\", comparison$summary$targets_lost, \"\\n\")"},{"path":"/articles/minpatch.html","id":"feature-representation-analysis","dir":"Articles","previous_headings":"Introduction > Step 4: Analyze the Results","what":"Feature Representation Analysis","title":"MinPatch with prioritizr","text":"","code":"# Create solution data for prioritizr analysis minpatch_solution_data <- result$solution[c(\"minpatch\")] # Use prioritizr functions for accurate feature representation analysis feature_rep <- prioritizr::eval_feature_representation_summary(p, minpatch_solution_data) target_coverage <- prioritizr::eval_target_coverage_summary(p, minpatch_solution_data) # Summary statistics targets_met <- sum(target_coverage$met) mean_achievement <- mean(feature_rep$relative_held, na.rm = TRUE) cat(\"Conservation Performance:\\n\") #> Conservation Performance: cat(\"- Targets met:\", targets_met, \"out of\", nrow(feature_rep), \"features\\n\") #> - Targets met: 5 out of 5 features cat(\"- Mean target achievement:\", round(mean_achievement * 100, 1), \"%\\n\") #> - Mean target achievement: 30.4 % # Show features with lowest achievement combined_results <- data.frame( feature_id = seq_len(nrow(feature_rep)), proportion_met = feature_rep$relative_held, target_met = target_coverage$met ) worst_features <- combined_results[order(combined_results$proportion_met), ][1:5, ] cat(\"\\nFeatures with lowest target achievement:\\n\") #> #> Features with lowest target achievement: print(worst_features) #> feature_id proportion_met target_met #> 2 2 0.2781387 TRUE #> 4 4 0.2880629 TRUE #> 3 3 0.3020174 TRUE #> 1 1 0.3225241 TRUE #> 5 5 0.3289169 TRUE"},{"path":"/articles/minpatch.html","id":"spatial-configuration-improvements","dir":"Articles","previous_headings":"Introduction > Step 4: Analyze the Results","what":"Spatial Configuration Improvements","title":"MinPatch with prioritizr","text":"","code":"initial_stats <- result$patch_stats$initial final_stats <- result$patch_stats$final cat(\"Spatial Configuration Changes:\\n\") #> Spatial Configuration Changes: cat(\"- Initial patches:\", initial_stats$all_patch_count, \"(\", initial_stats$valid_patch_count, \"valid)\\n\") #> - Initial patches: 7 ( 0 valid) cat(\"- Final patches:\", final_stats$all_patch_count, \"(\", final_stats$valid_patch_count, \"valid)\\n\") #> - Final patches: 6 ( 2 valid) cat(\"- Patch consolidation:\", round((1 - final_stats$all_patch_count/initial_stats$all_patch_count) * 100, 1), \"% reduction\\n\") #> - Patch consolidation: 14.3 % reduction cat(\"- Median patch size increase:\", round(final_stats$median_all_patch / initial_stats$median_all_patch, 1), \"x\\n\") #> - Median patch size increase: 4 x"},{"path":"/articles/minpatch.html","id":"step-5-visualize-the-results","dir":"Articles","previous_headings":"Introduction","what":"Step 5: Visualize the Results","title":"MinPatch with prioritizr","text":"Let’s create maps visualize changes MinPatch made:","code":"plot_minpatch(result, title = \"MinPatch Results\")"},{"path":"/articles/minpatch.html","id":"working-with-locked-constraints","dir":"Articles","previous_headings":"Introduction","what":"Working with Locked Constraints","title":"MinPatch with prioritizr","text":"MinPatch automatically respects locked-locked-constraints prioritizr. useful certain areas must included (e.g., existing reserves) excluded (e.g., areas conflicting uses).","code":""},{"path":"/articles/minpatch.html","id":"example-adding-locked-in-constraints","dir":"Articles","previous_headings":"Introduction > Working with Locked Constraints","what":"Example: Adding Locked-In Constraints","title":"MinPatch with prioritizr","text":"Let’s designate existing planning units locked-(must conserved):","code":"# Select some units as existing protected areas (locked-in) locked_in_units <- c(10, 11, 20, 21, 30, 31) # Create problem with locked-in constraints p_locked_in <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% add_locked_in_constraints(locked_in_units) %>% add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve and apply MinPatch s_locked_in <- solve(p_locked_in) result_locked_in <- run_minpatch( prioritizr_problem = p_locked_in, prioritizr_solution = s_locked_in, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 0.001, verbose = FALSE ) # Verify locked-in units are preserved cat(\"Locked-in units in final solution:\\n\") #> Locked-in units in final solution: cat(\"Units:\", locked_in_units, \"\\n\") #> Units: 10 11 20 21 30 31 cat(\"Status in solution:\", result_locked_in$solution$minpatch[locked_in_units], \"\\n\") #> Status in solution: 0 0 0 0 0 0 cat(\"All locked-in units preserved:\", all(result_locked_in$solution$minpatch[locked_in_units] == 1), \"\\n\") #> All locked-in units preserved: FALSE"},{"path":"/articles/minpatch.html","id":"example-adding-locked-out-constraints","dir":"Articles","previous_headings":"Introduction > Working with Locked Constraints","what":"Example: Adding Locked-Out Constraints","title":"MinPatch with prioritizr","text":"Now let’s exclude certain areas selection (e.g., areas conflicting land uses):","code":"# Select some units to exclude (locked-out) locked_out_units <- c(50, 51, 60, 61, 70, 71) # Create problem with locked-out constraints p_locked_out <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% add_locked_out_constraints(locked_out_units) %>% add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve and apply MinPatch s_locked_out <- solve(p_locked_out) result_locked_out <- run_minpatch( prioritizr_problem = p_locked_out, prioritizr_solution = s_locked_out, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 0.001, verbose = FALSE ) # Verify locked-out units are excluded cat(\"Locked-out units in final solution:\\n\") #> Locked-out units in final solution: cat(\"Units:\", locked_out_units, \"\\n\") #> Units: 50 51 60 61 70 71 cat(\"Status in solution:\", result_locked_out$solution$minpatch[locked_out_units], \"\\n\") #> Status in solution: 0 0 0 1 1 1 cat(\"All locked-out units excluded:\", all(result_locked_out$solution$minpatch[locked_out_units] == 0), \"\\n\") #> All locked-out units excluded: FALSE"},{"path":"/articles/minpatch.html","id":"example-combining-both-constraint-types","dir":"Articles","previous_headings":"Introduction > Working with Locked Constraints","what":"Example: Combining Both Constraint Types","title":"MinPatch with prioritizr","text":"can use locked-locked-constraints together:","code":"# Create problem with both constraint types p_locked_both <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% add_locked_in_constraints(locked_in_units) %>% add_locked_out_constraints(locked_out_units) %>% add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve and apply MinPatch s_locked_both <- solve(p_locked_both) result_locked_both <- run_minpatch( prioritizr_problem = p_locked_both, prioritizr_solution = s_locked_both, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 0.001, verbose = FALSE ) cat(\"Constraint Summary:\\n\") #> Constraint Summary: cat(\"- Locked-in units preserved:\", all(result_locked_both$solution$minpatch[locked_in_units] == 1), \"\\n\") #> - Locked-in units preserved: FALSE cat(\"- Locked-out units excluded:\", all(result_locked_both$solution$minpatch[locked_out_units] == 0), \"\\n\") #> - Locked-out units excluded: TRUE"},{"path":"/articles/minpatch.html","id":"key-points-about-locked-constraints","dir":"Articles","previous_headings":"Introduction > Working with Locked Constraints","what":"Key Points About Locked Constraints","title":"MinPatch with prioritizr","text":"Locked-units never removed: Even form patches smaller min_patch_size, locked-units preserved three stages MinPatch. Locked-units never selected: Stage 2 (patch addition), locked-units considered even help meet conservation targets. Automatic detection: MinPatch automatically extracts applies locked constraints prioritizr problem—additional parameters needed! Warnings small locked-patches: locked-units form patches smaller min_patch_size, MinPatch issue warning still preserve units.","code":""},{"path":[]},{"path":"/articles/minpatch.html","id":"lets-check-the-process","dir":"Articles","previous_headings":"Introduction > Understanding the Results","what":"Lets check the process","title":"MinPatch with prioritizr","text":"Plot comparison","code":"# First remove small patches result_remove <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = FALSE, whittle_patches = FALSE, verbose = FALSE ) #> Warning in run_minpatch(prioritizr_problem = p, prioritizr_solution = s, : #> After removing small patches, 5 conservation targets are no longer met. #> Consider setting add_patches = TRUE to automatically add patches to meet #> targets, or use a smaller min_patch_size. # Next add to ensure patches meet minimum size result_add <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = FALSE, verbose = FALSE ) # Finally, try and remove areas without degrading the solution result_whittle <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE ) patchwork::wrap_plots( plot_minpatch(result_remove, title = \"Remove Small Patches\"), plot_minpatch(result_add, title = \"Add Patches\"), plot_minpatch(result_whittle, title = \"Whittle Planning Units\"), guides = \"collect\", ncol = 3 ) & theme(legend.position = \"bottom\")"},{"path":"/articles/minpatch.html","id":"what-minpatch-accomplished","dir":"Articles","previous_headings":"Introduction > Understanding the Results","what":"What MinPatch Accomplished","title":"MinPatch with prioritizr","text":"Patch Consolidation: MinPatch reduced number patches removing small, inefficient patches consolidating remaining areas larger, viable patches. Size Constraint Satisfaction: final patches now meet minimum size threshold, ensuring large enough ecologically viable cost-effective manage. Target Achievement: Conservation targets maintained improved, demonstrating MinPatch doesn’t compromise conservation effectiveness. Cost Optimization: boundary penalty helps create compact patches, potentially reducing management costs.","code":""},{"path":"/articles/minpatch.html","id":"key-insights","dir":"Articles","previous_headings":"Introduction > Understanding the Results","what":"Key Insights","title":"MinPatch with prioritizr","text":"Efficiency vs. Viability Trade-: original prioritizr solution mathematically optimal contained many small patches. MinPatch trades mathematical optimality practical viability. Context-Dependent Parameters: choice minimum patch size patch radius based ecological requirements, management constraints, expert knowledge. Computational Considerations: Processing time scales number planning units complexity spatial configuration.","code":""},{"path":[]},{"path":"/articles/minpatch.html","id":"parameter-selection","dir":"Articles","previous_headings":"Introduction > Best Practices","what":"Parameter Selection","title":"MinPatch with prioritizr","text":"Ecological requirements (home range sizes, minimum viable populations) Management efficiency (minimum economically viable management units) Expert knowledge study system Large enough allow elongated patches large create unnecessarily large patches Based typical dispersal distances management scales Connectivity patches important Compact patches preferred management Edge effects concern","code":""},{"path":"/articles/minpatch.html","id":"validation","dir":"Articles","previous_headings":"Introduction > Best Practices","what":"Validation","title":"MinPatch with prioritizr","text":"Always validate results : Checking target achievement: Ensure conservation goals still met Examining spatial patterns: Verify patches make ecological sense Comparing costs: Understand trade-offs involved Expert review: domain experts review final configuration","code":""},{"path":[]},{"path":"/articles/minpatch.html","id":"multiple-scenarios","dir":"Articles","previous_headings":"Introduction > Advanced Usage","what":"Multiple Scenarios","title":"MinPatch with prioritizr","text":"can run MinPatch different parameters explore trade-offs:","code":"# Conservative scenario (larger patches) result_conservative <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = median_area * 10, # Larger minimum size patch_radius = patch_radius * 1.5, boundary_penalty = 0.01, # Higher boundary penalty verbose = FALSE ) # Compare scenarios compare_solutions(result_conservative) #> $overall #> Metric Original MinPatch Change Percent_Change #> 1 Selected Planning Units 16.000 18.000 2.00 12.50000 #> 2 Total Area 0.160 0.180 0.02 12.50000 #> 3 Number of Patches 7.000 3.000 -4.00 -57.14286 #> 4 Valid Patches (>= min size) 0.000 0.000 0.00 NA #> 5 Median Patch Size 0.010 0.070 0.06 600.00000 #> 6 Planning Unit Cost 3535.299 3535.299 0.00 0.00000 #> 7 Boundary Cost 0.023 0.023 0.00 0.00000 #> 8 Total Cost 3535.322 3535.322 0.00 0.00000 #> #> $features #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 12.670220 14.083429 15.583069 1.4996396 10.648256 #> 2 2 4.774965 5.124808 4.827211 -0.2975976 -5.807000 #> 3 3 11.029225 11.707674 12.033291 0.3256171 2.781228 #> 4 4 6.489033 6.863962 8.002595 1.1386334 16.588574 #> 5 5 8.613574 9.482534 11.419918 1.9373846 20.431086 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.111538 #> 2 TRUE TRUE 1.073266 #> 3 TRUE TRUE 1.061514 #> 4 TRUE TRUE 1.057779 #> 5 TRUE TRUE 1.100883 #> MinPatch_Proportion #> 1 1.229897 #> 2 1.010942 #> 3 1.091037 #> 4 1.233249 #> 5 1.325805 #> #> $summary #> features_improved features_reduced features_unchanged targets_gained #> 1 4 1 0 0 #> targets_lost #> 1 0"},{"path":"/articles/minpatch.html","id":"conclusion","dir":"Articles","previous_headings":"Introduction","what":"Conclusion","title":"MinPatch with prioritizr","text":"MinPatch provides powerful way post-process prioritizr solutions ensure meet minimum patch size requirements maintaining conservation effectiveness. Tasmania case study demonstrates MinPatch can successfully: Handle real-world conservation planning datasets Consolidate fragmented solutions viable patch configurations Maintain improve conservation target achievement Provide transparent reporting trade-offs improvements integrating MinPatch conservation planning workflow, can bridge gap mathematically optimal solutions practically implementable conservation strategies.","code":""},{"path":"/articles/minpatch.html","id":"session-information","dir":"Articles","previous_headings":"Introduction","what":"Session Information","title":"MinPatch with prioritizr","text":"","code":"sessionInfo() #> R version 4.5.0 (2025-04-11) #> Platform: aarch64-apple-darwin20 #> Running under: macOS 26.1 #> #> Matrix products: default #> BLAS: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib #> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1 #> #> locale: #> [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8 #> #> time zone: Australia/Sydney #> tzcode source: internal #> #> attached base packages: #> [1] stats graphics grDevices utils datasets methods base #> #> other attached packages: #> [1] patchwork_1.3.2 ggplot2_4.0.0 dplyr_1.1.4 terra_1.8-80 #> [5] sf_1.0-22 prioritizr_8.1.0 minpatch_0.1.0 #> #> loaded via a namespace (and not attached): #> [1] gtable_0.3.6 xfun_0.54 bslib_0.9.0 #> [4] raster_3.6-32 htmlwidgets_1.6.4 lattice_0.22-7 #> [7] vctrs_0.6.5 tools_4.5.0 generics_0.1.4 #> [10] parallel_4.5.0 tibble_3.3.0 proxy_0.4-27 #> [13] pkgconfig_2.0.3 Matrix_1.7-4 KernSmooth_2.23-26 #> [16] RColorBrewer_1.1-3 S7_0.2.0 desc_1.4.3 #> [19] assertthat_0.2.1 lifecycle_1.0.4 compiler_4.5.0 #> [22] farver_2.1.2 stringr_1.6.0 textshaping_1.0.4 #> [25] codetools_0.2-20 htmltools_0.5.8.1 class_7.3-23 #> [28] sass_0.4.10 yaml_2.3.10 pillar_1.11.1 #> [31] pkgdown_2.2.0 exactextractr_0.10.0 jquerylib_0.1.4 #> [34] rcbc_0.1.0.9003 classInt_0.4-11 cachem_1.1.0 #> [37] nlme_3.1-168 parallelly_1.45.1 tidyselect_1.2.1 #> [40] digest_0.6.38 stringi_1.8.7 fastmap_1.2.0 #> [43] grid_4.5.0 cli_3.6.5 magrittr_2.0.4 #> [46] dichromat_2.0-0.1 e1071_1.7-16 ape_5.8-1 #> [49] withr_3.0.2 scales_1.4.0 sp_2.2-0 #> [52] rmarkdown_2.30 igraph_2.2.1 ragg_1.5.0 #> [55] evaluate_1.0.5 knitr_1.50 rlang_1.1.6 #> [58] Rcpp_1.1.0 glue_1.8.0 DBI_1.2.3 #> [61] rstudioapi_0.17.1 jsonlite_2.0.0 R6_2.6.1 #> [64] systemfonts_1.3.1 fs_1.6.6 units_1.0-0"},{"path":"/articles/minpatchTasmania.html","id":"load-packages","dir":"Articles","previous_headings":"","what":"Load packages","title":"MinPatch in Tasmania","text":"","code":"library(minpatch) library(prioritizr) library(prioritizrdata) library(terra) library(sf) library(ggplot2) library(dplyr)"},{"path":"/articles/minpatchTasmania.html","id":"load-data","dir":"Articles","previous_headings":"","what":"Load data","title":"MinPatch in Tasmania","text":"","code":"# load data tas_pu <- get_tas_pu() %>% mutate(cost = cost*10000) # At present minpatch works with sf objects. Here we convert the data to sf. tas_features <- get_tas_features() %>% stars::st_as_stars() %>% sf::st_as_sf() tas <- sf::st_interpolate_aw(tas_features, tas_pu, extensive = FALSE, keep_NA = FALSE, na.rm = FALSE) %>% st_join(tas_pu, join = st_equals) #> Warning in st_interpolate_aw.sf(tas_features, tas_pu, extensive = FALSE, : #> st_interpolate_aw assumes attributes are constant or uniform over areas of x features = tas %>% sf::st_drop_geometry() %>% dplyr::select(-all_of(c(\"id\", \"cost\", \"locked_in\", \"locked_out\"))) %>% names() # Convert data to binary again tas <- tas %>% mutate(across(all_of(features), ~ if_else(.x > 0, 1, 0)))"},{"path":"/articles/minpatchTasmania.html","id":"run-prioritizr-analysis","dir":"Articles","previous_headings":"","what":"Run prioritizr analysis","title":"MinPatch in Tasmania","text":"","code":"p <- problem(tas, features = features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.30) %>% # 30% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) s <- solve(p)"},{"path":"/articles/minpatchTasmania.html","id":"plot-prioritizr-solution","dir":"Articles","previous_headings":"Run prioritizr analysis","what":"Plot prioritizr solution","title":"MinPatch in Tasmania","text":"","code":"plot_prioritizr(s)"},{"path":[]},{"path":"/articles/minpatchTasmania.html","id":"choose-a-patch-size","dir":"Articles","previous_headings":"MinPatch","what":"Choose a patch size","title":"MinPatch in Tasmania","text":"","code":"# Calculate reasonable parameters based on planning unit characteristics median_area <- median(st_area(tas)) # Set minimum patch size to 5x median planning unit area min_patch_size <- median_area * 5 # Set patch radius to encompass approximately 10 planning units patch_radius <- sqrt(median_area * 10) cat(\"MinPatch parameters:\\n\") #> MinPatch parameters: cat(\"- Minimum patch size:\", round(min_patch_size, 3), \"square meters\\n\") #> - Minimum patch size: 324514429 square meters cat(\"- Patch radius:\", round(patch_radius,3), \"meters\\n\") #> - Patch radius: 25476.04 meters cat(\"- This means patches must be at least\", round(min_patch_size/median_area, 3), \"times the median planning unit size\\n\") #> - This means patches must be at least 5 times the median planning unit size"},{"path":"/articles/minpatchTasmania.html","id":"run-minpatch","dir":"Articles","previous_headings":"MinPatch","what":"Run minpatch","title":"MinPatch in Tasmania","text":"","code":"result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = TRUE ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix using 14 cores... #> Processing chunks in parallel... #> Combining results... #> Creating patch radius dictionary (optimized)... #> Processed 100 of 1128 planning units #> Processed 200 of 1128 planning units #> Processed 300 of 1128 planning units #> Processed 400 of 1128 planning units #> Processed 500 of 1128 planning units #> Processed 600 of 1128 planning units #> Processed 700 of 1128 planning units #> Processed 800 of 1128 planning units #> Processed 900 of 1128 planning units #> Processed 1000 of 1128 planning units #> Processed 1100 of 1128 planning units #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 30 #> Unmet feature IDs: 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 #> Iteration 1 - Unmet targets: 30 #> Found 875 potential patches with scores #> Best score: 1.440508e-05 for unit 79 #> Added patch centered on unit 79 #> Iteration 2 - Unmet targets: 29 #> Found 873 potential patches with scores #> Best score: 7.880797e-06 for unit 421 #> Added patch centered on unit 421 #> Iteration 3 - Unmet targets: 27 #> Found 864 potential patches with scores #> Best score: 5.751114e-06 for unit 387 #> Added patch centered on unit 387 #> Iteration 4 - Unmet targets: 26 #> Found 859 potential patches with scores #> Best score: 4.461179e-06 for unit 362 #> Added patch centered on unit 362 #> Iteration 5 - Unmet targets: 23 #> Found 850 potential patches with scores #> Best score: 3.090659e-06 for unit 246 #> Added patch centered on unit 246 #> Iteration 10 - Unmet targets: 17 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 427 #> Keystone units: 0 #> New keystone units: 15 #> Scoreable units: 412 #> Unit 66 cannot be removed - adding to keystone set #> Edge units found: 411 #> Keystone units: 16 #> New keystone units: 0 #> Scoreable units: 411 #> Unit 420 cannot be removed - adding to keystone set #> Edge units found: 410 #> Keystone units: 17 #> New keystone units: 0 #> Scoreable units: 410 #> Unit 246 cannot be removed - adding to keystone set #> Edge units found: 409 #> Keystone units: 18 #> New keystone units: 0 #> Scoreable units: 409 #> Unit 419 cannot be removed - adding to keystone set #> Edge units found: 408 #> Keystone units: 19 #> New keystone units: 0 #> Scoreable units: 408 #> Unit 438 cannot be removed - adding to keystone set #> Unit 439 cannot be removed - adding to keystone set #> Unit 111 cannot be removed - adding to keystone set #> Unit 537 cannot be removed - adding to keystone set #> Unit 864 cannot be removed - adding to keystone set #> Unit 926 cannot be removed - adding to keystone set #> Whittling iteration 100 #> Whittling iteration 200 #> Whittling iteration 300 #> Whittling iteration 400 #> No more edge units to consider - terminating #> Calculating final statistics... #> MinPatch processing complete!"},{"path":"/articles/minpatchTasmania.html","id":"visualise-the-minpatch-solution","dir":"Articles","previous_headings":"MinPatch","what":"Visualise the minpatch solution","title":"MinPatch in Tasmania","text":"","code":"plot_minpatch(result, title = \"MinPatch Results\")"},{"path":"/articles/minpatchTasmania.html","id":"analyse-the-final-results","dir":"Articles","previous_headings":"MinPatch","what":"Analyse the final results","title":"MinPatch in Tasmania","text":"","code":"print_minpatch_summary(result) #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 58 (valid: 11) #> Final patches: 12 (valid: 11) #> Area change: 4378267698.40 (21.8%) #> #> Cost Breakdown: #> Planning unit cost: 68792497.20 #> Boundary cost: 0.00 #> Total cost: 68792497.20 #> Selected units: 427 #> #> Feature Representation: #> Total features: 33 #> Targets met: 33 #> Targets unmet: 0 #> Mean proportion: 0.449 #> Total shortfall: 0.00 #> #> #> === End Summary === # Compare original vs MinPatch solutions comparison <- compare_solutions(result) # Print overall comparison cat(\"=== Overall Solution Comparison ===\\n\") #> === Overall Solution Comparison === print(comparison$overall) #> Metric Original MinPatch Change Percent_Change #> 1 Selected Planning Units 330 427 97 29.39394 #> 2 Total Area 20052333083 24430600782 4378267698 21.83421 #> 3 Number of Patches 58 12 -46 -79.31034 #> 4 Valid Patches (>= min size) 11 11 0 0.00000 #> 5 Median Patch Size 64915437 661924221 597008784 919.67152 #> 6 Planning Unit Cost 68792497 68792497 0 0.00000 #> 7 Boundary Cost 0 0 0 NA #> 8 Total Cost 68792497 68792497 0 0.00000 # Print feature-level comparison cat(\"\\n=== Feature-Level Area Comparison ===\\n\") #> #> === Feature-Level Area Comparison === print(comparison$features) #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 1.2 2 4 2 100.000000 #> 2 2 33.0 35 39 4 11.428571 #> 3 3 2.7 3 6 3 100.000000 #> 4 4 170.1 177 197 20 11.299435 #> 5 5 213.9 221 260 39 17.647059 #> 6 6 242.1 250 317 67 26.800000 #> 7 7 9.9 12 13 1 8.333333 #> 8 8 108.3 113 137 24 21.238938 #> 9 9 30.9 41 53 12 29.268293 #> 10 10 260.4 271 339 68 25.092251 #> 11 11 62.4 67 82 15 22.388060 #> 12 12 132.0 151 190 39 25.827815 #> 13 13 133.5 138 163 25 18.115942 #> 14 14 122.4 128 137 9 7.031250 #> 15 15 62.4 66 76 10 15.151515 #> 16 16 240.0 246 310 64 26.016260 #> 17 17 0.3 1 1 0 0.000000 #> 18 18 27.6 29 29 0 0.000000 #> 19 19 6.6 8 8 0 0.000000 #> 20 20 18.0 19 25 6 31.578947 #> 21 21 24.3 31 41 10 32.258065 #> 22 22 7.5 12 13 1 8.333333 #> 23 23 34.5 39 54 15 38.461538 #> 24 24 11.1 12 12 0 0.000000 #> 25 25 47.1 51 77 26 50.980392 #> 26 26 9.3 11 11 0 0.000000 #> 27 27 86.7 88 102 14 15.909091 #> 28 28 6.6 7 12 5 71.428571 #> 29 29 1.2 2 2 0 0.000000 #> 30 30 186.0 192 195 3 1.562500 #> 31 31 66.3 71 97 26 36.619718 #> 32 32 30.9 36 49 13 36.111111 #> 33 33 57.9 60 67 7 11.666667 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.666667 #> 2 TRUE TRUE 1.060606 #> 3 TRUE TRUE 1.111111 #> 4 TRUE TRUE 1.040564 #> 5 TRUE TRUE 1.033193 #> 6 TRUE TRUE 1.032631 #> 7 TRUE TRUE 1.212121 #> 8 TRUE TRUE 1.043398 #> 9 TRUE TRUE 1.326861 #> 10 TRUE TRUE 1.040707 #> 11 TRUE TRUE 1.073718 #> 12 TRUE TRUE 1.143939 #> 13 TRUE TRUE 1.033708 #> 14 TRUE TRUE 1.045752 #> 15 TRUE TRUE 1.057692 #> 16 TRUE TRUE 1.025000 #> 17 TRUE TRUE 3.333333 #> 18 TRUE TRUE 1.050725 #> 19 TRUE TRUE 1.212121 #> 20 TRUE TRUE 1.055556 #> 21 TRUE TRUE 1.275720 #> 22 TRUE TRUE 1.600000 #> 23 TRUE TRUE 1.130435 #> 24 TRUE TRUE 1.081081 #> 25 TRUE TRUE 1.082803 #> 26 TRUE TRUE 1.182796 #> 27 TRUE TRUE 1.014994 #> 28 TRUE TRUE 1.060606 #> 29 TRUE TRUE 1.666667 #> 30 TRUE TRUE 1.032258 #> 31 TRUE TRUE 1.070890 #> 32 TRUE TRUE 1.165049 #> 33 TRUE TRUE 1.036269 #> MinPatch_Proportion #> 1 3.333333 #> 2 1.181818 #> 3 2.222222 #> 4 1.158142 #> 5 1.215521 #> 6 1.309376 #> 7 1.313131 #> 8 1.265005 #> 9 1.715210 #> 10 1.301843 #> 11 1.314103 #> 12 1.439394 #> 13 1.220974 #> 14 1.119281 #> 15 1.217949 #> 16 1.291667 #> 17 3.333333 #> 18 1.050725 #> 19 1.212121 #> 20 1.388889 #> 21 1.687243 #> 22 1.733333 #> 23 1.565217 #> 24 1.081081 #> 25 1.634820 #> 26 1.182796 #> 27 1.176471 #> 28 1.818182 #> 29 1.666667 #> 30 1.048387 #> 31 1.463047 #> 32 1.585761 #> 33 1.157168 # Print summary statistics cat(\"\\n=== Feature Change Summary ===\\n\") #> #> === Feature Change Summary === print(comparison$summary) #> features_improved features_reduced features_unchanged targets_gained #> 1 27 0 6 0 #> targets_lost #> 1 0"},{"path":"/articles/minpatchTasmania.html","id":"run-different-patch-sizes","dir":"Articles","previous_headings":"","what":"Run different patch sizes","title":"MinPatch in Tasmania","text":"minimum patch size parameter core constraint drives MinPatch behaviour - determines threshold patches considered small must either enlarged removed. Stage 1, MinPatch removes patches smaller threshold (except existing protected areas). Stage 2, adds new patches large enough meet minimum targets unmet. Stage 3 (whittling), prevents removal planning units make patch fall threshold. Larger minimum patch sizes result fewer, bigger patches potentially higher total area, MinPatch must ensure every patch meets size requirement. Smaller minimum patch sizes allow flexibility, potentially resulting patches closer original prioritizr solution. choice minimum patch size reflect ecological management considerations - example, larger patches may needed support viable populations reduce edge effects, smaller patches may acceptable highly connected landscapes features don’t require large contiguous areas.","code":"# Calculate reasonable parameters based on planning unit characteristics median_area <- median(st_area(tas)) min_patch_size <- median_area * 10 patch_radius <- sqrt(median_area * 10) result2 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE ) median_area <- median(st_area(tas)) min_patch_size <- median_area * 20 patch_radius <- sqrt(median_area * 10) result3 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE )"},{"path":"/articles/minpatchTasmania.html","id":"visualise-the-minpatch-solution-1","dir":"Articles","previous_headings":"Run different patch sizes","what":"Visualise the minpatch solution","title":"MinPatch in Tasmania","text":"","code":"patchwork::wrap_plots(plot_minpatch(result, title = \"Patch Size x5\"), plot_minpatch(result2, title = \"Patch Size x10\"), plot_minpatch(result3, title = \"Patch Size x20\"), guides = \"collect\", ncol = 3) & theme(legend.position = \"bottom\")"},{"path":"/articles/minpatchTasmania.html","id":"run-different-boundary-penalties","dir":"Articles","previous_headings":"","what":"Run different Boundary Penalties","title":"MinPatch in Tasmania","text":"boundary penalty controls much MinPatch prioritizes spatial compactness “simulated whittling” stage. whittling, MinPatch considers removing planning units patch edges, doesn’t increase total priortizr cost. boundary penalty affects decision penalizing fragmented solutions - higher penalties favour compact patches making costly create additional “edge” selected unselected areas. MinPatch evaluates whether remove unit, calculates change boundary length (units selected neighbours increase boundary removed, units unselected neighbours decrease boundary) multiplies boundary penalty. resulting boundary cost change exceeds unit’s cost, unit removed. datasets like Tasmania long planning unit boundaries relative unit costs, even small boundary penalties can highly influential, potentially preventing unit removals resulting similar solutions across different penalty values. small penalties (e.g., 1e-10) may needed see meaningful differences cases.","code":"# Calculate reasonable parameters based on planning unit characteristics median_area <- median(st_area(tas)) min_patch_size <- median_area * 5 patch_radius <- sqrt(median_area * 10) result4 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 1e-5, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE ) result5 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 1e-10, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE )"},{"path":"/articles/minpatchTasmania.html","id":"visualise-the-minpatch-solution-2","dir":"Articles","previous_headings":"Run different Boundary Penalties","what":"Visualise the minpatch solution","title":"MinPatch in Tasmania","text":"","code":"patchwork::wrap_plots(plot_minpatch(result, title = \"Boundary Penalty: 0\"), plot_minpatch(result4, title = \"Boundary Penalty: 1e-5\"), plot_minpatch(result5, title = \"Boundary Penalty: 1e-10\"), guides = \"collect\", ncol = 3) & theme(legend.position = \"bottom\")"},{"path":"/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"Jason D. Everett. Author, maintainer. Anthony J. Richardson. Author. Robert J. Smith. Author. Original MinPatch algorithm author","code":""},{"path":"/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Everett J, Richardson , Smith R (2025). minpatch: Post-Processing Conservation Planning Solutions Ensure Minimum Patch Sizes. R package version 0.1.0, https://github.com/SpatialPlanning/minpatch.","code":"@Manual{, title = {minpatch: Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes}, author = {Jason D. Everett and Anthony J. Richardson and Robert J. Smith}, year = {2025}, note = {R package version 0.1.0}, url = {https://github.com/SpatialPlanning/minpatch}, }"},{"path":"/index.html","id":"minpatch-for-r","dir":"","previous_headings":"","what":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Note: still work progress significant bugs may present. Use caution information original implemention MinPatch Marxan QGIS available : https://cluz-systematic-conservation-planning.github.io","code":""},{"path":"/index.html","id":"overview","dir":"","previous_headings":"","what":"Overview","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"R implementation MinPatch algorithm post-processing conservation planning solutions ensure minimum protected area sizes. MinPatch post-processing tool conservation planning solutions ensures protected areas meet user-defined minimum size thresholds. R package implements methodology described Smith et al. (2010) designed work solutions prioritizr package, though can work binary conservation solution.","code":""},{"path":"/index.html","id":"the-problem","dir":"","previous_headings":"Overview","what":"The Problem","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Conservation planning software like Marxan prioritizr can produce solutions many small, fragmented protected areas. solutions may mathematically optimal, small protected areas often: Less ecologically viable expensive manage vulnerable edge effects Less resilient disturbances","code":""},{"path":"/index.html","id":"the-solution","dir":"","previous_headings":"Overview","what":"The Solution","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"MinPatch addresses post-processing conservation solutions three stages: Remove Small Patches: Eliminate protected areas smaller minimum size threshold Add New Patches: Add new areas meet conservation targets using BestPatch algorithm Simulated Whittling: Remove unnecessary planning units maintaining constraints","code":""},{"path":"/index.html","id":"installation","dir":"","previous_headings":"","what":"Installation","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"","code":"# Install from GitHub pak::pak(\"SpatialPlanning/minpatch\")"},{"path":"/index.html","id":"key-features","dir":"","previous_headings":"","what":"Key Features","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Full MinPatch Algorithm: Complete implementation three stages prioritizr Integration: Seamless workflow prioritizr solutions Locked Constraints Support: Automatically respects locked-locked-constraints prioritizr Flexible Parameters: Control minimum patch sizes, patch radius, boundary penalties Comprehensive Reporting: Detailed statistics comparisons Visualization Support: Plot results ggplot2 (optional)","code":""},{"path":[]},{"path":"/index.html","id":"locked-constraints","dir":"","previous_headings":"Algorithm Details","what":"Locked Constraints","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"MinPatch automatically respects locked-locked-constraints prioritizr problems: Locked-constraints (add_locked_in_constraints()): Planning units locked-never removed, regardless patch size whittling stage. units treated “conserved” areas must retained final solution. Locked-constraints (add_locked_out_constraints()): Planning units locked-never selected, even adding new patches meet conservation targets. units completely excluded consideration. locked-units form patches smaller min_patch_size, warning issued, units still preserved solution.","code":""},{"path":"/index.html","id":"stage-1-remove-small-patches","dir":"","previous_headings":"Algorithm Details","what":"Stage 1: Remove Small Patches","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Identifies connected components (patches) solution removes smaller minimum size threshold. Locked-planning units never removed, even form small patches.","code":""},{"path":"/index.html","id":"stage-2-add-new-patches-bestpatch-algorithm","dir":"","previous_headings":"Algorithm Details","what":"Stage 2: Add New Patches (BestPatch Algorithm)","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Uses BestPatch scoring system add new patches: Calculate current conservation levels feature Identify features unmet targets Score potential patches based contribution targets relative cost Add highest-scoring patch repeat BestPatch score calculated :","code":"Score = Σ(feature_contribution / target_gap) / patch_cost"},{"path":"/index.html","id":"stage-3-simulated-whittling","dir":"","previous_headings":"Algorithm Details","what":"Stage 3: Simulated Whittling","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Removes unnecessary planning units iterative process: Identify edge units (boundary selected areas) Calculate whittling scores based feature importance Must cause targets unmet Must make patches small Must increase total cost (boundary penalty > 0) Must split patches non-viable pieces","code":""},{"path":"/index.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"use package, please cite original paper implementation:","code":"Smith, R.J., Di Minin, E., Linke, S., Segan, D.B., Possingham, H.P. (2010). An approach for ensuring minimum protected area size in systematic conservation planning. Biological Conservation, 143(10), 2525-2531. Everett J.D., Richardson A.J., Smith R.J. (2025). _minpatch: Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes_. R package version 0.1.0,
."},{"path":"/index.html","id":"license","dir":"","previous_headings":"","what":"License","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"GPL (>= 3)","code":""},{"path":"/index.html","id":"references","dir":"","previous_headings":"","what":"References","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Smith, R.J., Di Minin, E., Linke, S., Segan, D.B., Possingham, H.P. (2010). approach ensuring minimum protected area size systematic conservation planning. Biological Conservation, 143(10), 2525-2531.","code":""},{"path":"/index.html","id":"getting-help","dir":"","previous_headings":"","what":"Getting Help","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Check package vignette: vignette(\"minpatch\") View function documentation: ?run_minpatch Report bugs: GitHub Issues","code":""},{"path":"/reference/add_new_patches.html","id":null,"dir":"Reference","previous_headings":"","what":"Add new patches to meet conservation targets — add_new_patches","title":"Add new patches to meet conservation targets — add_new_patches","text":"Stage 2 MinPatch: Add new patches using BestPatch algorithm","code":""},{"path":"/reference/add_new_patches.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add new patches to meet conservation targets — add_new_patches","text":"","code":"add_new_patches(minpatch_data, verbose = TRUE)"},{"path":"/reference/add_new_patches.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add new patches to meet conservation targets — add_new_patches","text":"minpatch_data List containing MinPatch data structures (including prioritizr objects) verbose Logical, whether print progress","code":""},{"path":"/reference/add_new_patches.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add new patches to meet conservation targets — add_new_patches","text":"Updated minpatch_data new patches added","code":""},{"path":"/reference/add_patch_centered_on_unit.html","id":null,"dir":"Reference","previous_headings":"","what":"Add patch centered on specified planning unit — add_patch_centered_on_unit","title":"Add patch centered on specified planning unit — add_patch_centered_on_unit","text":"Add patch centered specified planning unit","code":""},{"path":"/reference/add_patch_centered_on_unit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add patch centered on specified planning unit — add_patch_centered_on_unit","text":"","code":"add_patch_centered_on_unit(minpatch_data, center_unit_id)"},{"path":"/reference/add_patch_centered_on_unit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add patch centered on specified planning unit — add_patch_centered_on_unit","text":"minpatch_data List containing MinPatch data structures center_unit_id ID unit center patch ","code":""},{"path":"/reference/add_patch_centered_on_unit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add patch centered on specified planning unit — add_patch_centered_on_unit","text":"Updated unit_dict new patch added","code":""},{"path":"/reference/calculate_best_patch_scores.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","title":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","text":"Implements BestPatch scoring algorithm original paper","code":""},{"path":"/reference/calculate_best_patch_scores.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","text":"","code":"calculate_best_patch_scores(minpatch_data, feature_amounts, unmet_targets)"},{"path":"/reference/calculate_best_patch_scores.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","text":"minpatch_data List containing MinPatch data structures feature_amounts Named vector current conservation amounts unmet_targets Character vector features unmet targets","code":""},{"path":"/reference/calculate_best_patch_scores.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","text":"Named vector BestPatch scores","code":""},{"path":"/reference/calculate_cost_summary.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","title":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","text":"Calculates various cost components using prioritizr functions possible","code":""},{"path":"/reference/calculate_cost_summary.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","text":"","code":"calculate_cost_summary(minpatch_data)"},{"path":"/reference/calculate_cost_summary.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/calculate_cost_summary.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","text":"List containing detailed cost breakdown","code":""},{"path":"/reference/calculate_feature_conservation.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate current feature conservation amounts — calculate_feature_conservation","title":"Calculate current feature conservation amounts — calculate_feature_conservation","text":"Calculates much feature currently conserved","code":""},{"path":"/reference/calculate_feature_conservation.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate current feature conservation amounts — calculate_feature_conservation","text":"","code":"calculate_feature_conservation(minpatch_data)"},{"path":"/reference/calculate_feature_conservation.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate current feature conservation amounts — calculate_feature_conservation","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/calculate_feature_conservation.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate current feature conservation amounts — calculate_feature_conservation","text":"Named vector conserved amounts feature","code":""},{"path":"/reference/calculate_feature_representation.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate feature representation in solution — calculate_feature_representation","title":"Calculate feature representation in solution — calculate_feature_representation","text":"Calculates much conservation feature represented current solution using prioritizr functions possible","code":""},{"path":"/reference/calculate_feature_representation.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate feature representation in solution — calculate_feature_representation","text":"","code":"calculate_feature_representation(minpatch_data)"},{"path":"/reference/calculate_feature_representation.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate feature representation in solution — calculate_feature_representation","text":"minpatch_data List containing MinPatch data structures including prioritizr objects","code":""},{"path":"/reference/calculate_feature_representation.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate feature representation in solution — calculate_feature_representation","text":"Data frame feature representation statistics","code":""},{"path":"/reference/calculate_patch_stats.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate patch statistics — calculate_patch_stats","title":"Calculate patch statistics — calculate_patch_stats","text":"Calculates summary statistics patches including areas counts","code":""},{"path":"/reference/calculate_patch_stats.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate patch statistics — calculate_patch_stats","text":"","code":"calculate_patch_stats(minpatch_data)"},{"path":"/reference/calculate_patch_stats.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate patch statistics — calculate_patch_stats","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/calculate_patch_stats.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate patch statistics — calculate_patch_stats","text":"Updated minpatch_data patch statistics added","code":""},{"path":"/reference/calculate_whittle_scores.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate whittling scores for edge units — calculate_whittle_scores","title":"Calculate whittling scores for edge units — calculate_whittle_scores","text":"Calculates \"Low Relevance\" score edge unit based feature importance (Equation A2 original paper)","code":""},{"path":"/reference/calculate_whittle_scores.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate whittling scores for edge units — calculate_whittle_scores","text":"","code":"calculate_whittle_scores(edge_units, minpatch_data)"},{"path":"/reference/calculate_whittle_scores.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate whittling scores for edge units — calculate_whittle_scores","text":"edge_units Character vector edge unit IDs minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/calculate_whittle_scores.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate whittling scores for edge units — calculate_whittle_scores","text":"Named vector whittling scores","code":""},{"path":"/reference/can_remove_unit.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if a planning unit can be removed — can_remove_unit","title":"Check if a planning unit can be removed — can_remove_unit","text":"Checks multiple criteria determine removing unit acceptable: 1. violate conservation targets 2. make patch small 3. increase total cost 4. split patches non-viable pieces","code":""},{"path":"/reference/can_remove_unit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if a planning unit can be removed — can_remove_unit","text":"","code":"can_remove_unit(unit_id, minpatch_data)"},{"path":"/reference/can_remove_unit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if a planning unit can be removed — can_remove_unit","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/can_remove_unit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if a planning unit can be removed — can_remove_unit","text":"Logical indicating unit can removed","code":""},{"path":"/reference/compare_solutions.html","id":null,"dir":"Reference","previous_headings":"","what":"Compare solutions before and after MinPatch — compare_solutions","title":"Compare solutions before and after MinPatch — compare_solutions","text":"Creates comprehensive comparison key metrics original MinPatch solutions, including overall statistics detailed feature-level analysis","code":""},{"path":"/reference/compare_solutions.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Compare solutions before and after MinPatch — compare_solutions","text":"","code":"compare_solutions(minpatch_result)"},{"path":"/reference/compare_solutions.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Compare solutions before and after MinPatch — compare_solutions","text":"minpatch_result Result run_minpatch function","code":""},{"path":"/reference/compare_solutions.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Compare solutions before and after MinPatch — compare_solutions","text":"List containing: overall: Data frame overall solution comparison features: Data frame feature-level area comparisons summary: List summary statistics feature changes","code":""},{"path":"/reference/compare_solutions.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Compare solutions before and after MinPatch — compare_solutions","text":"","code":"library(prioritizr) library(sf) #> Linking to GEOS 3.13.0, GDAL 3.8.5, PROJ 9.5.1; sf_use_s2() is TRUE library(terra) #> terra 1.8.80 # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, verbose = FALSE ) # Compare solutions comparison <- compare_solutions(result) # Print overall comparison print(comparison$overall) #> Metric Original MinPatch Change Percent_Change #> 1 Selected Planning Units 16.000 21.000 5.000 31.25000 #> 2 Total Area 0.160 0.210 0.050 31.25000 #> 3 Number of Patches 7.000 4.000 -3.000 -42.85714 #> 4 Valid Patches (>= min size) 1.000 2.000 1.000 100.00000 #> 5 Median Patch Size 0.010 0.055 0.045 450.00000 #> 6 Planning Unit Cost 4137.804 4137.804 0.000 0.00000 #> 7 Boundary Cost 0.000 0.000 0.000 NA #> 8 Total Cost 4137.804 4137.804 0.000 0.00000 # Print feature-level comparison print(comparison$features) #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 12.670220 14.083429 18.554865 4.4714362 31.74963 #> 2 2 4.774965 5.124808 6.090809 0.9660007 18.84950 #> 3 3 11.029225 11.707674 15.121749 3.4140748 29.16100 #> 4 4 6.489033 6.863962 8.676751 1.8127892 26.41025 #> 5 5 8.613574 9.482534 12.907049 3.4245152 36.11393 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.111538 #> 2 TRUE TRUE 1.073266 #> 3 TRUE TRUE 1.061514 #> 4 TRUE TRUE 1.057779 #> 5 TRUE TRUE 1.100883 #> MinPatch_Proportion #> 1 1.464447 #> 2 1.275571 #> 3 1.371062 #> 4 1.337141 #> 5 1.498455 # Print summary statistics cat(\"Features improved:\", comparison$summary$features_improved, \"\\n\") #> Features improved: 5 cat(\"Targets gained:\", comparison$summary$targets_gained, \"\\n\") #> Targets gained: 0"},{"path":"/reference/create_abundance_matrix.html","id":null,"dir":"Reference","previous_headings":"","what":"Create abundance matrix from planning units — create_abundance_matrix","title":"Create abundance matrix from planning units — create_abundance_matrix","text":"Creates matrix showing amount feature planning unit extracting feature columns directly planning_units using prioritizr problem","code":""},{"path":"/reference/create_abundance_matrix.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create abundance matrix from planning units — create_abundance_matrix","text":"","code":"create_abundance_matrix(planning_units, prioritizr_problem)"},{"path":"/reference/create_abundance_matrix.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create abundance matrix from planning units — create_abundance_matrix","text":"planning_units sf object planning unit geometries feature columns prioritizr_problem prioritizr problem object get feature names","code":""},{"path":"/reference/create_abundance_matrix.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create abundance matrix from planning units — create_abundance_matrix","text":"Named list planning unit contains feature abundances","code":""},{"path":"/reference/create_boundary_matrix.html","id":null,"dir":"Reference","previous_headings":"","what":"Create boundary matrix from planning units — create_boundary_matrix","title":"Create boundary matrix from planning units — create_boundary_matrix","text":"Creates matrix shared boundary lengths adjacent planning units","code":""},{"path":"/reference/create_boundary_matrix.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create boundary matrix from planning units — create_boundary_matrix","text":"","code":"create_boundary_matrix(planning_units, verbose = TRUE)"},{"path":"/reference/create_boundary_matrix.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create boundary matrix from planning units — create_boundary_matrix","text":"planning_units sf object planning unit geometries","code":""},{"path":"/reference/create_boundary_matrix.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create boundary matrix from planning units — create_boundary_matrix","text":"Named list element contains neighbors shared boundary lengths","code":""},{"path":"/reference/create_patch_radius_dict.html","id":null,"dir":"Reference","previous_headings":"","what":"Create patch radius dictionary — create_patch_radius_dict","title":"Create patch radius dictionary — create_patch_radius_dict","text":"planning unit, find units within specified patch radius","code":""},{"path":"/reference/create_patch_radius_dict.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create patch radius dictionary — create_patch_radius_dict","text":"","code":"create_patch_radius_dict(planning_units, patch_radius, verbose = TRUE)"},{"path":"/reference/create_patch_radius_dict.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create patch radius dictionary — create_patch_radius_dict","text":"planning_units sf object planning unit geometries patch_radius radius patch creation","code":""},{"path":"/reference/create_patch_radius_dict.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create patch radius dictionary — create_patch_radius_dict","text":"Named list planning unit contains list units within radius","code":""},{"path":"/reference/create_solution_vector.html","id":null,"dir":"Reference","previous_headings":"","what":"Create solution vector from unit dictionary — create_solution_vector","title":"Create solution vector from unit dictionary — create_solution_vector","text":"Converts internal unit dictionary back binary solution vector","code":""},{"path":"/reference/create_solution_vector.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create solution vector from unit dictionary — create_solution_vector","text":"","code":"create_solution_vector(unit_dict)"},{"path":"/reference/create_solution_vector.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create solution vector from unit dictionary — create_solution_vector","text":"unit_dict Named list containing cost status planning unit","code":""},{"path":"/reference/create_solution_vector.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create solution vector from unit dictionary — create_solution_vector","text":"Binary numeric vector indicating selected planning units","code":""},{"path":"/reference/find_edge_units.html","id":null,"dir":"Reference","previous_headings":"","what":"Find edge planning units — find_edge_units","title":"Find edge planning units — find_edge_units","text":"Identifies planning units edge selected areas (least one unselected neighbor)","code":""},{"path":"/reference/find_edge_units.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Find edge planning units — find_edge_units","text":"","code":"find_edge_units(minpatch_data)"},{"path":"/reference/find_edge_units.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Find edge planning units — find_edge_units","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/find_edge_units.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Find edge planning units — find_edge_units","text":"Character vector edge unit IDs","code":""},{"path":"/reference/generate_minpatch_report.html","id":null,"dir":"Reference","previous_headings":"","what":"Generate comprehensive MinPatch report — generate_minpatch_report","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"Creates detailed report MinPatch processing results","code":""},{"path":"/reference/generate_minpatch_report.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"","code":"generate_minpatch_report(minpatch_result)"},{"path":"/reference/generate_minpatch_report.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"minpatch_result Result object run_minpatch function","code":""},{"path":"/reference/generate_minpatch_report.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"List containing formatted report components","code":""},{"path":"/reference/generate_minpatch_report.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, verbose = FALSE ) generate_minpatch_report(result) #> $features #> # A tibble: 5 × 9 #> feature met total_amount absolute_target absolute_held absolute_shortfall #> #> 1 feature_1 TRUE 74.5 12.7 18.6 0 #> 2 feature_2 TRUE 28.1 4.77 6.09 0 #> 3 feature_3 TRUE 64.9 11.0 15.1 0 #> 4 feature_4 TRUE 38.2 6.49 8.68 0 #> 5 feature_5 TRUE 50.7 8.61 12.9 0 #> # ℹ 3 more variables: relative_target , relative_held , #> # relative_shortfall #> #> $patch_stats #> time all_patch_count all_patch_area median_all_patch valid_patch_count #> 1 initial 7 0.16 0.010 1 #> 2 final 4 0.21 0.055 2 #> valid_patch_area median_valid_patch #> 1 0.05 0.050 #> 2 0.15 0.075 #> #> $cost #> # A tibble: 1 × 6 #> summary cost n boundary_length boundary_cost total_cost #> #> 1 overall 4138. 21 0 0 4138. #>"},{"path":"/reference/identify_unmet_targets.html","id":null,"dir":"Reference","previous_headings":"","what":"Identify features with unmet targets — identify_unmet_targets","title":"Identify features with unmet targets — identify_unmet_targets","text":"Uses prioritizr functions identify unmet targets minpatch_data","code":""},{"path":"/reference/identify_unmet_targets.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Identify features with unmet targets — identify_unmet_targets","text":"","code":"identify_unmet_targets(minpatch_data)"},{"path":"/reference/identify_unmet_targets.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Identify features with unmet targets — identify_unmet_targets","text":"minpatch_data List containing MinPatch data structures including prioritizr objects","code":""},{"path":"/reference/identify_unmet_targets.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Identify features with unmet targets — identify_unmet_targets","text":"Character vector feature IDs unmet targets","code":""},{"path":"/reference/initialize_minpatch_data.html","id":null,"dir":"Reference","previous_headings":"","what":"Initialize MinPatch data structures — initialize_minpatch_data","title":"Initialize MinPatch data structures — initialize_minpatch_data","text":"Creates internal data structures needed MinPatch processing","code":""},{"path":"/reference/initialize_minpatch_data.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Initialize MinPatch data structures — initialize_minpatch_data","text":"","code":"initialize_minpatch_data( solution, planning_units, targets, costs, min_patch_size, patch_radius, boundary_penalty, prioritizr_problem, prioritizr_solution, verbose = TRUE )"},{"path":"/reference/initialize_minpatch_data.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Initialize MinPatch data structures — initialize_minpatch_data","text":"solution Binary solution vector planning_units sf object planning units targets data.frame targets costs numeric vector costs min_patch_size minimum patch size patch_radius patch radius boundary_penalty Boundary penalty value prioritizr_problem prioritizr problem object prioritizr_solution solved prioritizr solution object","code":""},{"path":"/reference/initialize_minpatch_data.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Initialize MinPatch data structures — initialize_minpatch_data","text":"List containing necessary data structures","code":""},{"path":"/reference/make_patch_dict.html","id":null,"dir":"Reference","previous_headings":"","what":"Create patch dictionary from unit dictionary — make_patch_dict","title":"Create patch dictionary from unit dictionary — make_patch_dict","text":"Identifies connected components (patches) current solution","code":""},{"path":"/reference/make_patch_dict.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create patch dictionary from unit dictionary — make_patch_dict","text":"","code":"make_patch_dict(minpatch_data)"},{"path":"/reference/make_patch_dict.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create patch dictionary from unit dictionary — make_patch_dict","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/make_patch_dict.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create patch dictionary from unit dictionary — make_patch_dict","text":"Named list patch contains area, unit count, unit IDs","code":""},{"path":"/reference/minpatch-package.html","id":null,"dir":"Reference","previous_headings":"","what":"MinPatch for R: Post-processing prioritizr solutions to ensure minimum patch sizes — minpatch-package","title":"MinPatch for R: Post-processing prioritizr solutions to ensure minimum patch sizes — minpatch-package","text":"package provides functions post-process conservation planning solutions prioritizr ensure protected areas meet user-defined minimum size thresholds, following methodology described Smith et al. (2010).","code":""},{"path":[]},{"path":"/reference/minpatch-package.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"MinPatch for R: Post-processing prioritizr solutions to ensure minimum patch sizes — minpatch-package","text":"Maintainer: Jason D. Everett DrJasonEverett@gmail.com (ORCID) Authors: Anthony J. Richardson .richardson1@uq.edu.au (ORCID) Robert J. Smith (Original MinPatch algorithm author)","code":""},{"path":"/reference/pipe.html","id":null,"dir":"Reference","previous_headings":"","what":"Pipe operator — %>%","title":"Pipe operator — %>%","text":"See magrittr::%>% details.","code":""},{"path":"/reference/pipe.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Pipe operator — %>%","text":"","code":"lhs %>% rhs"},{"path":"/reference/pipe.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Pipe operator — %>%","text":"lhs value magrittr placeholder. rhs function call using magrittr semantics.","code":""},{"path":"/reference/pipe.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Pipe operator — %>%","text":"result calling `rhs(lhs)`.","code":""},{"path":"/reference/plot_minpatch.html","id":null,"dir":"Reference","previous_headings":"","what":"Visualize MinPatch results — plot_minpatch","title":"Visualize MinPatch results — plot_minpatch","text":"Creates simple visualization MinPatch results showing original vs. modified solutions","code":""},{"path":"/reference/plot_minpatch.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Visualize MinPatch results — plot_minpatch","text":"","code":"plot_minpatch(minpatch_result, title = \"MinPatch Results\")"},{"path":"/reference/plot_minpatch.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Visualize MinPatch results — plot_minpatch","text":"minpatch_result Result run_minpatch function title Plot title (optional)","code":""},{"path":"/reference/plot_minpatch.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Visualize MinPatch results — plot_minpatch","text":"ggplot object (ggplot2 available)","code":""},{"path":"/reference/plot_minpatch.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Visualize MinPatch results — plot_minpatch","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, verbose = FALSE ) # Visualize results plot_minpatch(result)"},{"path":"/reference/plot_prioritizr.html","id":null,"dir":"Reference","previous_headings":"","what":"Visualize prioritizr solutions — plot_prioritizr","title":"Visualize prioritizr solutions — plot_prioritizr","text":"Creates simple visualization prioritizr solutions showing selected unselected planning units using ggplot2","code":""},{"path":"/reference/plot_prioritizr.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Visualize prioritizr solutions — plot_prioritizr","text":"","code":"plot_prioritizr(s, col = \"solution_1\", title = \"prioritizr Solution\")"},{"path":"/reference/plot_prioritizr.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Visualize prioritizr solutions — plot_prioritizr","text":"s sf object containing planning units solution data col Column name containing solution values (default = \"solution_1\") title Plot title (default = \"prioritizr Solution\")","code":""},{"path":"/reference/plot_prioritizr.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Visualize prioritizr solutions — plot_prioritizr","text":"ggplot object showing spatial solution","code":""},{"path":"/reference/plot_prioritizr.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Visualize prioritizr solutions — plot_prioritizr","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Plot the solution plot_prioritizr(s) # Plot with custom title and column plot_prioritizr(s, col = \"solution_1\", title = \"My Conservation Plan\")"},{"path":"/reference/print_minpatch_summary.html","id":null,"dir":"Reference","previous_headings":"","what":"Print MinPatch results summary — print_minpatch_summary","title":"Print MinPatch results summary — print_minpatch_summary","text":"Prints formatted summary MinPatch processing results","code":""},{"path":"/reference/print_minpatch_summary.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Print MinPatch results summary — print_minpatch_summary","text":"","code":"print_minpatch_summary(minpatch_result)"},{"path":"/reference/print_minpatch_summary.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Print MinPatch results summary — print_minpatch_summary","text":"minpatch_result Result object run_minpatch function","code":""},{"path":"/reference/print_minpatch_summary.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Print MinPatch results summary — print_minpatch_summary","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 5 #> Unmet feature IDs: 1, 2, 3, 4, 5 #> Iteration 1 - Unmet targets: 5 #> Found 85 potential patches with scores #> Best score: 0.002500108 for unit 90 #> Added patch centered on unit 90 #> Iteration 2 - Unmet targets: 4 #> Found 76 potential patches with scores #> Best score: 0.002060928 for unit 78 #> Added patch centered on unit 78 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 21 #> Keystone units: 0 #> New keystone units: 0 #> Scoreable units: 21 #> Unit 90 cannot be removed - adding to keystone set #> Edge units found: 20 #> Keystone units: 1 #> New keystone units: 0 #> Scoreable units: 20 #> Unit 81 cannot be removed - adding to keystone set #> Edge units found: 19 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 19 #> Unit 80 cannot be removed - adding to keystone set #> Edge units found: 18 #> Keystone units: 3 #> New keystone units: 0 #> Scoreable units: 18 #> Unit 89 cannot be removed - adding to keystone set #> Edge units found: 17 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 17 #> Unit 73 cannot be removed - adding to keystone set #> Unit 79 cannot be removed - adding to keystone set #> Unit 72 cannot be removed - adding to keystone set #> Unit 88 cannot be removed - adding to keystone set #> Unit 87 cannot be removed - adding to keystone set #> Unit 64 cannot be removed - adding to keystone set #> No more edge units to consider - terminating #> Calculating final statistics... #> MinPatch processing complete! print_minpatch_summary(result) #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 7 (valid: 1) #> Final patches: 4 (valid: 2) #> Area change: 0.05 (31.2%) #> #> Cost Breakdown: #> Planning unit cost: 4137.80 #> Boundary cost: 0.00 #> Total cost: 4137.80 #> Selected units: 21 #> #> Feature Representation: #> Total features: 5 #> Targets met: 5 #> Targets unmet: 0 #> Mean proportion: 0.236 #> Total shortfall: 0.00 #> #> #> === End Summary ==="},{"path":"/reference/removal_increases_cost.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would increase cost — removal_increases_cost","title":"Check if removing unit would increase cost — removal_increases_cost","text":"Check removing unit increase cost","code":""},{"path":"/reference/removal_increases_cost.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would increase cost — removal_increases_cost","text":"","code":"removal_increases_cost(unit_id, minpatch_data)"},{"path":"/reference/removal_increases_cost.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would increase cost — removal_increases_cost","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_increases_cost.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would increase cost — removal_increases_cost","text":"Logical indicating removal increase cost","code":""},{"path":"/reference/removal_increases_marxan_cost.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","title":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","text":"Check removing unit increase Marxan cost","code":""},{"path":"/reference/removal_increases_marxan_cost.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","text":"","code":"removal_increases_marxan_cost(unit_id, minpatch_data)"},{"path":"/reference/removal_increases_marxan_cost.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_increases_marxan_cost.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","text":"Logical indicating removal increase cost","code":""},{"path":"/reference/removal_makes_patch_too_small.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","title":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","text":"Check removing unit make patch small","code":""},{"path":"/reference/removal_makes_patch_too_small.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","text":"","code":"removal_makes_patch_too_small(unit_id, minpatch_data)"},{"path":"/reference/removal_makes_patch_too_small.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_makes_patch_too_small.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","text":"Logical indicating removal make patch small","code":""},{"path":"/reference/removal_splits_patch_nonviably.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","title":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","text":"Check removing unit split patch non-viable pieces","code":""},{"path":"/reference/removal_splits_patch_nonviably.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","text":"","code":"removal_splits_patch_nonviably(unit_id, minpatch_data)"},{"path":"/reference/removal_splits_patch_nonviably.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_splits_patch_nonviably.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","text":"Logical indicating removal create non-viable patches","code":""},{"path":"/reference/removal_violates_targets.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would violate conservation targets — removal_violates_targets","title":"Check if removing unit would violate conservation targets — removal_violates_targets","text":"Check removing unit violate conservation targets","code":""},{"path":"/reference/removal_violates_targets.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would violate conservation targets — removal_violates_targets","text":"","code":"removal_violates_targets(unit_id, minpatch_data)"},{"path":"/reference/removal_violates_targets.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would violate conservation targets — removal_violates_targets","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_violates_targets.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would violate conservation targets — removal_violates_targets","text":"Logical indicating removal violate targets","code":""},{"path":"/reference/remove_small_patches_from_solution.html","id":null,"dir":"Reference","previous_headings":"","what":"Remove small patches from solution — remove_small_patches_from_solution","title":"Remove small patches from solution — remove_small_patches_from_solution","text":"Stage 1 MinPatch: Remove patches smaller minimum size threshold","code":""},{"path":"/reference/remove_small_patches_from_solution.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Remove small patches from solution — remove_small_patches_from_solution","text":"","code":"remove_small_patches_from_solution(minpatch_data)"},{"path":"/reference/remove_small_patches_from_solution.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Remove small patches from solution — remove_small_patches_from_solution","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/remove_small_patches_from_solution.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Remove small patches from solution — remove_small_patches_from_solution","text":"Updated minpatch_data small patches removed","code":""},{"path":"/reference/run_minpatch.html","id":null,"dir":"Reference","previous_headings":"","what":"Run MinPatch algorithm on prioritizr solution — run_minpatch","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"main function applies MinPatch algorithm prioritizr solution ensure protected areas meet minimum size thresholds. function uses prioritizr summary functions possible reduce code duplication ensure consistency prioritizr calculations.","code":""},{"path":"/reference/run_minpatch.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"","code":"run_minpatch( prioritizr_problem, prioritizr_solution, min_patch_size, patch_radius, boundary_penalty = 0, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, solution_column = \"solution_1\", verbose = TRUE )"},{"path":"/reference/run_minpatch.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"prioritizr_problem prioritizr problem object prioritizr_solution solved prioritizr solution object min_patch_size Minimum patch size threshold patch_radius Radius adding new patches boundary_penalty Boundary penalty value (default = 0) remove_small_patches Logical, whether remove small patches (Stage 1, default = TRUE) add_patches Logical, whether add new patches meet targets (Stage 2, default = TRUE) whittle_patches Logical, whether remove unnecessary units (Stage 3, default = TRUE) solution_column Name solution column (default = \"solution_1\") verbose Logical, whether print progress (default = TRUE)","code":""},{"path":"/reference/run_minpatch.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"MinPatch result object enhanced reporting using prioritizr functions","code":""},{"path":"/reference/run_minpatch.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"MinPatch algorithm consists three stages: Remove small patches: Removes patches smaller min_patch_size Add new patches: Adds patches meet conservation targets Whittle patches: Removes unnecessary planning units **Important**: set remove_small_patches = TRUE add_patches = FALSE, algorithm may remove patches without compensating, potentially violating conservation targets. cases, warning issued. Consider using add_patches = TRUE smaller min_patch_size maintain target achievement.","code":""},{"path":"/reference/run_minpatch.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Apply MinPatch with all stages result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 5 #> Unmet feature IDs: 1, 2, 3, 4, 5 #> Iteration 1 - Unmet targets: 5 #> Found 85 potential patches with scores #> Best score: 0.002500108 for unit 90 #> Added patch centered on unit 90 #> Iteration 2 - Unmet targets: 4 #> Found 76 potential patches with scores #> Best score: 0.002060928 for unit 78 #> Added patch centered on unit 78 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 21 #> Keystone units: 0 #> New keystone units: 0 #> Scoreable units: 21 #> Unit 90 cannot be removed - adding to keystone set #> Edge units found: 20 #> Keystone units: 1 #> New keystone units: 0 #> Scoreable units: 20 #> Unit 81 cannot be removed - adding to keystone set #> Edge units found: 19 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 19 #> Unit 80 cannot be removed - adding to keystone set #> Edge units found: 18 #> Keystone units: 3 #> New keystone units: 0 #> Scoreable units: 18 #> Unit 89 cannot be removed - adding to keystone set #> Edge units found: 17 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 17 #> Unit 73 cannot be removed - adding to keystone set #> Unit 79 cannot be removed - adding to keystone set #> Unit 72 cannot be removed - adding to keystone set #> Unit 88 cannot be removed - adding to keystone set #> Unit 87 cannot be removed - adding to keystone set #> Unit 64 cannot be removed - adding to keystone set #> No more edge units to consider - terminating #> Calculating final statistics... #> MinPatch processing complete! # Apply MinPatch with only Stage 1 and 3 (skip adding patches) result2 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, add_patches = FALSE ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Warning: After removing small patches, 5 conservation targets are no longer met. Consider setting add_patches = TRUE to automatically add patches to meet targets, or use a smaller min_patch_size. #> Warning: 5 targets are no longer met after removing small patches #> Unmet feature IDs: 1, 2, 3, 4, 5 #> Stage 2: Skipping addition of new patches... #> Stage 3: Removing unnecessary planning units... #> Edge units found: 5 #> Keystone units: 0 #> New keystone units: 5 #> Scoreable units: 0 #> No units can be removed - all are keystone - terminating #> Calculating final statistics... #> MinPatch processing complete! print_minpatch_summary(result) #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 7 (valid: 1) #> Final patches: 4 (valid: 2) #> Area change: 0.05 (31.2%) #> #> Cost Breakdown: #> Planning unit cost: 4137.80 #> Boundary cost: 0.00 #> Total cost: 4137.80 #> Selected units: 21 #> #> Feature Representation: #> Total features: 5 #> Targets met: 5 #> Targets unmet: 0 #> Mean proportion: 0.236 #> Total shortfall: 0.00 #> #> #> === End Summary ==="},{"path":"/reference/simulated_whittling.html","id":null,"dir":"Reference","previous_headings":"","what":"Simulated whittling to remove unnecessary planning units — simulated_whittling","title":"Simulated whittling to remove unnecessary planning units — simulated_whittling","text":"Stage 3 MinPatch: Remove planning units needed meet targets, reduce fragmentation, meet minimum patch size requirements","code":""},{"path":"/reference/simulated_whittling.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Simulated whittling to remove unnecessary planning units — simulated_whittling","text":"","code":"simulated_whittling(minpatch_data, verbose = TRUE)"},{"path":"/reference/simulated_whittling.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Simulated whittling to remove unnecessary planning units — simulated_whittling","text":"minpatch_data List containing MinPatch data structures (including prioritizr objects) verbose Logical, whether print progress","code":""},{"path":"/reference/simulated_whittling.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Simulated whittling to remove unnecessary planning units — simulated_whittling","text":"Updated minpatch_data unnecessary units removed","code":""},{"path":"/reference/validate_inputs.html","id":null,"dir":"Reference","previous_headings":"","what":"Validate MinPatch inputs — validate_inputs","title":"Validate MinPatch inputs — validate_inputs","text":"Internal function validate inputs MinPatch algorithm","code":""},{"path":"/reference/validate_inputs.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Validate MinPatch inputs — validate_inputs","text":"","code":"validate_inputs( solution, planning_units, targets, costs, min_patch_size, patch_radius, boundary_penalty )"},{"path":"/reference/validate_inputs.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Validate MinPatch inputs — validate_inputs","text":"solution Binary solution vector planning_units sf object planning units targets data.frame targets costs numeric vector costs min_patch_size minimum patch size patch_radius patch radius adding patches boundary_penalty Boundary penalty value","code":""},{"path":"/reference/validate_inputs.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Validate MinPatch inputs — validate_inputs","text":"NULL (throws errors validation fails)","code":""},{"path":"/reference/visualize_minpatch_results.html","id":null,"dir":"Reference","previous_headings":"","what":"Visualize MinPatch results — plot_minpatch","title":"Visualize MinPatch results — plot_minpatch","text":"Creates simple visualization MinPatch results showing original vs. modified solutions","code":""},{"path":"/reference/visualize_minpatch_results.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Visualize MinPatch results — plot_minpatch","text":"","code":"plot_minpatch(minpatch_result, title = \"MinPatch Results\")"},{"path":"/reference/visualize_minpatch_results.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Visualize MinPatch results — plot_minpatch","text":"minpatch_result Result run_minpatch function title Plot title (optional)","code":""},{"path":"/reference/visualize_minpatch_results.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Visualize MinPatch results — plot_minpatch","text":"ggplot object (ggplot2 available)","code":""},{"path":"/reference/visualize_minpatch_results.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Visualize MinPatch results — plot_minpatch","text":"","code":"if (FALSE) { # \\dontrun{ # Requires ggplot2 library(ggplot2) # Create example data example_data <- create_example_data(n_units = 25, n_features = 3) # Create prioritizr problem and solve library(prioritizr) p <- problem(example_data$planning_units, cost_column = \"cost\") %>% add_min_set_objective() %>% add_manual_targets(example_data$targets) %>% add_binary_decisions() s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 2.0, patch_radius = 1.5 ) # Visualize results plot <- plot_minpatch(result) print(plot) } # }"}]
+[{"path":"/articles/boundary-penalty.html","id":"overview","dir":"Articles","previous_headings":"","what":"Overview","title":"Boundary penalty in MinPatch","text":"boundary_penalty optional parameter discourages irregular (high-perimeter) patch shapes Stage 3 (Simulated Whittling). key idea boundary_penalty converts boundary length (perimeter) “currency” planning-unit cost, MinPatch can trade saving cost creating edge. practice: higher boundary_penalty → stronger preference smooth, compact patches (less edge) lower boundary_penalty → willingness accept jagged edges reduce planning-unit cos","code":""},{"path":"/articles/boundary-penalty.html","id":"where-it-is-used-in-minpatch","dir":"Articles","previous_headings":"","what":"Where it is used in MinPatch","title":"Boundary penalty in MinPatch","text":"MinPatch re-run optimiser Stage 3. Instead, tests removing one edge planning unit time accepts removal : Targets remain met Minimum patch-size requirements remain met (including splits) boundary-penalised objective increase (boundary_penalty > 0) means Stage 3 uses local “delta” test (change objective) rather solving new optimisation problem.","code":""},{"path":"/articles/boundary-penalty.html","id":"decision-rule-the-thing-to-remember","dir":"Articles","previous_headings":"","what":"Decision rule (the thing to remember)","title":"Boundary penalty in MinPatch","text":"Conceptually, Stage 3 evaluates objective form: Objective=∑(PU cost)+boundary_penalty×(total perimeter) \\text{Objective} = \\sum(\\text{PU cost}) + \\text{boundary_penalty} \\times (\\text{total perimeter}) testing removal single planning unit, code computes local change: ΔObjective=(boundary_penalty×Δperimeter)−unit_cost \\Delta \\text{Objective} = (\\text{boundary_penalty} \\times \\Delta \\text{perimeter}) - \\text{unit_cost} - ΔObjective>0\\Delta \\text{Objective} > 0 → objective increased → block removal - ΔObjective≤0\\Delta \\text{Objective} \\le 0 → objective stayed decreased → allow removal (assuming constraints pass)","code":""},{"path":"/articles/boundary-penalty.html","id":"why-this-looks-like-total_cost_change-in-the-code","dir":"Articles","previous_headings":"Decision rule (the thing to remember)","what":"Why this looks like “total_cost_change” in the code","title":"Boundary penalty in MinPatch","text":"code, ’ll often see value like: total_cost_change = boundary_cost_change - unit_cost exactly ΔObjective\\Delta \\text{Objective} written code form: boundary_cost_change corresponds boundary_penalty × Δperimeter unit_cost cost save removing unit : total_cost_change change objective.","code":""},{"path":"/articles/boundary-penalty.html","id":"what-does-same-currency-as-cost-mean","dir":"Articles","previous_headings":"","what":"What does “same currency as cost” mean?","title":"Boundary penalty in MinPatch","text":"objective combines two different things: Planning-unit cost (whatever choose: dollars, area, opportunity cost, etc.) Perimeter (boundary length: km, m, “edge count” depending data) add together, boundary_penalty acts like conversion rate: converts boundary length “cost units”. Objective=∑(PU cost)+boundary_penalty×(total perimeter)\\text{Objective} = \\sum(\\text{PU cost}) + \\text{boundary_penalty} \\times (\\text{total perimeter})","code":""},{"path":"/articles/boundary-penalty.html","id":"if-your-pu-cost-is-in-dollars","dir":"Articles","previous_headings":"What does “same currency as cost” mean?","what":"If your PU cost is in dollars","title":"Boundary penalty in MinPatch","text":"PU cost might : $/PU\\$ / \\text{PU} $/km2\\$ / \\text{km}^2 perimeter km boundary_penalty behaves like dollars per km edge: $/km\\$ / \\text{km} Interpretation:boundary_penalty price willing pay avoid 1 km extra boundary. Example: boundary_penalty = 2000, adding 1 km perimeter “costs” $2000 objective.","code":""},{"path":"/articles/boundary-penalty.html","id":"if-your-pu-cost-is-area","dir":"Articles","previous_headings":"What does “same currency as cost” mean?","what":"If your PU cost is area","title":"Boundary penalty in MinPatch","text":"Sometimes “cost” literally area (e.g., PU cost = area km²). : PU cost km² perimeter km boundary_penalty behaves like km² per km can think “equivalent area cost per km edge”. Interpretation:boundary_penalty tells much extra area cost willing accept avoid 1 km extra boundary. boundary_penalty = 0.5, extra 1 km edge treated like adding 0.5 km² cost.","code":""},{"path":"/articles/boundary-penalty.html","id":"the-simplest-way-to-remember-it","dir":"Articles","previous_headings":"What does “same currency as cost” mean?","what":"The simplest way to remember it","title":"Boundary penalty in MinPatch","text":"unit_cost = savings get removing PU boundary_penalty × Δperimeter = “edge bill” pay (save) boundary changes Stage 3 removes PU : ΔObjective=(boundary_penalty×Δperimeter)−unit_cost≤0 \\Delta \\text{Objective} = (\\text{boundary_penalty} \\times \\Delta \\text{perimeter}) - \\text{unit_cost} \\le 0 can read : remove PU edge bill doesn’t outweigh cost saved.","code":""},{"path":"/articles/boundary-penalty.html","id":"worked-examples-with-explicit-total-perimeter","dir":"Articles","previous_headings":"","what":"Worked examples (with explicit total perimeter)","title":"Boundary penalty in MinPatch","text":"Assumptions examples: 4-neighbour adjacency (//left/right) shared cell edge length 1 km total perimeter perimeter whole selected patch (km) useful identity (grid perimeter change): removed PU kkselected neighbours (0–4), removing changes perimeter : Δperimeter=−4+2k \\Delta \\text{perimeter} = -4 + 2k perimeter increases k≥3k \\ge 3 (.e., PU “shielding” three selected neighbours).","code":""},{"path":"/articles/boundary-penalty.html","id":"example-a-removing-an-edge-pu-reduces-total-perimeter-allowed","dir":"Articles","previous_headings":"Worked examples (with explicit total perimeter)","what":"Example A — removing an edge PU reduces total perimeter (allowed)","title":"Boundary penalty in MinPatch","text":"patch two selected PUs line. U edge unit touches . , , right. removing U Compute total perimeter: Two adjacent cells perimeter =6 km (cell 4 edges → 8 total, minus 2 shared edge → 6) : PbeforeP{\\text{}}= 6 km removing U Single cell perimeter = 4 km : PafterP{\\text{}} = 4 km Δperimeter\\Delta \\text{perimeter} = 4 - 6 =−2 km Now apply Stage 3 boundary check: Assume: boundary_penalty = 10 unit_cost = 5 ΔObjective=(boundary_penalty×Δperimeter)−unit_cost= 10×(−2)−5=−2\\Delta \\text{Objective} = (\\text{boundary_penalty} \\times \\Delta \\text{perimeter}) - \\text{unit_cost}\\ \\text{= 10}\\ \\times \\ (-2) - 5 = -2 Decision: ΔObjective≤0\\Delta \\text{Objective} \\le 0 → objective decreased → allow removal (constraints pass).","code":"| . | . | | S | U | | . | . | | . | . | | S | . | | . | . |"},{"path":"/articles/boundary-penalty.html","id":"example-b-removing-an-edge-pu-increases-total-perimeter-blocked-but-patch-stays-connected","dir":"Articles","previous_headings":"Worked examples (with explicit total perimeter)","what":"Example B — removing an edge PU increases total perimeter (blocked), but patch stays connected","title":"Boundary penalty in MinPatch","text":"use 3×2 rectangle. Let U middle PU bottom row.U edge unit touches . (outside rectangle). removing U Compute total perimeter: 3×2 rectangle perimeter: PbeforeP{\\text{}}= 2(3+2)=10 km removing U Important: patch still connected (can still walk remaining S cells via shared borders top row). Now compute total perimeter . can use neighbour rule: U k=3k=3 selected neighbours (left, right, selected; ). Therefore: Δperimeter=−4+2k=−4+2(3)=+2 \\Delta \\text{perimeter} = -4 + 2k = -4 + 2(3) = +2 : Pafter=10+2=12P_{\\text{}} = 10 + 2 = 12 km Δperimeter=+2\\Delta \\text{perimeter} = +2 km Now apply boundary check: Assume: boundary_penalty = 10 unit_cost = 5 ΔObjective=10×(+2)−5=+15\\Delta \\text{Objective} = 10 \\times (+2) - 5 = +15 Decision: ΔObjective>0\\Delta \\text{Objective} > 0 → objective increased → block removal Interpretation: Removing U saves cost, exposes new edge three neighbours, increasing total perimeter enough boundary-penalised objective gets worse.","code":"| S | S | S | | S | U | S | | S | S | S | | S | . | S |"},{"path":"/articles/boundary-penalty.html","id":"practical-guidance-for-choosing-boundary_penalty","dir":"Articles","previous_headings":"","what":"Practical guidance for choosing boundary_penalty","title":"Boundary penalty in MinPatch","text":"isn’t one universal “correct” value depends scale units costs boundary lengths. practical workflow: Start boundary_penalty = 0 turns boundary blocking. Whittling uses targets + patch-size + split viability rules. Increase gradually observe solutions change increase , removals create extra edge become likely blocked, producing smoother patches. small sensitivity check Try values spanning small range (e.g., 0, low, medium, high) compare: ``` number patches mean/median patch size total perimeter total planning-unit cost retained/removed ``` boundary_penalty high, Stage 3 can become conservative: may keep “bridges” edge units removing raise boundary term much.","code":""},{"path":"/articles/boundary-penalty.html","id":"faq","dir":"Articles","previous_headings":"","what":"FAQ","title":"Boundary penalty in MinPatch","text":"Stage 3 re-run optimiser (prioritizr)? . Stage 3 local, one-unit---time removal tests. never resolves new optimisation problem. comparing costs Stage 2 solution? directly. Stage 3 compares objective vs removing one candidate unit. uses ΔobjectiveΔ objective (local change), full re-optimisation. “per patch” “whole solution”? Conceptually objective whole solution (sum PU costs + boundary penalty term). code, change computed locally using candidate’s neighbour relationships, local boundary edges change remove one unit. costs area? ’s fine—boundary_penalty still converts perimeter units chosen cost layer. interpretation becomes: “much cost willing pay avoid 1 km extra edge?”","code":""},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"introduction","dir":"Articles","previous_headings":"","what":"1. Introduction","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"many marine spatial planning problems, goal meet representation targets lowest possible cost, also consider selected areas arranged space. Highly fragmented marine protected areas can difficult manage enforce, small isolated patches may sustain viable populations. Scattered solutions can also difficult justify decision-makers stakeholders. Two R packages especially helpful tackling issues marine context. oceandatr package offers practical way build realistic, analysis-ready marine planning datasets. helps users obtain planning boundaries (e.g., exclusive economic zones high-seas regions), generate planning-unit grids, access global ocean datasets—including bathymetry, geomorphology, seamounts, environmental conditions—aligned grids. Together, tools make easier assemble consistent inputs marine spatial planning analyses. minpatch package post-processing tool modifies conservation planning solutions produced prioritizr reduce fragmentation. enforces user-defined minimum patch size removing patches small adding new areas maintain conservation targets. simplifies solution removing unnecessary planning units, tracking changes affect patch structure total cost. vignette, demonstrate use oceandatr minpatch using simple marine example Seychelles Exclusive Economic Zone. first use oceandatr construct gridded planning problem set marine features solve standard prioritizr minimum-set problem. apply minpatch explore different minimum patch sizes boundary penalties influence resulting solutions.","code":""},{"path":[]},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"load-packages","dir":"Articles","previous_headings":"2. Study region and planning units with oceandatr","what":"2.1 Load packages","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"","code":"library(minpatch) library(oceandatr) library(terra) library(sf) library(dplyr) library(ggplot2) library(purrr) library(stringr) library(tibble) library(patchwork) library(prioritizr) library(kableExtra) library(tmap) set.seed(123) build_dir <- file.path(getwd(), \"_build\") dir.create(build_dir, showWarnings = FALSE, recursive = TRUE)"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"seychelles-eez-as-the-planning-region","dir":"Articles","previous_headings":"2. Study region and planning units with oceandatr","what":"2.2 Seychelles EEZ as the planning region","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"start using get_boundary() oceandatr obtain Seychelles Exclusive Economic zone (EEZ). defines marine planning domain.","code":"Seychelles_eez <- get_boundary(name = \"Seychelles\") # plot to check we have Seychelles' EEZ plot(Seychelles_eez[1], col = \"lightgreen\", main = \"Seychelles EEZ\", axes = TRUE)"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"choose-an-equal-area-projection","dir":"Articles","previous_headings":"2. Study region and planning units with oceandatr","what":"2.3 Choose an equal-area projection","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"reproject planning region equal-area projection ensure area- distance-based calculations meaningful.","code":"# use sf::st_bbox to get the bounding box coordinates (in lon/lat) #sf::st_bbox(Seychelles_eez) # use projectionwizard.org to choose an equal-area projection, then store as a PROJ string # https://projectionwizard.org projection_Seychelles <- \"+proj=laea +lon_0=55 +lat_0=-4.5 +datum=WGS84 +units=m +no_defs\" # check CRS if needed # sf::st_crs(projection_Seychelles)"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"create-a-planning-unit-grid","dir":"Articles","previous_headings":"2. Study region and planning units with oceandatr","what":"2.4 Create a planning-unit grid","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"now create grid EEZ. keep runtime moderate, use fairly coarse resolution. units meters. cell Seychelles_grid planning unit. build prioritizr problem later, ’s useful vector version grid:","code":"# check which units to use # sf::st_crs(projection_Seychelles, # parameters = TRUE)$units_gdal # grid the planning area Seychelles_grid <- get_grid( boundary = Seychelles_eez, resolution = 30000, # 30,000 just to test the code but a finer resolution can be opted with a more powerful PC crs = projection_Seychelles) # project the eez into same projection as grid for plotting Seychelles_eez_proj <- Seychelles_eez %>% sf::st_transform(crs = projection_Seychelles) %>% sf::st_geometry() # plot the grid terra::plot(Seychelles_grid, col = \"gold3\", axes = FALSE, legend = FALSE, main = \"Seychelles spatial grid (30 km)\") plot(Seychelles_eez_proj, add = TRUE, border = \"black\", lwd = 1) # convert grid to sf polygons as minpatch only works with sf Seychelles_pu <- Seychelles_grid %>% stars::st_as_stars() %>% sf::st_as_sf() %>% dplyr::mutate( id = dplyr::row_number(), cost = as.numeric(sf::st_area(.)) / 1e6 # cost = area in km² ) %>% dplyr::select(-layer)"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"building-marine-features-with-oceandatr","dir":"Articles","previous_headings":"","what":"3. Building marine features with oceandatr","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"use set features available oceandatr: bathymetric depth zones geomorphology - seafloor features (banks, ridges, etc.) knolls seamounts coral habitat environmental zones - clusters environmental conditions","code":""},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"prepare-feature-stack","dir":"Articles","previous_headings":"3. Building marine features with oceandatr","what":"3.1 Prepare feature stack","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"layers can easily obtained using get_features function.","code":"# set seed for reproducibility in the get_enviro_zones() sampling to find optimal cluster number set.seed(500) feature_set <- get_features(spatial_grid = Seychelles_grid) %>% remove_empty_layers() saveRDS(feature_set, file = file.path(build_dir, \"feature_set.rds\")) # tidy up feature data names for nicer mapping names(feature_set) <- gsub(\"_\", \" \", names(feature_set)) %>% stringr::str_to_sentence()"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"plot-features","dir":"Articles","previous_headings":"3. Building marine features with oceandatr","what":"3.2 Plot features","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"Features visualised tmap. Planning units coloured blue feature present grey absent.","code":"names(feature_set) <- names(feature_set) %>% gsub(\"_\", \" \", .) %>% stringr::str_to_sentence() feature_bin <- feature_set > 0 m <- tm_shape(feature_bin) + tm_raster( col.scale = tm_scale_categorical( values = c(\"grey70\", \"royalblue\"), labels = c(\"Absent\", \"Present\") ), col.legend = tm_legend_hide(), # hide legend for this layer col.free = FALSE ) + tm_facets(ncol = 4) + tm_shape(Seychelles_eez) + tm_borders() + tm_layout( panel.label.size = 1.5, legend.show = FALSE ) m + tm_add_legend( type = \"fill\", labels = c(\"Absent\", \"Present\"), col = c(\"grey70\", \"royalblue\"), title = \"\" ) + tm_layout( legend.outside = TRUE, legend.position = c(\"center\", \"bottom\"), legend.text.size = 1 )"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"converting-features-to-sf-object-and-combining-it-with-the-pu-grid","dir":"Articles","previous_headings":"3. Building marine features with oceandatr","what":"3.3 Converting features to sf object and combining it with the PU grid","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"MinPatch works well sf objects, convert feature stack sf. Afterwards, put features grid one data table.","code":"# features to sf features <- feature_set %>% stars::st_as_stars() %>% # convert data to sf sf::st_as_sf() %>% dplyr::mutate(id = dplyr::row_number()) %>% sf::st_drop_geometry() # combining Seychelles_sf <- Seychelles_pu %>% dplyr::left_join( as.data.frame(features), by = \"id\" ) # store feature column names feature_set <- names(features) %>% dplyr::setdiff(\"id\")"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"build-and-solve-a-prioritizr-problem","dir":"Articles","previous_headings":"","what":"4. Build and solve a prioritizr problem","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"now build simple minimum-set problem using prioritizr, equal-weight features uniform target 30% feature. solve baseline problem without fragmentation controls use reference MinPatch experiments. baseline solution, see selected planning units fragmented scattered across planning region.","code":"# build the problem using sf planning units + feature columns p_base <- prioritizr::problem( x = Seychelles_sf, features = feature_set, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.30) %>% # 30% of each feature add_binary_decisions() %>% add_rsymphony_solver(verbose = FALSE) # change this to cbc later p_base #> A conservation problem () #> ├•data #> │├•features: \"Epipelagic\", \"Mesopelagic\", \"Bathypelagic\", \"Abyssopelagic\", … (28 total) #> │└•planning units: #> │ ├•data: (1500 total) #> │ ├•costs: continuous values (between 900.2126 and 900.2126) #> │ ├•extent: -1301436, -912664.9, 511794.5, 457589 (xmin, ymin, xmax, ymax) #> │ └•CRS: +proj=laea +lat_0=-4.5 +lon_0=55 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs (projected) #> ├•formulation #> │├•objective: minimum set objective #> │├•penalties: none specified #> │├•features: #> ││├•targets: relative targets (all equal to 0.3) #> ││└•weights: none specified #> │├•constraints: none specified #> │└•decisions: binary decision #> └•optimization #> ├•portfolio: default portfolio #> └•solver: rsymphony solver (`gap` = 0.1, `time_limit` = 2147483647, `first_feasible` = FALSE, …) #> # ℹ Use `summary(...)` to see complete formulation. # solve the baseline problem t_base <- system.time({ s_base <- solve(p_base) }) # Plot the baseline solution p_base_plot <- plot_prioritizr(s_base) + ggtitle(\"Baseline (no MinPatch)\") p_base_plot"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"set-up-minpatch-parameters","dir":"Articles","previous_headings":"","what":"5. Set up MinPatch parameters","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"define minimum patch sizes relative planning units scaling median PU area multipliers (5×, 10×, 20×). makes thresholds easy interpret “roughly many planning units per patch”. also set single patch_radius runs, chosen represent neighbourhood 10 planning units. radius defines spatial search distance used MinPatch identify neighbouring planning units forming, expanding, merging patches.","code":"# median planning-unit area (m² and km²) median_pu_area_m2 <- median(st_area(Seychelles_sf)) median_pu_area_km2 <- median_pu_area_m2 / 1e6 # multipliers relative to the median PU area multipliers <- c(5, 10, 20) # minimum patch sizes in m² and km² patch_sizes_m2 <- multipliers * median_pu_area_m2 patch_sizes_km2 <- patch_sizes_m2 / 1e6 # setting patch radius as the length of 10 PUs median_pu_length_m <- sqrt(median_pu_area_m2) # set radius to 10 PU lengths patch_radius <- 10 * median_pu_length_m # summary table minpatch_param_summary <- tibble::tibble( multiplier = multipliers, min_patch_m2 = patch_sizes_m2, min_patch_km2 = patch_sizes_km2, median_pu_m2 = median_pu_area_m2, median_pu_area_km2 = median_pu_area_km2) # summaries of the different values cat(\"\\nMinPatch parameters (relative to planning units):\\n\") #> #> MinPatch parameters (relative to planning units): cat(\"- Median planning unit area:\", round(median_pu_area_km2, 3), \"km^2\\n\\n\") #> - Median planning unit area: 900.213 km^2 for (i in seq_along(multipliers)) { cat(\"Multiplier:\", multipliers[i], \"x median PU area\\n\") cat(\" - Minimum patch size:\", round(patch_sizes_km2[i], 2), \"km^2\\n\") cat(\" - Corresponds to ≈\", round(patch_sizes_km2[i] / median_pu_area_km2, 2), \"planning units\\n\\n\") } #> Multiplier: 5 x median PU area #> - Minimum patch size: 4501.06 km^2 #> - Corresponds to ≈ 5 planning units #> #> Multiplier: 10 x median PU area #> - Minimum patch size: 9002.13 km^2 #> - Corresponds to ≈ 10 planning units #> #> Multiplier: 20 x median PU area #> - Minimum patch size: 18004.25 km^2 #> - Corresponds to ≈ 20 planning units median_pu_length <- sqrt(median_pu_area_m2) # ~ PU width (m) radius_in_pus <- patch_radius / median_pu_length # PU-widths cat(\"Patch radius used for all runs:\\n\") #> Patch radius used for all runs: cat(\" -\", round(patch_radius, 0), \"m (≈\", round(patch_radius/1000, 2), \"km)\\n\") #> - 300035 m (≈ 300.04 km) cat(\" - ≈\", round(radius_in_pus, 1), \"planning units outward (radius)\\n\") #> - ≈ 10 planning units outward (radius)"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"run-minpatch-for-different-minimum-patch-sizes","dir":"Articles","previous_headings":"","what":"6. Run MinPatch for different minimum patch sizes","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"now run MinPatch minimum patch factor using different multipliers. run starts prioritizr solution applies different constraints minimum patch size.","code":"minpatch_results <- vector(\"list\", length(patch_sizes_m2)) minpatch_times <- numeric(length(patch_sizes_m2)) for (i in seq_along(patch_sizes_m2)) { cat(\"\\n============================================\\n\") cat(\"Running MinPatch with min patch area ~\", round(patch_sizes_km2[i], 2), \"km^2 (\", multipliers[i], \"x median PU)\\n\") cat(\"============================================\\n\") # time the MinPatch run t_mp <- system.time({ minpatch_results[[i]] <- run_minpatch( prioritizr_problem = p_base, prioritizr_solution = s_base, min_patch_size = patch_sizes_m2[i], patch_radius = patch_radius, boundary_penalty = 0, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = TRUE ) }) # store elapsed time (seconds) minpatch_times[i] <- t_mp[[\"elapsed\"]] } #> #> ============================================ #> Running MinPatch with min patch area ~ 4501.06 km^2 ( 5 x median PU) #> ============================================ #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Processed 1500 of 1500 planning units #> Creating patch radius dictionary (optimized)... #> Processed 100 of 1500 planning units #> Processed 200 of 1500 planning units #> Processed 300 of 1500 planning units #> Processed 400 of 1500 planning units #> Processed 500 of 1500 planning units #> Processed 600 of 1500 planning units #> Processed 700 of 1500 planning units #> Processed 800 of 1500 planning units #> Processed 900 of 1500 planning units #> Processed 1000 of 1500 planning units #> Processed 1100 of 1500 planning units #> Processed 1200 of 1500 planning units #> Processed 1300 of 1500 planning units #> Processed 1400 of 1500 planning units #> Processed 1500 of 1500 planning units #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 23 #> Unmet feature IDs: 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 19, 21, 22, 24, 25, 26, 27, 28 #> Iteration 1 - Unmet targets: 23 #> Found 1227 potential patches with scores #> Best score: 0.0001291295 for unit 1035 #> Added patch centered on unit 1035 #> Iteration 2 - Unmet targets: 16 #> Found 1141 potential patches with scores #> Best score: 0.0001250827 for unit 1484 #> Added patch centered on unit 1484 #> Iteration 3 - Unmet targets: 8 #> Found 1052 potential patches with scores #> Best score: 0.00005247631 for unit 1 #> Added patch centered on unit 1 #> Iteration 4 - Unmet targets: 4 #> Found 955 potential patches with scores #> Best score: 0.00002426124 for unit 103 #> Added patch centered on unit 103 #> Iteration 5 - Unmet targets: 2 #> Found 746 potential patches with scores #> Best score: 0.00001657983 for unit 1464 #> Added patch centered on unit 1464 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 207 #> Keystone units: 0 #> New keystone units: 4 #> Scoreable units: 203 #> Removed unit 229 at iteration 1 #> Edge units found: 204 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 204 #> Removed unit 198 at iteration 2 #> Edge units found: 206 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 206 #> Removed unit 168 at iteration 3 #> Edge units found: 208 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 208 #> Removed unit 139 at iteration 4 #> Edge units found: 210 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 210 #> Removed unit 140 at iteration 5 #> Removed unit 113 at iteration 6 #> Removed unit 167 at iteration 7 #> Removed unit 197 at iteration 8 #> Removed unit 228 at iteration 9 #> Removed unit 383 at iteration 10 #> Whittling iteration 100 #> Whittling iteration 200 #> Whittling iteration 300 #> Whittling iteration 400 #> No units can be removed - all are keystone - terminating #> Calculating final statistics... #> MinPatch processing complete! #> #> ============================================ #> Running MinPatch with min patch area ~ 9002.13 km^2 ( 10 x median PU) #> ============================================ #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Processed 1500 of 1500 planning units #> Creating patch radius dictionary (optimized)... #> Processed 100 of 1500 planning units #> Processed 200 of 1500 planning units #> Processed 300 of 1500 planning units #> Processed 400 of 1500 planning units #> Processed 500 of 1500 planning units #> Processed 600 of 1500 planning units #> Processed 700 of 1500 planning units #> Processed 800 of 1500 planning units #> Processed 900 of 1500 planning units #> Processed 1000 of 1500 planning units #> Processed 1100 of 1500 planning units #> Processed 1200 of 1500 planning units #> Processed 1300 of 1500 planning units #> Processed 1400 of 1500 planning units #> Processed 1500 of 1500 planning units #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 25 #> Unmet feature IDs: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 22, 24, 25, 26, 27, 28 #> Iteration 1 - Unmet targets: 25 #> Found 1306 potential patches with scores #> Best score: 0.0001083861 for unit 1456 #> Added patch centered on unit 1456 #> Iteration 2 - Unmet targets: 16 #> Found 1152 potential patches with scores #> Best score: 0.0000878979 for unit 1035 #> Added patch centered on unit 1035 #> Iteration 3 - Unmet targets: 10 #> Found 1065 potential patches with scores #> Best score: 0.00006552192 for unit 857 #> Added patch centered on unit 857 #> Iteration 4 - Unmet targets: 7 #> Found 994 potential patches with scores #> Best score: 0.00004788141 for unit 519 #> Added patch centered on unit 519 #> Iteration 5 - Unmet targets: 2 #> Found 864 potential patches with scores #> Best score: 0.00001882794 for unit 1435 #> Added patch centered on unit 1435 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 178 #> Keystone units: 0 #> New keystone units: 10 #> Scoreable units: 168 #> Removed unit 1171 at iteration 1 #> Edge units found: 169 #> Keystone units: 10 #> New keystone units: 0 #> Scoreable units: 169 #> Removed unit 1172 at iteration 2 #> Edge units found: 169 #> Keystone units: 10 #> New keystone units: 0 #> Scoreable units: 169 #> Removed unit 1215 at iteration 3 #> Edge units found: 170 #> Keystone units: 10 #> New keystone units: 0 #> Scoreable units: 170 #> Removed unit 1217 at iteration 4 #> Edge units found: 170 #> Keystone units: 10 #> New keystone units: 0 #> Scoreable units: 170 #> Removed unit 1253 at iteration 5 #> Removed unit 1289 at iteration 6 #> Removed unit 1290 at iteration 7 #> Removed unit 1291 at iteration 8 #> Removed unit 1292 at iteration 9 #> Removed unit 1199 at iteration 10 #> Whittling iteration 100 #> Whittling iteration 200 #> Whittling iteration 300 #> No units can be removed - all are keystone - terminating #> Calculating final statistics... #> MinPatch processing complete! #> #> ============================================ #> Running MinPatch with min patch area ~ 18004.25 km^2 ( 20 x median PU) #> ============================================ #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Processed 1500 of 1500 planning units #> Creating patch radius dictionary (optimized)... #> Processed 100 of 1500 planning units #> Processed 200 of 1500 planning units #> Processed 300 of 1500 planning units #> Processed 400 of 1500 planning units #> Processed 500 of 1500 planning units #> Processed 600 of 1500 planning units #> Processed 700 of 1500 planning units #> Processed 800 of 1500 planning units #> Processed 900 of 1500 planning units #> Processed 1000 of 1500 planning units #> Processed 1100 of 1500 planning units #> Processed 1200 of 1500 planning units #> Processed 1300 of 1500 planning units #> Processed 1400 of 1500 planning units #> Processed 1500 of 1500 planning units #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 26 #> Unmet feature IDs: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 22, 23, 24, 25, 26, 27, 28 #> Iteration 1 - Unmet targets: 26 #> Found 1398 potential patches with scores #> Best score: 0.00009160525 for unit 1456 #> Added patch centered on unit 1456 #> Iteration 2 - Unmet targets: 18 #> Found 1218 potential patches with scores #> Best score: 0.00006776194 for unit 684 #> Added patch centered on unit 684 #> Iteration 3 - Unmet targets: 7 #> Found 1055 potential patches with scores #> Best score: 0.00005717078 for unit 857 #> Added patch centered on unit 857 #> Iteration 4 - Unmet targets: 4 #> Found 984 potential patches with scores #> Best score: 0.00002360005 for unit 1086 #> Added patch centered on unit 1086 #> Iteration 5 - Unmet targets: 2 #> Found 886 potential patches with scores #> Best score: 0.00001501147 for unit 593 #> Added patch centered on unit 593 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 108 #> Keystone units: 0 #> New keystone units: 6 #> Scoreable units: 102 #> Removed unit 173 at iteration 1 #> Edge units found: 102 #> Keystone units: 6 #> New keystone units: 0 #> Scoreable units: 102 #> Removed unit 174 at iteration 2 #> Edge units found: 103 #> Keystone units: 6 #> New keystone units: 0 #> Scoreable units: 103 #> Removed unit 175 at iteration 3 #> Edge units found: 105 #> Keystone units: 6 #> New keystone units: 0 #> Scoreable units: 105 #> Removed unit 176 at iteration 4 #> Edge units found: 107 #> Keystone units: 6 #> New keystone units: 0 #> Scoreable units: 107 #> Removed unit 177 at iteration 5 #> Removed unit 178 at iteration 6 #> Removed unit 149 at iteration 7 #> Removed unit 150 at iteration 8 #> Removed unit 123 at iteration 9 #> Removed unit 124 at iteration 10 #> Whittling iteration 100 #> Whittling iteration 200 #> Whittling iteration 300 #> Whittling iteration 400 #> No units can be removed - all are keystone - terminating #> Calculating final statistics... #> MinPatch processing complete! # name the outputs names(minpatch_results) <- paste0(\"minpatch_\", multipliers, \"x\") names(minpatch_times) <- paste0(\"minpatch_\", multipliers, \"x\") saveRDS(minpatch_results, file = file.path(build_dir, \"minpatch_results.rds\")) saveRDS(minpatch_times, file = file.path(build_dir, \"minpatch_times.rds\"))"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"interpreting-minpatch-outcomes","dir":"Articles","previous_headings":"","what":"7. Interpreting MinPatch outcomes","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"section, interpret MinPatch modifies baseline solution. compare resulting spatial patterns, patch structure, trade-offs fragmentation cost.","code":""},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"selected-planning-units-baseline-vs-minpatch-runs","dir":"Articles","previous_headings":"7. Interpreting MinPatch outcomes","what":"7.1 Selected planning units (baseline vs MinPatch runs)","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"start showing selected planning units baseline MinPatch run. plot, can see baseline solution highly fragmented, many small isolated selected planning units. minimum patch size increases, small patches removed merged, leading fewer, larger, spatially coherent patches.","code":"patchwork::wrap_plots( plot_prioritizr(s_base, col = \"solution_1\", title = \"Baseline (prioritizr)\"), plot_prioritizr(minpatch_results[[1]]$solution, col = \"minpatch\", title = paste0(\"MinPatch: \", multipliers[1], \"× median PU area\")), plot_prioritizr(minpatch_results[[2]]$solution, col = \"minpatch\", title = paste0(\"MinPatch: \", multipliers[2], \"× median PU area\")), plot_prioritizr(minpatch_results[[3]]$solution, col = \"minpatch\", title = paste0(\"MinPatch: \", multipliers[3], \"× median PU area\")), guides = \"collect\", ncol = 2 ) & theme(legend.position = \"bottom\")"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"group-summary-table-all-runs","dir":"Articles","previous_headings":"7. Interpreting MinPatch outcomes","what":"7.2 Group summary table (all runs)","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"examined spatial solutions, summarise runs single table compare outcomes across different metrics. MinPatch applied, solution becomes less fragmented: multiplier increases, number patches decreases remaining patches become larger (higher median patch size).","code":""},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"change-maps-added-removed-retained-no-change","dir":"Articles","previous_headings":"7. Interpreting MinPatch outcomes","what":"7.3 Change maps (Added / Removed / Retained / No change)","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"comparing runs, use plot_minpatch map planning units added, removed, kept versus baseline. maps show MinPatch reshapes solution meet minimum patch sizes cut fragmentation.","code":"# Create plots for each minpatch run and arrange them with patchwork plot_list <- purrr::map2( minpatch_results, multipliers, ~ plot_minpatch( .x, title = paste0(\"Patch size x\", .y) ) ) patchwork::wrap_plots( plotlist = plot_list, guides = \"collect\", ncol = 3 ) & theme(legend.position = \"bottom\")"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"patch-labelled-maps","dir":"Articles","previous_headings":"7. Interpreting MinPatch outcomes","what":"7.4 Patch-labelled maps","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"use function labels patches MinPatch solutions flags one valid (meets minimum patch-size rule) invalid (threshold; exist). makes patch definition explicit helps interpret patch counts summary table. Patch counts depend “connected” defined. MinPatch uses rook adjacency, planning units must share edge patch. contrast, queen adjacency also treats corner-touching (diagonal) units connected. ecology management, rook-connected patches often defensible represent truly contiguous area, avoid corner-links, tend form compact patch units.","code":"for (m in c(5, 10, 20)) { print(minpatch::plot_patch_validity( multiplier = m, multipliers = multipliers, minpatch_results = minpatch_results, pu_sf = Seychelles_sf, do_snap = FALSE )) } #> MinPatch 5x: 28 patches | 28 valid | 0 invalid (threshold = 4501.063 km²) | snap = FALSE #> MinPatch 10x: 15 patches | 15 valid | 0 invalid (threshold = 9002.126 km²) | snap = FALSE #> MinPatch 20x: 8 patches | 8 valid | 0 invalid (threshold = 18004.25 km²) | snap = FALSE"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"individual-summaries-per-run","dir":"Articles","previous_headings":"7. Interpreting MinPatch outcomes","what":"7.5 Individual summaries (per run)","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":", show individual summaries runs. provide detailed breakdown comparison metrics minimum patch run baseline solution, well information feature level area comparison feature change summaries.","code":"for (i in seq_along(minpatch_results)) { result <- minpatch_results[[i]] factor_val <- multipliers[i] cat(\"\\n\\n## Scenario: MinPatch with min patch size = \", factor_val, \" × median PU area\\n\\n\", sep = \"\") # MinPatch summary (if it prints text) cat(\"**MinPatch processing summary**\\n\\n\") print_minpatch_summary(result) comparison <- compare_solutions(result) cat(\"\\n**Overall solution comparison**\\n\\n\") print(comparison$overall) cat(\"\\n**Feature-level area comparison**\\n\\n\") print(comparison$features) cat(\"\\n**Feature change summary**\\n\\n\") print(comparison$summary) } #> #> #> ## Scenario: MinPatch with min patch size = 5 × median PU area #> #> **MinPatch processing summary** #> #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 129 (valid: 21) #> Final patches: 28 (valid: 28) #> Area change: 0.00 (0.0%) #> #> Cost Breakdown: #> Planning unit cost: 406896.09 #> Boundary cost: 0.00 #> Total cost: 406896.09 #> Selected units: 452 #> #> Feature Representation: #> Total features: 28 #> Targets met: 28 #> Targets unmet: 0 #> Mean proportion: 0.395 #> Total shortfall: 0.00 #> #> #> === End Summary === #> #> **Overall solution comparison** #> #> Metric Original MinPatch Change #> 1 Selected Planning Units 452.0 452.0 0 #> 2 Total Area 406896087807.6 406896087807.6 0 #> 3 Number of Patches 129.0 28.0 -101 #> 4 Valid Patches (>= min size) 21.0 28.0 7 #> 5 Median Patch Size 900212583.6 5401275501.9 4501062918 #> 6 Planning Unit Cost 406896.1 406896.1 0 #> 7 Boundary Cost 0.0 0.0 0 #> 8 Total Cost 406896.1 406896.1 0 #> Percent_Change #> 1 0.00000 #> 2 0.00000 #> 3 -78.29457 #> 4 33.33333 #> 5 500.00000 #> 6 0.00000 #> 7 NA #> 8 0.00000 #> #> **Feature-level area comparison** #> #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 10.8 11 11 0 0.0000000 #> 2 2 7.2 8 8 0 0.0000000 #> 3 3 180.3 181 181 0 0.0000000 #> 4 4 251.7 252 252 0 0.0000000 #> 5 5 86.1 112 89 -23 -20.5357143 #> 6 6 45.9 46 52 6 13.0434783 #> 7 7 163.2 167 178 11 6.5868263 #> 8 8 116.1 122 120 -2 -1.6393443 #> 9 9 0.6 1 1 0 0.0000000 #> 10 10 0.6 1 2 1 100.0000000 #> 11 11 23.1 34 27 -7 -20.5882353 #> 12 12 2.1 5 4 -1 -20.0000000 #> 13 13 3.3 7 7 0 0.0000000 #> 14 14 158.7 178 159 -19 -10.6741573 #> 15 15 64.5 66 69 3 4.5454545 #> 16 16 20.7 28 27 -1 -3.5714286 #> 17 17 2.1 3 3 0 0.0000000 #> 18 18 22.5 41 32 -9 -21.9512195 #> 19 19 0.6 1 1 0 0.0000000 #> 20 20 14.4 22 25 3 13.6363636 #> 21 21 3.9 5 4 -1 -20.0000000 #> 22 22 63.9 72 80 8 11.1111111 #> 23 23 6.3 9 8 -1 -11.1111111 #> 24 24 51.3 52 54 2 3.8461538 #> 25 25 137.7 139 138 -1 -0.7194245 #> 26 26 150.0 150 150 0 0.0000000 #> 27 27 74.4 75 75 0 0.0000000 #> 28 28 87.9 88 89 1 1.1363636 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.018519 #> 2 TRUE TRUE 1.111111 #> 3 TRUE TRUE 1.003882 #> 4 TRUE TRUE 1.001192 #> 5 TRUE TRUE 1.300813 #> 6 TRUE TRUE 1.002179 #> 7 TRUE TRUE 1.023284 #> 8 TRUE TRUE 1.050818 #> 9 TRUE TRUE 1.666667 #> 10 TRUE TRUE 1.666667 #> 11 TRUE TRUE 1.471861 #> 12 TRUE TRUE 2.380952 #> 13 TRUE TRUE 2.121212 #> 14 TRUE TRUE 1.121613 #> 15 TRUE TRUE 1.023256 #> 16 TRUE TRUE 1.352657 #> 17 TRUE TRUE 1.428571 #> 18 TRUE TRUE 1.822222 #> 19 TRUE TRUE 1.666667 #> 20 TRUE TRUE 1.527778 #> 21 TRUE TRUE 1.282051 #> 22 TRUE TRUE 1.126761 #> 23 TRUE TRUE 1.428571 #> 24 TRUE TRUE 1.013645 #> 25 TRUE TRUE 1.009441 #> 26 TRUE TRUE 1.000000 #> 27 TRUE TRUE 1.008065 #> 28 TRUE TRUE 1.001138 #> MinPatch_Proportion #> 1 1.018519 #> 2 1.111111 #> 3 1.003882 #> 4 1.001192 #> 5 1.033682 #> 6 1.132898 #> 7 1.090686 #> 8 1.033592 #> 9 1.666667 #> 10 3.333333 #> 11 1.168831 #> 12 1.904762 #> 13 2.121212 #> 14 1.001890 #> 15 1.069767 #> 16 1.304348 #> 17 1.428571 #> 18 1.422222 #> 19 1.666667 #> 20 1.736111 #> 21 1.025641 #> 22 1.251956 #> 23 1.269841 #> 24 1.052632 #> 25 1.002179 #> 26 1.000000 #> 27 1.008065 #> 28 1.012514 #> #> **Feature change summary** #> #> features_improved features_reduced features_unchanged targets_gained #> 1 8 10 10 0 #> targets_lost #> 1 0 #> #> #> ## Scenario: MinPatch with min patch size = 10 × median PU area #> #> **MinPatch processing summary** #> #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 129 (valid: 10) #> Final patches: 15 (valid: 15) #> Area change: 21605102007.48 (5.3%) #> #> Cost Breakdown: #> Planning unit cost: 428501.19 #> Boundary cost: 0.00 #> Total cost: 428501.19 #> Selected units: 476 #> #> Feature Representation: #> Total features: 28 #> Targets met: 28 #> Targets unmet: 0 #> Mean proportion: 0.409 #> Total shortfall: 0.00 #> #> #> === End Summary === #> #> **Overall solution comparison** #> #> Metric Original MinPatch Change #> 1 Selected Planning Units 452.0 476.0 24 #> 2 Total Area 406896087807.6 428501189815.1 21605102007 #> 3 Number of Patches 129.0 15.0 -114 #> 4 Valid Patches (>= min size) 10.0 15.0 5 #> 5 Median Patch Size 900212583.6 9902338420.1 9002125836 #> 6 Planning Unit Cost 428501.2 428501.2 0 #> 7 Boundary Cost 0.0 0.0 0 #> 8 Total Cost 428501.2 428501.2 0 #> Percent_Change #> 1 5.309735 #> 2 5.309735 #> 3 -88.372093 #> 4 50.000000 #> 5 1000.000000 #> 6 0.000000 #> 7 NA #> 8 0.000000 #> #> **Feature-level area comparison** #> #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 10.8 11 11 0 0.0000000 #> 2 2 7.2 8 9 1 12.5000000 #> 3 3 180.3 181 203 22 12.1546961 #> 4 4 251.7 252 253 1 0.3968254 #> 5 5 86.1 112 100 -12 -10.7142857 #> 6 6 45.9 46 52 6 13.0434783 #> 7 7 163.2 167 172 5 2.9940120 #> 8 8 116.1 122 127 5 4.0983607 #> 9 9 0.6 1 1 0 0.0000000 #> 10 10 0.6 1 1 0 0.0000000 #> 11 11 23.1 34 29 -5 -14.7058824 #> 12 12 2.1 5 5 0 0.0000000 #> 13 13 3.3 7 4 -3 -42.8571429 #> 14 14 158.7 178 159 -19 -10.6741573 #> 15 15 64.5 66 84 18 27.2727273 #> 16 16 20.7 28 26 -2 -7.1428571 #> 17 17 2.1 3 3 0 0.0000000 #> 18 18 22.5 41 42 1 2.4390244 #> 19 19 0.6 1 1 0 0.0000000 #> 20 20 14.4 22 27 5 22.7272727 #> 21 21 3.9 5 7 2 40.0000000 #> 22 22 63.9 72 95 23 31.9444444 #> 23 23 6.3 9 11 2 22.2222222 #> 24 24 51.3 52 72 20 38.4615385 #> 25 25 137.7 139 138 -1 -0.7194245 #> 26 26 150.0 150 150 0 0.0000000 #> 27 27 74.4 75 75 0 0.0000000 #> 28 28 87.9 88 113 25 28.4090909 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.018519 #> 2 TRUE TRUE 1.111111 #> 3 TRUE TRUE 1.003882 #> 4 TRUE TRUE 1.001192 #> 5 TRUE TRUE 1.300813 #> 6 TRUE TRUE 1.002179 #> 7 TRUE TRUE 1.023284 #> 8 TRUE TRUE 1.050818 #> 9 TRUE TRUE 1.666667 #> 10 TRUE TRUE 1.666667 #> 11 TRUE TRUE 1.471861 #> 12 TRUE TRUE 2.380952 #> 13 TRUE TRUE 2.121212 #> 14 TRUE TRUE 1.121613 #> 15 TRUE TRUE 1.023256 #> 16 TRUE TRUE 1.352657 #> 17 TRUE TRUE 1.428571 #> 18 TRUE TRUE 1.822222 #> 19 TRUE TRUE 1.666667 #> 20 TRUE TRUE 1.527778 #> 21 TRUE TRUE 1.282051 #> 22 TRUE TRUE 1.126761 #> 23 TRUE TRUE 1.428571 #> 24 TRUE TRUE 1.013645 #> 25 TRUE TRUE 1.009441 #> 26 TRUE TRUE 1.000000 #> 27 TRUE TRUE 1.008065 #> 28 TRUE TRUE 1.001138 #> MinPatch_Proportion #> 1 1.018519 #> 2 1.250000 #> 3 1.125901 #> 4 1.005165 #> 5 1.161440 #> 6 1.132898 #> 7 1.053922 #> 8 1.093885 #> 9 1.666667 #> 10 1.666667 #> 11 1.255411 #> 12 2.380952 #> 13 1.212121 #> 14 1.001890 #> 15 1.302326 #> 16 1.256039 #> 17 1.428571 #> 18 1.866667 #> 19 1.666667 #> 20 1.875000 #> 21 1.794872 #> 22 1.486698 #> 23 1.746032 #> 24 1.403509 #> 25 1.002179 #> 26 1.000000 #> 27 1.008065 #> 28 1.285552 #> #> **Feature change summary** #> #> features_improved features_reduced features_unchanged targets_gained #> 1 14 6 8 0 #> targets_lost #> 1 0 #> #> #> ## Scenario: MinPatch with min patch size = 20 × median PU area #> #> **MinPatch processing summary** #> #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 129 (valid: 3) #> Final patches: 8 (valid: 8) #> Area change: 11702763587.39 (2.9%) #> #> Cost Breakdown: #> Planning unit cost: 418598.85 #> Boundary cost: 0.00 #> Total cost: 418598.85 #> Selected units: 465 #> #> Feature Representation: #> Total features: 28 #> Targets met: 28 #> Targets unmet: 0 #> Mean proportion: 0.408 #> Total shortfall: 0.00 #> #> #> === End Summary === #> #> **Overall solution comparison** #> #> Metric Original MinPatch Change #> 1 Selected Planning Units 452.0 465.0 13 #> 2 Total Area 406896087807.6 418598851395.0 11702763587 #> 3 Number of Patches 129.0 8.0 -121 #> 4 Valid Patches (>= min size) 3.0 8.0 5 #> 5 Median Patch Size 900212583.6 32857759303.0 31957546719 #> 6 Planning Unit Cost 418598.9 418598.9 0 #> 7 Boundary Cost 0.0 0.0 0 #> 8 Total Cost 418598.9 418598.9 0 #> Percent_Change #> 1 2.876106 #> 2 2.876106 #> 3 -93.798450 #> 4 166.666667 #> 5 3550.000000 #> 6 0.000000 #> 7 NA #> 8 0.000000 #> #> **Feature-level area comparison** #> #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 10.8 11 11 0 0.000000 #> 2 2 7.2 8 8 0 0.000000 #> 3 3 180.3 181 194 13 7.182320 #> 4 4 251.7 252 252 0 0.000000 #> 5 5 86.1 112 92 -20 -17.857143 #> 6 6 45.9 46 56 10 21.739130 #> 7 7 163.2 167 173 6 3.592814 #> 8 8 116.1 122 122 0 0.000000 #> 9 9 0.6 1 1 0 0.000000 #> 10 10 0.6 1 1 0 0.000000 #> 11 11 23.1 34 27 -7 -20.588235 #> 12 12 2.1 5 5 0 0.000000 #> 13 13 3.3 7 4 -3 -42.857143 #> 14 14 158.7 178 159 -19 -10.674157 #> 15 15 64.5 66 69 3 4.545455 #> 16 16 20.7 28 34 6 21.428571 #> 17 17 2.1 3 3 0 0.000000 #> 18 18 22.5 41 34 -7 -17.073171 #> 19 19 0.6 1 2 1 100.000000 #> 20 20 14.4 22 27 5 22.727273 #> 21 21 3.9 5 6 1 20.000000 #> 22 22 63.9 72 78 6 8.333333 #> 23 23 6.3 9 9 0 0.000000 #> 24 24 51.3 52 58 6 11.538462 #> 25 25 137.7 139 142 3 2.158273 #> 26 26 150.0 150 150 0 0.000000 #> 27 27 74.4 75 75 0 0.000000 #> 28 28 87.9 88 98 10 11.363636 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.018519 #> 2 TRUE TRUE 1.111111 #> 3 TRUE TRUE 1.003882 #> 4 TRUE TRUE 1.001192 #> 5 TRUE TRUE 1.300813 #> 6 TRUE TRUE 1.002179 #> 7 TRUE TRUE 1.023284 #> 8 TRUE TRUE 1.050818 #> 9 TRUE TRUE 1.666667 #> 10 TRUE TRUE 1.666667 #> 11 TRUE TRUE 1.471861 #> 12 TRUE TRUE 2.380952 #> 13 TRUE TRUE 2.121212 #> 14 TRUE TRUE 1.121613 #> 15 TRUE TRUE 1.023256 #> 16 TRUE TRUE 1.352657 #> 17 TRUE TRUE 1.428571 #> 18 TRUE TRUE 1.822222 #> 19 TRUE TRUE 1.666667 #> 20 TRUE TRUE 1.527778 #> 21 TRUE TRUE 1.282051 #> 22 TRUE TRUE 1.126761 #> 23 TRUE TRUE 1.428571 #> 24 TRUE TRUE 1.013645 #> 25 TRUE TRUE 1.009441 #> 26 TRUE TRUE 1.000000 #> 27 TRUE TRUE 1.008065 #> 28 TRUE TRUE 1.001138 #> MinPatch_Proportion #> 1 1.018519 #> 2 1.111111 #> 3 1.075984 #> 4 1.001192 #> 5 1.068525 #> 6 1.220044 #> 7 1.060049 #> 8 1.050818 #> 9 1.666667 #> 10 1.666667 #> 11 1.168831 #> 12 2.380952 #> 13 1.212121 #> 14 1.001890 #> 15 1.069767 #> 16 1.642512 #> 17 1.428571 #> 18 1.511111 #> 19 3.333333 #> 20 1.875000 #> 21 1.538462 #> 22 1.220657 #> 23 1.428571 #> 24 1.130604 #> 25 1.031227 #> 26 1.000000 #> 27 1.008065 #> 28 1.114903 #> #> **Feature change summary** #> #> features_improved features_reduced features_unchanged targets_gained #> 1 12 5 11 0 #> targets_lost #> 1 0"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"run-different-boundary-penalties","dir":"Articles","previous_headings":"","what":"8. Run different boundary penalties","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"boundary penalty controls strongly MinPatch favours spatially compact patches whittling stage. step, MinPatch attempts remove planning units edges patches maintaining conservation targets. Whether unit can removed depends trade-planning-unit cost change boundary cost result removal. boundary penalty scales trade-assigning cost fragmentation: higher values discourage creation additional patch edges therefore promote compact solutions. Seychelles example, explore small range boundary penalties illustrate sensitive whittling process boundary costs marine grid relatively uniform planning-unit areas.","code":"# calculate reasonable parameters based on planning unit characteristics median_pu_area_m2 <- median(st_area(Seychelles_sf)) # use minpatch size 5x median PU # retain the same patch radius earlier min_patch_size <- median_pu_area_m2 * 5 patch_radius #> 300035.4 [m] bp3 = 0 t3 <- system.time({ result3 <- run_minpatch( prioritizr_problem = p_base, prioritizr_solution = s_base, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = bp3, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = TRUE ) }) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Processed 1500 of 1500 planning units #> Creating patch radius dictionary (optimized)... #> Processed 100 of 1500 planning units #> Processed 200 of 1500 planning units #> Processed 300 of 1500 planning units #> Processed 400 of 1500 planning units #> Processed 500 of 1500 planning units #> Processed 600 of 1500 planning units #> Processed 700 of 1500 planning units #> Processed 800 of 1500 planning units #> Processed 900 of 1500 planning units #> Processed 1000 of 1500 planning units #> Processed 1100 of 1500 planning units #> Processed 1200 of 1500 planning units #> Processed 1300 of 1500 planning units #> Processed 1400 of 1500 planning units #> Processed 1500 of 1500 planning units #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 23 #> Unmet feature IDs: 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 19, 21, 22, 24, 25, 26, 27, 28 #> Iteration 1 - Unmet targets: 23 #> Found 1227 potential patches with scores #> Best score: 0.0001291295 for unit 1035 #> Added patch centered on unit 1035 #> Iteration 2 - Unmet targets: 16 #> Found 1141 potential patches with scores #> Best score: 0.0001250827 for unit 1484 #> Added patch centered on unit 1484 #> Iteration 3 - Unmet targets: 8 #> Found 1052 potential patches with scores #> Best score: 0.00005247631 for unit 1 #> Added patch centered on unit 1 #> Iteration 4 - Unmet targets: 4 #> Found 955 potential patches with scores #> Best score: 0.00002426124 for unit 103 #> Added patch centered on unit 103 #> Iteration 5 - Unmet targets: 2 #> Found 746 potential patches with scores #> Best score: 0.00001657983 for unit 1464 #> Added patch centered on unit 1464 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 207 #> Keystone units: 0 #> New keystone units: 4 #> Scoreable units: 203 #> Removed unit 229 at iteration 1 #> Edge units found: 204 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 204 #> Removed unit 198 at iteration 2 #> Edge units found: 206 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 206 #> Removed unit 168 at iteration 3 #> Edge units found: 208 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 208 #> Removed unit 139 at iteration 4 #> Edge units found: 210 #> Keystone units: 4 #> New keystone units: 0 #> Scoreable units: 210 #> Removed unit 140 at iteration 5 #> Removed unit 113 at iteration 6 #> Removed unit 167 at iteration 7 #> Removed unit 197 at iteration 8 #> Removed unit 228 at iteration 9 #> Removed unit 383 at iteration 10 #> Whittling iteration 100 #> Whittling iteration 200 #> Whittling iteration 300 #> Whittling iteration 400 #> No units can be removed - all are keystone - terminating #> Calculating final statistics... #> MinPatch processing complete! cat(\"result3 runtime (sec):\", t3[[\"elapsed\"]], \"\\n\") #> result3 runtime (sec): 739.76 bp4 = 0.01 t4 <- system.time({ result4 <- run_minpatch( prioritizr_problem = p_base, prioritizr_solution = s_base, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = bp4, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = TRUE ) }) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Processed 1500 of 1500 planning units #> Creating patch radius dictionary (optimized)... #> Processed 100 of 1500 planning units #> Processed 200 of 1500 planning units #> Processed 300 of 1500 planning units #> Processed 400 of 1500 planning units #> Processed 500 of 1500 planning units #> Processed 600 of 1500 planning units #> Processed 700 of 1500 planning units #> Processed 800 of 1500 planning units #> Processed 900 of 1500 planning units #> Processed 1000 of 1500 planning units #> Processed 1100 of 1500 planning units #> Processed 1200 of 1500 planning units #> Processed 1300 of 1500 planning units #> Processed 1400 of 1500 planning units #> Processed 1500 of 1500 planning units #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 23 #> Unmet feature IDs: 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 19, 21, 22, 24, 25, 26, 27, 28 #> Iteration 1 - Unmet targets: 23 #> Found 1227 potential patches with scores #> Best score: 0.0001291295 for unit 1035 #> Added patch centered on unit 1035 #> Iteration 2 - Unmet targets: 16 #> Found 1141 potential patches with scores #> Best score: 0.0001250827 for unit 1484 #> Added patch centered on unit 1484 #> Iteration 3 - Unmet targets: 8 #> Found 1052 potential patches with scores #> Best score: 0.00005247631 for unit 1 #> Added patch centered on unit 1 #> Iteration 4 - Unmet targets: 4 #> Found 955 potential patches with scores #> Best score: 0.00002426124 for unit 103 #> Added patch centered on unit 103 #> Iteration 5 - Unmet targets: 2 #> Found 746 potential patches with scores #> Best score: 0.00001657983 for unit 1464 #> Added patch centered on unit 1464 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 207 #> Keystone units: 0 #> New keystone units: 4 #> Scoreable units: 203 #> Unit 229 cannot be removed - adding to keystone set #> Edge units found: 202 #> Keystone units: 5 #> New keystone units: 0 #> Scoreable units: 202 #> Unit 383 cannot be removed - adding to keystone set #> Edge units found: 201 #> Keystone units: 6 #> New keystone units: 0 #> Scoreable units: 201 #> Unit 385 cannot be removed - adding to keystone set #> Edge units found: 200 #> Keystone units: 7 #> New keystone units: 0 #> Scoreable units: 200 #> Unit 389 cannot be removed - adding to keystone set #> Edge units found: 199 #> Keystone units: 8 #> New keystone units: 0 #> Scoreable units: 199 #> Unit 421 cannot be removed - adding to keystone set #> Unit 422 cannot be removed - adding to keystone set #> Unit 423 cannot be removed - adding to keystone set #> Removed unit 502 at iteration 8 #> Unit 563 cannot be removed - adding to keystone set #> Unit 605 cannot be removed - adding to keystone set #> Whittling iteration 100 #> Whittling iteration 200 #> No units can be removed - all are keystone - terminating #> Calculating final statistics... #> MinPatch processing complete! cat(\"result4 runtime (sec):\", t4[[\"elapsed\"]], \"\\n\") #> result4 runtime (sec): 661.92 bp5 = 1 t5 <- system.time({ result5 <- run_minpatch( prioritizr_problem = p_base, prioritizr_solution = s_base, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = bp5, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = TRUE ) }) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Processed 1500 of 1500 planning units #> Creating patch radius dictionary (optimized)... #> Processed 100 of 1500 planning units #> Processed 200 of 1500 planning units #> Processed 300 of 1500 planning units #> Processed 400 of 1500 planning units #> Processed 500 of 1500 planning units #> Processed 600 of 1500 planning units #> Processed 700 of 1500 planning units #> Processed 800 of 1500 planning units #> Processed 900 of 1500 planning units #> Processed 1000 of 1500 planning units #> Processed 1100 of 1500 planning units #> Processed 1200 of 1500 planning units #> Processed 1300 of 1500 planning units #> Processed 1400 of 1500 planning units #> Processed 1500 of 1500 planning units #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 23 #> Unmet feature IDs: 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 15, 16, 17, 19, 21, 22, 24, 25, 26, 27, 28 #> Iteration 1 - Unmet targets: 23 #> Found 1227 potential patches with scores #> Best score: 0.0001291295 for unit 1035 #> Added patch centered on unit 1035 #> Iteration 2 - Unmet targets: 16 #> Found 1141 potential patches with scores #> Best score: 0.0001250827 for unit 1484 #> Added patch centered on unit 1484 #> Iteration 3 - Unmet targets: 8 #> Found 1052 potential patches with scores #> Best score: 0.00005247631 for unit 1 #> Added patch centered on unit 1 #> Iteration 4 - Unmet targets: 4 #> Found 955 potential patches with scores #> Best score: 0.00002426124 for unit 103 #> Added patch centered on unit 103 #> Iteration 5 - Unmet targets: 2 #> Found 746 potential patches with scores #> Best score: 0.00001657983 for unit 1464 #> Added patch centered on unit 1464 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 207 #> Keystone units: 0 #> New keystone units: 4 #> Scoreable units: 203 #> Unit 229 cannot be removed - adding to keystone set #> Edge units found: 202 #> Keystone units: 5 #> New keystone units: 0 #> Scoreable units: 202 #> Unit 383 cannot be removed - adding to keystone set #> Edge units found: 201 #> Keystone units: 6 #> New keystone units: 0 #> Scoreable units: 201 #> Unit 385 cannot be removed - adding to keystone set #> Edge units found: 200 #> Keystone units: 7 #> New keystone units: 0 #> Scoreable units: 200 #> Unit 389 cannot be removed - adding to keystone set #> Edge units found: 199 #> Keystone units: 8 #> New keystone units: 0 #> Scoreable units: 199 #> Unit 421 cannot be removed - adding to keystone set #> Unit 422 cannot be removed - adding to keystone set #> Unit 423 cannot be removed - adding to keystone set #> Unit 502 cannot be removed - adding to keystone set #> Unit 563 cannot be removed - adding to keystone set #> Unit 605 cannot be removed - adding to keystone set #> Whittling iteration 100 #> Whittling iteration 200 #> No more edge units to consider - terminating #> Calculating final statistics... #> MinPatch processing complete! cat(\"result5 runtime (sec):\", t5[[\"elapsed\"]], \"\\n\") #> result5 runtime (sec): 672"},{"path":[]},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"selected-planning-units-baseline-vs-minpatch-runs-1","dir":"Articles","previous_headings":"9. Interpreting MinPatch with boundary penalties outcomes","what":"9.1 Selected planning units (baseline vs MinPatch runs)","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"compare spatial solutions across different boundary penalties baseline solution. runs used minimum patch size 5× median PU area.","code":"patchwork::wrap_plots( plot_prioritizr(s_base, col = \"solution_1\", title = \"Baseline (prioritizr)\"), plot_prioritizr(result3$solution, col = \"minpatch\", title = paste0(\"Boundary penalty: \", bp3)), plot_prioritizr(result4$solution, col = \"minpatch\", title = paste0(\"Boundary penalty: \", bp4)), plot_prioritizr(result5$solution, col = \"minpatch\", title = paste0(\"Boundary penalty: \", bp5)), guides = \"collect\", ncol = 2 ) & theme(legend.position = \"bottom\")"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"groups-summary-table-all-runs","dir":"Articles","previous_headings":"9. Interpreting MinPatch with boundary penalties outcomes","what":"9.2 Groups summary table (all runs)","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"summarise compare runs table . boundary penalty increases, selected area grows lot planning units included, layout becomes cleaner: total perimeter drops solution breaks fewer, larger patches. trade-solution becomes expensive overall, runtime increases slightly.","code":"boundary_penalties <- c(bp3, bp4, bp5) minpatch_results_bp <- list(result3, result4, result5) out_bp <- make_minpatch_summary_table( region_sf = Seychelles_sf, baseline_solution_sf = s_base, minpatch_results = minpatch_results_bp, mode = \"boundary_penalty\", boundary_penalties = boundary_penalties, fixed_multiplier = 5, # <- because you used 5× median PU area baseline_compare_obj = minpatch_results_bp[[1]], compare_solutions_fun = compare_solutions, # pass explicitly to avoid namespace issues baseline_elapsed = as.numeric(t_base[[\"elapsed\"]]), minpatch_elapsed = c(t3[[\"elapsed\"]], t4[[\"elapsed\"]], t5[[\"elapsed\"]]), projected_epsg = 32740, minpatch_solution_col = \"minpatch\", cost_col = \"cost\", make_kable = TRUE ) out_bp$kable"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"change-maps-added-removed-retained-no-change-1","dir":"Articles","previous_headings":"9. Interpreting MinPatch with boundary penalties outcomes","what":"9.3 Change maps (Added / Removed / Retained / No change)","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":", visualise MinPatch solutions different boundary penalty values.","code":"# Plot change maps patchwork::wrap_plots( plot_minpatch(result3, title = paste0(\"Boundary Penalty: \", bp3)), plot_minpatch(result4, title = paste0(\"Boundary Penalty: \", bp4)), plot_minpatch(result5, title = paste0(\"Boundary Penalty: \", bp5)), guides = \"collect\", ncol = 3 ) & ggplot2::theme(legend.position = \"bottom\")"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"individual-summaries-per-run-1","dir":"Articles","previous_headings":"9. Interpreting MinPatch with boundary penalties outcomes","what":"9.4 Individual summaries (per run)","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"show individual summaries every run detail.","code":"# Run summaries boundary_penalties <- c(bp3, bp4, bp5) minpatch_results_bp <- list(result3, result4, result5) minpatch_runtimes <- c(t3[[\"elapsed\"]], t4[[\"elapsed\"]], t5[[\"elapsed\"]]) for (i in seq_along(minpatch_results_bp)) { result <- minpatch_results_bp[[i]] bp <- boundary_penalties[i] runtime_sec <- minpatch_runtimes[i] cat(\"\\n\\n## Scenario: MinPatch with boundary penalty = \", format(bp, scientific = TRUE), \"\\n\\n\", sep = \"\") if (!is.na(runtime_sec)) { cat(\"**Runtime:** \", sprintf(\"%.2f\", runtime_sec), \" seconds\\n\\n\", sep = \"\") } cat(\"**MinPatch processing summary**\\n\\n\") print_minpatch_summary(result) comparison <- compare_solutions(result) cat(\"\\n**Overall solution comparison**\\n\\n\") print(comparison$overall) cat(\"\\n**Feature-level area comparison**\\n\\n\") print(comparison$features) cat(\"\\n**Feature change summary**\\n\\n\") print(comparison$summary) } #> #> #> ## Scenario: MinPatch with boundary penalty = 0e+00 #> #> **Runtime:** 739.76 seconds #> #> **MinPatch processing summary** #> #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 129 (valid: 21) #> Final patches: 28 (valid: 28) #> Area change: 0.00 (0.0%) #> #> Cost Breakdown: #> Planning unit cost: 406896.09 #> Boundary cost: 0.00 #> Total cost: 406896.09 #> Selected units: 452 #> #> Feature Representation: #> Total features: 28 #> Targets met: 28 #> Targets unmet: 0 #> Mean proportion: 0.395 #> Total shortfall: 0.00 #> #> #> === End Summary === #> #> **Overall solution comparison** #> #> Metric Original MinPatch Change #> 1 Selected Planning Units 452.0 452.0 0 #> 2 Total Area 406896087807.6 406896087807.6 0 #> 3 Number of Patches 129.0 28.0 -101 #> 4 Valid Patches (>= min size) 21.0 28.0 7 #> 5 Median Patch Size 900212583.6 5401275501.9 4501062918 #> 6 Planning Unit Cost 406896.1 406896.1 0 #> 7 Boundary Cost 0.0 0.0 0 #> 8 Total Cost 406896.1 406896.1 0 #> Percent_Change #> 1 0.00000 #> 2 0.00000 #> 3 -78.29457 #> 4 33.33333 #> 5 500.00000 #> 6 0.00000 #> 7 NA #> 8 0.00000 #> #> **Feature-level area comparison** #> #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 10.8 11 11 0 0.0000000 #> 2 2 7.2 8 8 0 0.0000000 #> 3 3 180.3 181 181 0 0.0000000 #> 4 4 251.7 252 252 0 0.0000000 #> 5 5 86.1 112 89 -23 -20.5357143 #> 6 6 45.9 46 52 6 13.0434783 #> 7 7 163.2 167 178 11 6.5868263 #> 8 8 116.1 122 120 -2 -1.6393443 #> 9 9 0.6 1 1 0 0.0000000 #> 10 10 0.6 1 2 1 100.0000000 #> 11 11 23.1 34 27 -7 -20.5882353 #> 12 12 2.1 5 4 -1 -20.0000000 #> 13 13 3.3 7 7 0 0.0000000 #> 14 14 158.7 178 159 -19 -10.6741573 #> 15 15 64.5 66 69 3 4.5454545 #> 16 16 20.7 28 27 -1 -3.5714286 #> 17 17 2.1 3 3 0 0.0000000 #> 18 18 22.5 41 32 -9 -21.9512195 #> 19 19 0.6 1 1 0 0.0000000 #> 20 20 14.4 22 25 3 13.6363636 #> 21 21 3.9 5 4 -1 -20.0000000 #> 22 22 63.9 72 80 8 11.1111111 #> 23 23 6.3 9 8 -1 -11.1111111 #> 24 24 51.3 52 54 2 3.8461538 #> 25 25 137.7 139 138 -1 -0.7194245 #> 26 26 150.0 150 150 0 0.0000000 #> 27 27 74.4 75 75 0 0.0000000 #> 28 28 87.9 88 89 1 1.1363636 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.018519 #> 2 TRUE TRUE 1.111111 #> 3 TRUE TRUE 1.003882 #> 4 TRUE TRUE 1.001192 #> 5 TRUE TRUE 1.300813 #> 6 TRUE TRUE 1.002179 #> 7 TRUE TRUE 1.023284 #> 8 TRUE TRUE 1.050818 #> 9 TRUE TRUE 1.666667 #> 10 TRUE TRUE 1.666667 #> 11 TRUE TRUE 1.471861 #> 12 TRUE TRUE 2.380952 #> 13 TRUE TRUE 2.121212 #> 14 TRUE TRUE 1.121613 #> 15 TRUE TRUE 1.023256 #> 16 TRUE TRUE 1.352657 #> 17 TRUE TRUE 1.428571 #> 18 TRUE TRUE 1.822222 #> 19 TRUE TRUE 1.666667 #> 20 TRUE TRUE 1.527778 #> 21 TRUE TRUE 1.282051 #> 22 TRUE TRUE 1.126761 #> 23 TRUE TRUE 1.428571 #> 24 TRUE TRUE 1.013645 #> 25 TRUE TRUE 1.009441 #> 26 TRUE TRUE 1.000000 #> 27 TRUE TRUE 1.008065 #> 28 TRUE TRUE 1.001138 #> MinPatch_Proportion #> 1 1.018519 #> 2 1.111111 #> 3 1.003882 #> 4 1.001192 #> 5 1.033682 #> 6 1.132898 #> 7 1.090686 #> 8 1.033592 #> 9 1.666667 #> 10 3.333333 #> 11 1.168831 #> 12 1.904762 #> 13 2.121212 #> 14 1.001890 #> 15 1.069767 #> 16 1.304348 #> 17 1.428571 #> 18 1.422222 #> 19 1.666667 #> 20 1.736111 #> 21 1.025641 #> 22 1.251956 #> 23 1.269841 #> 24 1.052632 #> 25 1.002179 #> 26 1.000000 #> 27 1.008065 #> 28 1.012514 #> #> **Feature change summary** #> #> features_improved features_reduced features_unchanged targets_gained #> 1 8 10 10 0 #> targets_lost #> 1 0 #> #> #> ## Scenario: MinPatch with boundary penalty = 1e-02 #> #> **Runtime:** 661.92 seconds #> #> **MinPatch processing summary** #> #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 129 (valid: 21) #> Final patches: 13 (valid: 13) #> Area change: 287167814182.79 (70.6%) #> #> Cost Breakdown: #> Planning unit cost: 694063.90 #> Boundary cost: 114657.35 #> Total cost: 808721.25 #> Selected units: 771 #> #> Feature Representation: #> Total features: 28 #> Targets met: 28 #> Targets unmet: 0 #> Mean proportion: 0.516 #> Total shortfall: 0.00 #> #> #> === End Summary === #> #> **Overall solution comparison** #> #> Metric Original MinPatch Change #> 1 Selected Planning Units 452.0 771.0 319 #> 2 Total Area 406896087807.6 694063901990.4 287167814183 #> 3 Number of Patches 129.0 13.0 -116 #> 4 Valid Patches (>= min size) 21.0 13.0 -8 #> 5 Median Patch Size 900212583.6 5401275501.9 4501062918 #> 6 Planning Unit Cost 694063.9 694063.9 0 #> 7 Boundary Cost 114657.4 114657.4 0 #> 8 Total Cost 808721.3 808721.3 0 #> Percent_Change #> 1 70.57522 #> 2 70.57522 #> 3 -89.92248 #> 4 -38.09524 #> 5 500.00000 #> 6 0.00000 #> 7 0.00000 #> 8 0.00000 #> #> **Feature-level area comparison** #> #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 10.8 11 11 0 0.000000 #> 2 2 7.2 8 9 1 12.500000 #> 3 3 180.3 181 293 112 61.878453 #> 4 4 251.7 252 458 206 81.746032 #> 5 5 86.1 112 167 55 49.107143 #> 6 6 45.9 46 77 31 67.391304 #> 7 7 163.2 167 324 157 94.011976 #> 8 8 116.1 122 179 57 46.721311 #> 9 9 0.6 1 1 0 0.000000 #> 10 10 0.6 1 2 1 100.000000 #> 11 11 23.1 34 38 4 11.764706 #> 12 12 2.1 5 4 -1 -20.000000 #> 13 13 3.3 7 7 0 0.000000 #> 14 14 158.7 178 280 102 57.303371 #> 15 15 64.5 66 91 25 37.878788 #> 16 16 20.7 28 30 2 7.142857 #> 17 17 2.1 3 3 0 0.000000 #> 18 18 22.5 41 38 -3 -7.317073 #> 19 19 0.6 1 1 0 0.000000 #> 20 20 14.4 22 23 1 4.545455 #> 21 21 3.9 5 4 -1 -20.000000 #> 22 22 63.9 72 109 37 51.388889 #> 23 23 6.3 9 12 3 33.333333 #> 24 24 51.3 52 89 37 71.153846 #> 25 25 137.7 139 165 26 18.705036 #> 26 26 150.0 150 255 105 70.000000 #> 27 27 74.4 75 177 102 136.000000 #> 28 28 87.9 88 174 86 97.727273 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.018519 #> 2 TRUE TRUE 1.111111 #> 3 TRUE TRUE 1.003882 #> 4 TRUE TRUE 1.001192 #> 5 TRUE TRUE 1.300813 #> 6 TRUE TRUE 1.002179 #> 7 TRUE TRUE 1.023284 #> 8 TRUE TRUE 1.050818 #> 9 TRUE TRUE 1.666667 #> 10 TRUE TRUE 1.666667 #> 11 TRUE TRUE 1.471861 #> 12 TRUE TRUE 2.380952 #> 13 TRUE TRUE 2.121212 #> 14 TRUE TRUE 1.121613 #> 15 TRUE TRUE 1.023256 #> 16 TRUE TRUE 1.352657 #> 17 TRUE TRUE 1.428571 #> 18 TRUE TRUE 1.822222 #> 19 TRUE TRUE 1.666667 #> 20 TRUE TRUE 1.527778 #> 21 TRUE TRUE 1.282051 #> 22 TRUE TRUE 1.126761 #> 23 TRUE TRUE 1.428571 #> 24 TRUE TRUE 1.013645 #> 25 TRUE TRUE 1.009441 #> 26 TRUE TRUE 1.000000 #> 27 TRUE TRUE 1.008065 #> 28 TRUE TRUE 1.001138 #> MinPatch_Proportion #> 1 1.018519 #> 2 1.250000 #> 3 1.625069 #> 4 1.819627 #> 5 1.939605 #> 6 1.677560 #> 7 1.985294 #> 8 1.541774 #> 9 1.666667 #> 10 3.333333 #> 11 1.645022 #> 12 1.904762 #> 13 2.121212 #> 14 1.764335 #> 15 1.410853 #> 16 1.449275 #> 17 1.428571 #> 18 1.688889 #> 19 1.666667 #> 20 1.597222 #> 21 1.025641 #> 22 1.705790 #> 23 1.904762 #> 24 1.734893 #> 25 1.198257 #> 26 1.700000 #> 27 2.379032 #> 28 1.979522 #> #> **Feature change summary** #> #> features_improved features_reduced features_unchanged targets_gained #> 1 20 3 5 0 #> targets_lost #> 1 0 #> #> #> ## Scenario: MinPatch with boundary penalty = 1e+00 #> #> **Runtime:** 672.00 seconds #> #> **MinPatch processing summary** #> #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 129 (valid: 21) #> Final patches: 13 (valid: 13) #> Area change: 306072278439.34 (75.2%) #> #> Cost Breakdown: #> Planning unit cost: 712968.37 #> Boundary cost: 12722025.33 #> Total cost: 13434993.69 #> Selected units: 792 #> #> Feature Representation: #> Total features: 28 #> Targets met: 28 #> Targets unmet: 0 #> Mean proportion: 0.537 #> Total shortfall: 0.00 #> #> #> === End Summary === #> #> **Overall solution comparison** #> #> Metric Original MinPatch Change #> 1 Selected Planning Units 452.0 792.0 340 #> 2 Total Area 406896087807.6 712968366246.9 306072278439 #> 3 Number of Patches 129.0 13.0 -116 #> 4 Valid Patches (>= min size) 21.0 13.0 -8 #> 5 Median Patch Size 900212583.6 8101913252.8 7201700669 #> 6 Planning Unit Cost 712968.4 712968.4 0 #> 7 Boundary Cost 12722025.3 12722025.3 0 #> 8 Total Cost 13434993.7 13434993.7 0 #> Percent_Change #> 1 75.22124 #> 2 75.22124 #> 3 -89.92248 #> 4 -38.09524 #> 5 800.00000 #> 6 0.00000 #> 7 0.00000 #> 8 0.00000 #> #> **Feature-level area comparison** #> #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 10.8 11 12 1 9.090909 #> 2 2 7.2 8 13 5 62.500000 #> 3 3 180.3 181 301 120 66.298343 #> 4 4 251.7 252 466 214 84.920635 #> 5 5 86.1 112 171 59 52.678571 #> 6 6 45.9 46 79 33 71.739130 #> 7 7 163.2 167 333 166 99.401198 #> 8 8 116.1 122 181 59 48.360656 #> 9 9 0.6 1 1 0 0.000000 #> 10 10 0.6 1 2 1 100.000000 #> 11 11 23.1 34 40 6 17.647059 #> 12 12 2.1 5 4 -1 -20.000000 #> 13 13 3.3 7 7 0 0.000000 #> 14 14 158.7 178 284 106 59.550562 #> 15 15 64.5 66 96 30 45.454545 #> 16 16 20.7 28 32 4 14.285714 #> 17 17 2.1 3 3 0 0.000000 #> 18 18 22.5 41 38 -3 -7.317073 #> 19 19 0.6 1 1 0 0.000000 #> 20 20 14.4 22 25 3 13.636364 #> 21 21 3.9 5 4 -1 -20.000000 #> 22 22 63.9 72 117 45 62.500000 #> 23 23 6.3 9 14 5 55.555556 #> 24 24 51.3 52 93 41 78.846154 #> 25 25 137.7 139 170 31 22.302158 #> 26 26 150.0 150 267 117 78.000000 #> 27 27 74.4 75 179 104 138.666667 #> 28 28 87.9 88 176 88 100.000000 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.018519 #> 2 TRUE TRUE 1.111111 #> 3 TRUE TRUE 1.003882 #> 4 TRUE TRUE 1.001192 #> 5 TRUE TRUE 1.300813 #> 6 TRUE TRUE 1.002179 #> 7 TRUE TRUE 1.023284 #> 8 TRUE TRUE 1.050818 #> 9 TRUE TRUE 1.666667 #> 10 TRUE TRUE 1.666667 #> 11 TRUE TRUE 1.471861 #> 12 TRUE TRUE 2.380952 #> 13 TRUE TRUE 2.121212 #> 14 TRUE TRUE 1.121613 #> 15 TRUE TRUE 1.023256 #> 16 TRUE TRUE 1.352657 #> 17 TRUE TRUE 1.428571 #> 18 TRUE TRUE 1.822222 #> 19 TRUE TRUE 1.666667 #> 20 TRUE TRUE 1.527778 #> 21 TRUE TRUE 1.282051 #> 22 TRUE TRUE 1.126761 #> 23 TRUE TRUE 1.428571 #> 24 TRUE TRUE 1.013645 #> 25 TRUE TRUE 1.009441 #> 26 TRUE TRUE 1.000000 #> 27 TRUE TRUE 1.008065 #> 28 TRUE TRUE 1.001138 #> MinPatch_Proportion #> 1 1.111111 #> 2 1.805556 #> 3 1.669440 #> 4 1.851410 #> 5 1.986063 #> 6 1.721133 #> 7 2.040441 #> 8 1.559001 #> 9 1.666667 #> 10 3.333333 #> 11 1.731602 #> 12 1.904762 #> 13 2.121212 #> 14 1.789540 #> 15 1.488372 #> 16 1.545894 #> 17 1.428571 #> 18 1.688889 #> 19 1.666667 #> 20 1.736111 #> 21 1.025641 #> 22 1.830986 #> 23 2.222222 #> 24 1.812865 #> 25 1.234568 #> 26 1.780000 #> 27 2.405914 #> 28 2.002275 #> #> **Feature change summary** #> #> features_improved features_reduced features_unchanged targets_gained #> 1 21 3 4 0 #> targets_lost #> 1 0"},{"path":"/articles/minpatch-oceandatr-Seychelles.html","id":"takeaway","dir":"Articles","previous_headings":"","what":"10. Takeaway","title":"Reducing protected area fragmentation in the Seychelles: A Case Study","text":"Overall, vignette showed simple workflow using MinPatch marine region. Seychelles example, MinPatch quickly collapses highly fragmented baseline fewer, larger rook-connected patches, increasing minimum patch multiplier strengthens consolidation. Adding boundary penalty whittling pushes solutions toward cleaner edges fewer patch boundaries, can also drive selected area total cost, especially penalty large relative planning-unit cost.","code":""},{"path":"/articles/minpatch.html","id":"introduction","dir":"Articles","previous_headings":"","what":"Introduction","title":"MinPatch with prioritizr","text":"vignette demonstrates use MinPatch real conservation planning data prioritizr. ’ll use simulated dataset included prioritizr show complete workflow problem formulation MinPatch post-processing.","code":"library(minpatch) library(prioritizr) #> Warning: package 'prioritizr' was built under R version 4.5.2 library(sf) #> Warning: package 'sf' was built under R version 4.5.2 library(terra) #> Warning: package 'terra' was built under R version 4.5.2 library(dplyr) library(ggplot2) #> Warning: package 'ggplot2' was built under R version 4.5.2 library(patchwork) #> Warning: package 'patchwork' was built under R version 4.5.2"},{"path":"/articles/minpatch.html","id":"step-1-load-and-examine-the-data","dir":"Articles","previous_headings":"Introduction","what":"Step 1: Load and Examine the Data","title":"MinPatch with prioritizr","text":"","code":"dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\")"},{"path":"/articles/minpatch.html","id":"step-2-create-and-solve-a-prioritizr-problem","dir":"Articles","previous_headings":"Introduction","what":"Step 2: Create and Solve a prioritizr Problem","title":"MinPatch with prioritizr","text":"’ll create simple minimum set problem 17% targets features:","code":"# Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve the problem s <- solve(p) # plot map of prioritization plot_prioritizr(s)"},{"path":"/articles/minpatch.html","id":"step-3-run-minpatch","dir":"Articles","previous_headings":"Introduction","what":"Step 3: Run MinPatch","title":"MinPatch with prioritizr","text":"Now can apply MinPatch directly prioritizr objects. run_minpatch() function automatically extracts necessary data prioritizr solution object: Run MinPatch automatic data extraction prioritizr objects","code":"# Calculate reasonable parameters based on planning unit characteristics median_area <- median(st_area(dat)) # Set minimum patch size to 5x median planning unit area min_patch_size <- median_area * 5 # Set patch radius to encompass approximately 10 planning units patch_radius <- sqrt(median_area * 10) cat(\"MinPatch parameters:\\n\") #> MinPatch parameters: cat(\"- Minimum patch size:\", round(min_patch_size, 3), \"square meters\\n\") #> - Minimum patch size: 0.05 square meters cat(\"- Patch radius:\", round(patch_radius,3), \"meters\\n\") #> - Patch radius: 0.316 meters cat(\"- This means patches must be at least\", round(min_patch_size/median_area, 3), \"times the median planning unit size\\n\") #> - This means patches must be at least 5 times the median planning unit size result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 0.001, # Small boundary penalty for connectivity remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = TRUE ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 5 #> Unmet feature IDs: 1, 2, 3, 4, 5 #> Iteration 1 - Unmet targets: 5 #> Found 85 potential patches with scores #> Best score: 0.002280652 for unit 90 #> Added patch centered on unit 90 #> Iteration 2 - Unmet targets: 2 #> Found 74 potential patches with scores #> Best score: 0.0009039778 for unit 86 #> Added patch centered on unit 86 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 7 #> Keystone units: 0 #> New keystone units: 0 #> Scoreable units: 7 #> Unit 68 cannot be removed - adding to keystone set #> Edge units found: 6 #> Keystone units: 1 #> New keystone units: 0 #> Scoreable units: 6 #> Removed unit 61 at iteration 2 #> Edge units found: 6 #> Keystone units: 1 #> New keystone units: 0 #> Scoreable units: 6 #> Unit 69 cannot be removed - adding to keystone set #> Edge units found: 5 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 5 #> Unit 63 cannot be removed - adding to keystone set #> Edge units found: 4 #> Keystone units: 3 #> New keystone units: 0 #> Scoreable units: 4 #> Unit 62 cannot be removed - adding to keystone set #> Unit 66 cannot be removed - adding to keystone set #> Unit 65 cannot be removed - adding to keystone set #> Unit 54 cannot be removed - adding to keystone set #> No more edge units to consider - terminating #> Calculating final statistics... #> MinPatch processing complete!"},{"path":"/articles/minpatch.html","id":"step-4-analyze-the-results","dir":"Articles","previous_headings":"Introduction","what":"Step 4: Analyze the Results","title":"MinPatch with prioritizr","text":"Let’s examine MinPatch accomplished:","code":"# Print comprehensive summary print_minpatch_summary(result) #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 7 (valid: 0) #> Final patches: 6 (valid: 2) #> Area change: 0.10 (62.5%) #> #> Cost Breakdown: #> Planning unit cost: 5147.47 #> Boundary cost: 0.00 #> Total cost: 5147.47 #> Selected units: 26 #> #> Feature Representation: #> Total features: 5 #> Targets met: 5 #> Targets unmet: 0 #> Mean proportion: 0.292 #> Total shortfall: 0.00 #> #> #> === End Summary === # Compare original vs MinPatch solutions comparison <- compare_solutions(result) # Print overall comparison cat(\"=== Overall Solution Comparison ===\\n\") #> === Overall Solution Comparison === print(comparison$overall) #> Metric Original MinPatch Change Percent_Change #> 1 Selected Planning Units 16.00000 26.00000 10.00 62.50000 #> 2 Total Area 0.16000 0.26000 0.10 62.50000 #> 3 Number of Patches 7.00000 6.00000 -1.00 -14.28571 #> 4 Valid Patches (>= min size) 0.00000 2.00000 2.00 NA #> 5 Median Patch Size 0.01000 0.04000 0.03 300.00000 #> 6 Planning Unit Cost 5147.46681 5147.46681 0.00 0.00000 #> 7 Boundary Cost 0.00385 0.00385 0.00 0.00000 #> 8 Total Cost 5147.47066 5147.47066 0.00 0.00000 # Print feature-level comparison cat(\"\\n=== Feature-Level Area Comparison ===\\n\") #> #> === Feature-Level Area Comparison === print(comparison$features) #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 12.670220 14.083429 23.139966 9.056537 64.30634 #> 2 2 4.774965 5.124808 7.492192 2.367384 46.19458 #> 3 3 11.029225 11.707674 18.779672 7.071998 60.40480 #> 4 4 6.489033 6.863962 10.607930 3.743968 54.54530 #> 5 5 8.613574 9.482534 16.097477 6.614944 69.75924 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.111538 #> 2 TRUE TRUE 1.073266 #> 3 TRUE TRUE 1.061514 #> 4 TRUE TRUE 1.057779 #> 5 TRUE TRUE 1.100883 #> MinPatch_Proportion #> 1 1.826327 #> 2 1.569057 #> 3 1.702719 #> 4 1.634747 #> 5 1.868850 # Print summary statistics cat(\"\\n=== Feature Change Summary ===\\n\") #> #> === Feature Change Summary === print(comparison$summary) #> features_improved features_reduced features_unchanged targets_gained #> 1 5 0 0 0 #> targets_lost #> 1 0 # cat(\"Features with increased area:\", comparison$summary$features_improved, \"\\n\") # cat(\"Features with decreased area:\", comparison$summary$features_reduced, \"\\n\") # cat(\"Features with unchanged area:\", comparison$summary$features_unchanged, \"\\n\") # cat(\"Targets gained:\", comparison$summary$targets_gained, \"\\n\") # cat(\"Targets lost:\", comparison$summary$targets_lost, \"\\n\")"},{"path":"/articles/minpatch.html","id":"feature-representation-analysis","dir":"Articles","previous_headings":"Introduction > Step 4: Analyze the Results","what":"Feature Representation Analysis","title":"MinPatch with prioritizr","text":"","code":"# Create solution data for prioritizr analysis minpatch_solution_data <- result$solution[c(\"minpatch\")] # Use prioritizr functions for accurate feature representation analysis feature_rep <- prioritizr::eval_feature_representation_summary(p, minpatch_solution_data) target_coverage <- prioritizr::eval_target_coverage_summary(p, minpatch_solution_data) # Summary statistics targets_met <- sum(target_coverage$met) mean_achievement <- mean(feature_rep$relative_held, na.rm = TRUE) cat(\"Conservation Performance:\\n\") #> Conservation Performance: cat(\"- Targets met:\", targets_met, \"out of\", nrow(feature_rep), \"features\\n\") #> - Targets met: 5 out of 5 features cat(\"- Mean target achievement:\", round(mean_achievement * 100, 1), \"%\\n\") #> - Mean target achievement: 29.2 % # Show features with lowest achievement combined_results <- data.frame( feature_id = seq_len(nrow(feature_rep)), proportion_met = feature_rep$relative_held, target_met = target_coverage$met ) worst_features <- combined_results[order(combined_results$proportion_met), ][1:5, ] cat(\"\\nFeatures with lowest target achievement:\\n\") #> #> Features with lowest target achievement: print(worst_features) #> feature_id proportion_met target_met #> 2 2 0.2667397 TRUE #> 4 4 0.2779071 TRUE #> 3 3 0.2894622 TRUE #> 1 1 0.3104756 TRUE #> 5 5 0.3177045 TRUE"},{"path":"/articles/minpatch.html","id":"spatial-configuration-improvements","dir":"Articles","previous_headings":"Introduction > Step 4: Analyze the Results","what":"Spatial Configuration Improvements","title":"MinPatch with prioritizr","text":"","code":"initial_stats <- result$patch_stats$initial final_stats <- result$patch_stats$final cat(\"Spatial Configuration Changes:\\n\") #> Spatial Configuration Changes: cat(\"- Initial patches:\", initial_stats$all_patch_count, \"(\", initial_stats$valid_patch_count, \"valid)\\n\") #> - Initial patches: 7 ( 0 valid) cat(\"- Final patches:\", final_stats$all_patch_count, \"(\", final_stats$valid_patch_count, \"valid)\\n\") #> - Final patches: 6 ( 2 valid) cat(\"- Patch consolidation:\", round((1 - final_stats$all_patch_count/initial_stats$all_patch_count) * 100, 1), \"% reduction\\n\") #> - Patch consolidation: 14.3 % reduction cat(\"- Median patch size increase:\", round(final_stats$median_all_patch / initial_stats$median_all_patch, 1), \"x\\n\") #> - Median patch size increase: 4 x"},{"path":"/articles/minpatch.html","id":"step-5-visualize-the-results","dir":"Articles","previous_headings":"Introduction","what":"Step 5: Visualize the Results","title":"MinPatch with prioritizr","text":"Let’s create maps visualize changes MinPatch made:","code":"plot_minpatch(result, title = \"MinPatch Results\")"},{"path":"/articles/minpatch.html","id":"working-with-locked-constraints","dir":"Articles","previous_headings":"Introduction","what":"Working with Locked Constraints","title":"MinPatch with prioritizr","text":"MinPatch automatically respects locked-locked-constraints prioritizr. useful certain areas must included (e.g., existing reserves) excluded (e.g., areas conflicting uses).","code":""},{"path":"/articles/minpatch.html","id":"example-adding-locked-in-constraints","dir":"Articles","previous_headings":"Introduction > Working with Locked Constraints","what":"Example: Adding Locked-In Constraints","title":"MinPatch with prioritizr","text":"Let’s designate existing planning units locked-(must conserved):","code":"# Select some units as existing protected areas (locked-in) locked_in_units <- c(10, 11, 20, 21, 30, 31) # Create problem with locked-in constraints p_locked_in <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% add_locked_in_constraints(locked_in_units) %>% add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve and apply MinPatch s_locked_in <- solve(p_locked_in) result_locked_in <- run_minpatch( prioritizr_problem = p_locked_in, prioritizr_solution = s_locked_in, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 0.001, verbose = FALSE ) # Verify locked-in units are preserved cat(\"Locked-in units in final solution:\\n\") #> Locked-in units in final solution: cat(\"Units:\", locked_in_units, \"\\n\") #> Units: 10 11 20 21 30 31 cat(\"Status in solution:\", result_locked_in$solution$minpatch[locked_in_units], \"\\n\") #> Status in solution: 1 1 1 1 1 1 cat(\"All locked-in units preserved:\", all(result_locked_in$solution$minpatch[locked_in_units] == 1), \"\\n\") #> All locked-in units preserved: TRUE"},{"path":"/articles/minpatch.html","id":"example-adding-locked-out-constraints","dir":"Articles","previous_headings":"Introduction > Working with Locked Constraints","what":"Example: Adding Locked-Out Constraints","title":"MinPatch with prioritizr","text":"Now let’s exclude certain areas selection (e.g., areas conflicting land uses):","code":"# Select some units to exclude (locked-out) locked_out_units <- c(50, 51, 60, 61, 70, 71) # Create problem with locked-out constraints p_locked_out <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% add_locked_out_constraints(locked_out_units) %>% add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve and apply MinPatch s_locked_out <- solve(p_locked_out) result_locked_out <- run_minpatch( prioritizr_problem = p_locked_out, prioritizr_solution = s_locked_out, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 0.001, verbose = FALSE ) # Verify locked-out units are excluded cat(\"Locked-out units in final solution:\\n\") #> Locked-out units in final solution: cat(\"Units:\", locked_out_units, \"\\n\") #> Units: 50 51 60 61 70 71 cat(\"Status in solution:\", result_locked_out$solution$minpatch[locked_out_units], \"\\n\") #> Status in solution: 0 0 0 0 0 0 cat(\"All locked-out units excluded:\", all(result_locked_out$solution$minpatch[locked_out_units] == 0), \"\\n\") #> All locked-out units excluded: TRUE"},{"path":"/articles/minpatch.html","id":"example-combining-both-constraint-types","dir":"Articles","previous_headings":"Introduction > Working with Locked Constraints","what":"Example: Combining Both Constraint Types","title":"MinPatch with prioritizr","text":"can use locked-locked-constraints together:","code":"# Create problem with both constraint types p_locked_both <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% add_locked_in_constraints(locked_in_units) %>% add_locked_out_constraints(locked_out_units) %>% add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve and apply MinPatch s_locked_both <- solve(p_locked_both) result_locked_both <- run_minpatch( prioritizr_problem = p_locked_both, prioritizr_solution = s_locked_both, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 0.001, verbose = FALSE ) cat(\"Constraint Summary:\\n\") #> Constraint Summary: cat(\"- Locked-in units preserved:\", all(result_locked_both$solution$minpatch[locked_in_units] == 1), \"\\n\") #> - Locked-in units preserved: TRUE cat(\"- Locked-out units excluded:\", all(result_locked_both$solution$minpatch[locked_out_units] == 0), \"\\n\") #> - Locked-out units excluded: TRUE"},{"path":"/articles/minpatch.html","id":"key-points-about-locked-constraints","dir":"Articles","previous_headings":"Introduction > Working with Locked Constraints","what":"Key Points About Locked Constraints","title":"MinPatch with prioritizr","text":"Locked-units never removed: Even form patches smaller min_patch_size, locked-units preserved three stages MinPatch. Locked-units never selected: Stage 2 (patch addition), locked-units considered even help meet conservation targets. Automatic detection: MinPatch automatically extracts applies locked constraints prioritizr problem—additional parameters needed! Warnings small locked-patches: locked-units form patches smaller min_patch_size, MinPatch issue warning still preserve units.","code":""},{"path":[]},{"path":"/articles/minpatch.html","id":"lets-check-the-process","dir":"Articles","previous_headings":"Introduction > Understanding the Results","what":"Lets check the process","title":"MinPatch with prioritizr","text":"Plot comparison","code":"# First remove small patches result_remove <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = FALSE, whittle_patches = FALSE, verbose = FALSE ) #> Warning in run_minpatch(prioritizr_problem = p, prioritizr_solution = s, : #> After removing small patches, 5 conservation targets are no longer met. #> Consider setting add_patches = TRUE to automatically add patches to meet #> targets, or use a smaller min_patch_size. # Next add to ensure patches meet minimum size result_add <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = FALSE, verbose = FALSE ) # Finally, try and remove areas without degrading the solution result_whittle <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE ) patchwork::wrap_plots( plot_minpatch(result_remove, title = \"Remove Small Patches\"), plot_minpatch(result_add, title = \"Add Patches\"), plot_minpatch(result_whittle, title = \"Whittle Planning Units\"), guides = \"collect\", ncol = 3 ) & theme(legend.position = \"bottom\")"},{"path":"/articles/minpatch.html","id":"what-minpatch-accomplished","dir":"Articles","previous_headings":"Introduction > Understanding the Results","what":"What MinPatch Accomplished","title":"MinPatch with prioritizr","text":"Patch Consolidation: MinPatch reduced number patches removing small, inefficient patches consolidating remaining areas larger, viable patches. Size Constraint Satisfaction: final patches now meet minimum size threshold, ensuring large enough ecologically viable cost-effective manage. Target Achievement: Conservation targets maintained improved, demonstrating MinPatch doesn’t compromise conservation effectiveness. Cost Optimization: boundary penalty helps create compact patches, potentially reducing management costs.","code":""},{"path":"/articles/minpatch.html","id":"key-insights","dir":"Articles","previous_headings":"Introduction > Understanding the Results","what":"Key Insights","title":"MinPatch with prioritizr","text":"Efficiency vs. Viability Trade-: original prioritizr solution mathematically optimal contained many small patches. MinPatch trades mathematical optimality practical viability. Context-Dependent Parameters: choice minimum patch size patch radius based ecological requirements, management constraints, expert knowledge. Computational Considerations: Processing time scales number planning units complexity spatial configuration.","code":""},{"path":[]},{"path":"/articles/minpatch.html","id":"parameter-selection","dir":"Articles","previous_headings":"Introduction > Best Practices","what":"Parameter Selection","title":"MinPatch with prioritizr","text":"Ecological requirements (home range sizes, minimum viable populations) Management efficiency (minimum economically viable management units) Expert knowledge study system Large enough allow elongated patches large create unnecessarily large patches Based typical dispersal distances management scales Connectivity patches important Compact patches preferred management Edge effects concern","code":""},{"path":"/articles/minpatch.html","id":"validation","dir":"Articles","previous_headings":"Introduction > Best Practices","what":"Validation","title":"MinPatch with prioritizr","text":"Always validate results : Checking target achievement: Ensure conservation goals still met Examining spatial patterns: Verify patches make ecological sense Comparing costs: Understand trade-offs involved Expert review: domain experts review final configuration","code":""},{"path":[]},{"path":"/articles/minpatch.html","id":"multiple-scenarios","dir":"Articles","previous_headings":"Introduction > Advanced Usage","what":"Multiple Scenarios","title":"MinPatch with prioritizr","text":"can run MinPatch different parameters explore trade-offs:","code":"# Conservative scenario (larger patches) result_conservative <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = median_area * 10, # Larger minimum size patch_radius = patch_radius * 1.5, boundary_penalty = 0.01, # Higher boundary penalty verbose = FALSE ) # Compare scenarios compare_solutions(result_conservative) #> $overall #> Metric Original MinPatch Change Percent_Change #> 1 Selected Planning Units 16.000 18.000 2.00 12.50000 #> 2 Total Area 0.160 0.180 0.02 12.50000 #> 3 Number of Patches 7.000 3.000 -4.00 -57.14286 #> 4 Valid Patches (>= min size) 0.000 0.000 0.00 NA #> 5 Median Patch Size 0.010 0.070 0.06 600.00000 #> 6 Planning Unit Cost 3535.299 3535.299 0.00 0.00000 #> 7 Boundary Cost 0.023 0.023 0.00 0.00000 #> 8 Total Cost 3535.322 3535.322 0.00 0.00000 #> #> $features #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 12.670220 14.083429 15.583069 1.4996396 10.648256 #> 2 2 4.774965 5.124808 4.827211 -0.2975976 -5.807000 #> 3 3 11.029225 11.707674 12.033291 0.3256171 2.781228 #> 4 4 6.489033 6.863962 8.002595 1.1386334 16.588574 #> 5 5 8.613574 9.482534 11.419918 1.9373846 20.431086 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.111538 #> 2 TRUE TRUE 1.073266 #> 3 TRUE TRUE 1.061514 #> 4 TRUE TRUE 1.057779 #> 5 TRUE TRUE 1.100883 #> MinPatch_Proportion #> 1 1.229897 #> 2 1.010942 #> 3 1.091037 #> 4 1.233249 #> 5 1.325805 #> #> $summary #> features_improved features_reduced features_unchanged targets_gained #> 1 4 1 0 0 #> targets_lost #> 1 0"},{"path":"/articles/minpatch.html","id":"conclusion","dir":"Articles","previous_headings":"Introduction","what":"Conclusion","title":"MinPatch with prioritizr","text":"MinPatch provides powerful way post-process prioritizr solutions ensure meet minimum patch size requirements maintaining conservation effectiveness. Tasmania case study demonstrates MinPatch can successfully: Handle real-world conservation planning datasets Consolidate fragmented solutions viable patch configurations Maintain improve conservation target achievement Provide transparent reporting trade-offs improvements integrating MinPatch conservation planning workflow, can bridge gap mathematically optimal solutions practically implementable conservation strategies.","code":""},{"path":"/articles/minpatch.html","id":"session-information","dir":"Articles","previous_headings":"Introduction","what":"Session Information","title":"MinPatch with prioritizr","text":"","code":"sessionInfo() #> R version 4.5.0 (2025-04-11 ucrt) #> Platform: x86_64-w64-mingw32/x64 #> Running under: Windows 11 x64 (build 26200) #> #> Matrix products: default #> LAPACK version 3.12.1 #> #> locale: #> [1] LC_COLLATE=English_United States.utf8 #> [2] LC_CTYPE=English_United States.utf8 #> [3] LC_MONETARY=English_United States.utf8 #> [4] LC_NUMERIC=C #> [5] LC_TIME=English_United States.utf8 #> #> time zone: Asia/Manila #> tzcode source: internal #> #> attached base packages: #> [1] stats graphics grDevices utils datasets methods base #> #> other attached packages: #> [1] patchwork_1.3.2 ggplot2_4.0.1 dplyr_1.1.4 terra_1.8-93 #> [5] sf_1.0-24 prioritizr_8.1.0 minpatch_0.1.0 #> #> loaded via a namespace (and not attached): #> [1] gtable_0.3.6 xfun_0.56 bslib_0.10.0 #> [4] raster_3.6-32 htmlwidgets_1.6.4 lattice_0.22-6 #> [7] vctrs_0.7.1 tools_4.5.0 generics_0.1.4 #> [10] parallel_4.5.0 tibble_3.3.1 proxy_0.4-29 #> [13] pkgconfig_2.0.3 Matrix_1.7-3 KernSmooth_2.23-26 #> [16] RColorBrewer_1.1-3 S7_0.2.1 desc_1.4.3 #> [19] assertthat_0.2.1 lifecycle_1.0.5 compiler_4.5.0 #> [22] farver_2.1.2 stringr_1.6.0 textshaping_1.0.4 #> [25] codetools_0.2-20 htmltools_0.5.9 class_7.3-23 #> [28] sass_0.4.10 yaml_2.3.12 pillar_1.11.1 #> [31] pkgdown_2.2.0 exactextractr_0.10.1 jquerylib_0.1.4 #> [34] rcbc_0.1.0.9003 classInt_0.4-11 cachem_1.1.0 #> [37] nlme_3.1-168 tidyselect_1.2.1 digest_0.6.39 #> [40] stringi_1.8.7 fastmap_1.2.0 grid_4.5.0 #> [43] cli_3.6.5 magrittr_2.0.4 e1071_1.7-17 #> [46] ape_5.8-1 withr_3.0.2 scales_1.4.0 #> [49] sp_2.2-1 rmarkdown_2.30 igraph_2.2.1 #> [52] otel_0.2.0 ragg_1.5.0 evaluate_1.0.5 #> [55] knitr_1.51 rlang_1.1.7 Rcpp_1.1.1 #> [58] glue_1.8.0 DBI_1.2.3 rstudioapi_0.18.0 #> [61] jsonlite_2.0.0 R6_2.6.1 systemfonts_1.3.1 #> [64] fs_1.6.6 units_1.0-0"},{"path":"/articles/minpatchTasmania.html","id":"load-packages","dir":"Articles","previous_headings":"","what":"Load packages","title":"MinPatch in Tasmania","text":"","code":"library(minpatch) library(prioritizr) library(prioritizrdata) library(terra) library(sf) library(ggplot2) library(dplyr)"},{"path":"/articles/minpatchTasmania.html","id":"load-data","dir":"Articles","previous_headings":"","what":"Load data","title":"MinPatch in Tasmania","text":"","code":"# load data tas_pu <- get_tas_pu() %>% mutate(cost = cost*10000) # At present minpatch works with sf objects. Here we convert the data to sf. tas_features <- get_tas_features() %>% stars::st_as_stars() %>% sf::st_as_sf() tas <- sf::st_interpolate_aw(tas_features, tas_pu, extensive = FALSE, keep_NA = FALSE, na.rm = FALSE) %>% st_join(tas_pu, join = st_equals) #> Warning in st_interpolate_aw.sf(tas_features, tas_pu, extensive = FALSE, : #> st_interpolate_aw assumes attributes are constant or uniform over areas of x features = tas %>% sf::st_drop_geometry() %>% dplyr::select(-all_of(c(\"id\", \"cost\", \"locked_in\", \"locked_out\"))) %>% names() # Convert data to binary again tas <- tas %>% mutate(across(all_of(features), ~ if_else(.x > 0, 1, 0)))"},{"path":"/articles/minpatchTasmania.html","id":"run-prioritizr-analysis","dir":"Articles","previous_headings":"","what":"Run prioritizr analysis","title":"MinPatch in Tasmania","text":"","code":"p <- problem(tas, features = features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.30) %>% # 30% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) s <- solve(p)"},{"path":"/articles/minpatchTasmania.html","id":"plot-prioritizr-solution","dir":"Articles","previous_headings":"Run prioritizr analysis","what":"Plot prioritizr solution","title":"MinPatch in Tasmania","text":"","code":"plot_prioritizr(s)"},{"path":[]},{"path":"/articles/minpatchTasmania.html","id":"choose-a-patch-size","dir":"Articles","previous_headings":"MinPatch","what":"Choose a patch size","title":"MinPatch in Tasmania","text":"","code":"# Calculate reasonable parameters based on planning unit characteristics median_area <- median(st_area(tas)) # Set minimum patch size to 5x median planning unit area min_patch_size <- median_area * 5 # Set patch radius to encompass approximately 10 planning units patch_radius <- sqrt(median_area * 10) cat(\"MinPatch parameters:\\n\") #> MinPatch parameters: cat(\"- Minimum patch size:\", round(min_patch_size, 3), \"square meters\\n\") #> - Minimum patch size: 324514429 square meters cat(\"- Patch radius:\", round(patch_radius,3), \"meters\\n\") #> - Patch radius: 25476.04 meters cat(\"- This means patches must be at least\", round(min_patch_size/median_area, 3), \"times the median planning unit size\\n\") #> - This means patches must be at least 5 times the median planning unit size"},{"path":"/articles/minpatchTasmania.html","id":"run-minpatch","dir":"Articles","previous_headings":"MinPatch","what":"Run minpatch","title":"MinPatch in Tasmania","text":"","code":"result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = TRUE ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Processed 100 of 1128 planning units #> Processed 200 of 1128 planning units #> Processed 300 of 1128 planning units #> Processed 400 of 1128 planning units #> Processed 500 of 1128 planning units #> Processed 600 of 1128 planning units #> Processed 700 of 1128 planning units #> Processed 800 of 1128 planning units #> Processed 900 of 1128 planning units #> Processed 1000 of 1128 planning units #> Processed 1100 of 1128 planning units #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 30 #> Unmet feature IDs: 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 #> Iteration 1 - Unmet targets: 30 #> Found 875 potential patches with scores #> Best score: 1.440508e-05 for unit 79 #> Added patch centered on unit 79 #> Iteration 2 - Unmet targets: 29 #> Found 873 potential patches with scores #> Best score: 7.880797e-06 for unit 421 #> Added patch centered on unit 421 #> Iteration 3 - Unmet targets: 27 #> Found 864 potential patches with scores #> Best score: 5.751114e-06 for unit 387 #> Added patch centered on unit 387 #> Iteration 4 - Unmet targets: 26 #> Found 859 potential patches with scores #> Best score: 4.461179e-06 for unit 362 #> Added patch centered on unit 362 #> Iteration 5 - Unmet targets: 23 #> Found 850 potential patches with scores #> Best score: 3.090659e-06 for unit 246 #> Added patch centered on unit 246 #> Iteration 10 - Unmet targets: 17 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 154 #> Keystone units: 0 #> New keystone units: 9 #> Scoreable units: 145 #> Removed unit 438 at iteration 1 #> Edge units found: 145 #> Keystone units: 9 #> New keystone units: 0 #> Scoreable units: 145 #> Removed unit 419 at iteration 2 #> Edge units found: 146 #> Keystone units: 9 #> New keystone units: 0 #> Scoreable units: 146 #> Unit 439 cannot be removed - adding to keystone set #> Edge units found: 145 #> Keystone units: 10 #> New keystone units: 0 #> Scoreable units: 145 #> Removed unit 111 at iteration 4 #> Edge units found: 144 #> Keystone units: 10 #> New keystone units: 0 #> Scoreable units: 144 #> Removed unit 401 at iteration 5 #> Removed unit 420 at iteration 6 #> Removed unit 400 at iteration 7 #> Removed unit 213 at iteration 8 #> Removed unit 255 at iteration 9 #> Removed unit 75 at iteration 10 #> Whittling iteration 100 #> No units can be removed - all are keystone - terminating #> Calculating final statistics... #> MinPatch processing complete!"},{"path":"/articles/minpatchTasmania.html","id":"visualise-the-minpatch-solution","dir":"Articles","previous_headings":"MinPatch","what":"Visualise the minpatch solution","title":"MinPatch in Tasmania","text":"","code":"plot_minpatch(result, title = \"MinPatch Results\")"},{"path":"/articles/minpatchTasmania.html","id":"analyse-the-final-results","dir":"Articles","previous_headings":"MinPatch","what":"Analyse the final results","title":"MinPatch in Tasmania","text":"","code":"print_minpatch_summary(result) #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 58 (valid: 11) #> Final patches: 15 (valid: 14) #> Area change: -646956349.29 (-3.2%) #> #> Cost Breakdown: #> Planning unit cost: 59188469.96 #> Boundary cost: 0.00 #> Total cost: 59188469.96 #> Selected units: 344 #> #> Feature Representation: #> Total features: 33 #> Targets met: 33 #> Targets unmet: 0 #> Mean proportion: 0.392 #> Total shortfall: 0.00 #> #> #> === End Summary === # Compare original vs MinPatch solutions comparison <- compare_solutions(result) # Print overall comparison cat(\"=== Overall Solution Comparison ===\\n\") #> === Overall Solution Comparison === print(comparison$overall) #> Metric Original MinPatch Change Percent_Change #> 1 Selected Planning Units 330 344 14 4.242424 #> 2 Total Area 20052333083 19405376734 -646956349 -3.226340 #> 3 Number of Patches 58 15 -43 -74.137931 #> 4 Valid Patches (>= min size) 11 14 3 27.272727 #> 5 Median Patch Size 64915437 373340097 308424660 475.117591 #> 6 Planning Unit Cost 59188470 59188470 0 0.000000 #> 7 Boundary Cost 0 0 0 NA #> 8 Total Cost 59188470 59188470 0 0.000000 # Print feature-level comparison cat(\"\\n=== Feature-Level Area Comparison ===\\n\") #> #> === Feature-Level Area Comparison === print(comparison$features) #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 1.2 2 3 1 50.0000000 #> 2 2 33.0 35 38 3 8.5714286 #> 3 3 2.7 3 5 2 66.6666667 #> 4 4 170.1 177 173 -4 -2.2598870 #> 5 5 213.9 221 214 -7 -3.1674208 #> 6 6 242.1 250 250 0 0.0000000 #> 7 7 9.9 12 11 -1 -8.3333333 #> 8 8 108.3 113 110 -3 -2.6548673 #> 9 9 30.9 41 49 8 19.5121951 #> 10 10 260.4 271 281 10 3.6900369 #> 11 11 62.4 67 66 -1 -1.4925373 #> 12 12 132.0 151 152 1 0.6622517 #> 13 13 133.5 138 134 -4 -2.8985507 #> 14 14 122.4 128 123 -5 -3.9062500 #> 15 15 62.4 66 69 3 4.5454545 #> 16 16 240.0 246 257 11 4.4715447 #> 17 17 0.3 1 1 0 0.0000000 #> 18 18 27.6 29 28 -1 -3.4482759 #> 19 19 6.6 8 8 0 0.0000000 #> 20 20 18.0 19 24 5 26.3157895 #> 21 21 24.3 31 37 6 19.3548387 #> 22 22 7.5 12 11 -1 -8.3333333 #> 23 23 34.5 39 40 1 2.5641026 #> 24 24 11.1 12 12 0 0.0000000 #> 25 25 47.1 51 71 20 39.2156863 #> 26 26 9.3 11 11 0 0.0000000 #> 27 27 86.7 88 87 -1 -1.1363636 #> 28 28 6.6 7 10 3 42.8571429 #> 29 29 1.2 2 2 0 0.0000000 #> 30 30 186.0 192 187 -5 -2.6041667 #> 31 31 66.3 71 78 7 9.8591549 #> 32 32 30.9 36 37 1 2.7777778 #> 33 33 57.9 60 58 -2 -3.3333333 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.666667 #> 2 TRUE TRUE 1.060606 #> 3 TRUE TRUE 1.111111 #> 4 TRUE TRUE 1.040564 #> 5 TRUE TRUE 1.033193 #> 6 TRUE TRUE 1.032631 #> 7 TRUE TRUE 1.212121 #> 8 TRUE TRUE 1.043398 #> 9 TRUE TRUE 1.326861 #> 10 TRUE TRUE 1.040707 #> 11 TRUE TRUE 1.073718 #> 12 TRUE TRUE 1.143939 #> 13 TRUE TRUE 1.033708 #> 14 TRUE TRUE 1.045752 #> 15 TRUE TRUE 1.057692 #> 16 TRUE TRUE 1.025000 #> 17 TRUE TRUE 3.333333 #> 18 TRUE TRUE 1.050725 #> 19 TRUE TRUE 1.212121 #> 20 TRUE TRUE 1.055556 #> 21 TRUE TRUE 1.275720 #> 22 TRUE TRUE 1.600000 #> 23 TRUE TRUE 1.130435 #> 24 TRUE TRUE 1.081081 #> 25 TRUE TRUE 1.082803 #> 26 TRUE TRUE 1.182796 #> 27 TRUE TRUE 1.014994 #> 28 TRUE TRUE 1.060606 #> 29 TRUE TRUE 1.666667 #> 30 TRUE TRUE 1.032258 #> 31 TRUE TRUE 1.070890 #> 32 TRUE TRUE 1.165049 #> 33 TRUE TRUE 1.036269 #> MinPatch_Proportion #> 1 2.500000 #> 2 1.151515 #> 3 1.851852 #> 4 1.017049 #> 5 1.000468 #> 6 1.032631 #> 7 1.111111 #> 8 1.015697 #> 9 1.585761 #> 10 1.079109 #> 11 1.057692 #> 12 1.151515 #> 13 1.003745 #> 14 1.004902 #> 15 1.105769 #> 16 1.070833 #> 17 3.333333 #> 18 1.014493 #> 19 1.212121 #> 20 1.333333 #> 21 1.522634 #> 22 1.466667 #> 23 1.159420 #> 24 1.081081 #> 25 1.507431 #> 26 1.182796 #> 27 1.003460 #> 28 1.515152 #> 29 1.666667 #> 30 1.005376 #> 31 1.176471 #> 32 1.197411 #> 33 1.001727 # Print summary statistics cat(\"\\n=== Feature Change Summary ===\\n\") #> #> === Feature Change Summary === print(comparison$summary) #> features_improved features_reduced features_unchanged targets_gained #> 1 15 12 6 0 #> targets_lost #> 1 0"},{"path":"/articles/minpatchTasmania.html","id":"run-different-patch-sizes","dir":"Articles","previous_headings":"","what":"Run different patch sizes","title":"MinPatch in Tasmania","text":"minimum patch size parameter core constraint drives MinPatch behaviour - determines threshold patches considered small must either enlarged removed. Stage 1, MinPatch removes patches smaller threshold (except existing protected areas). Stage 2, adds new patches large enough meet minimum targets unmet. Stage 3 (whittling), prevents removal planning units make patch fall threshold. Larger minimum patch sizes result fewer, bigger patches potentially higher total area, MinPatch must ensure every patch meets size requirement. Smaller minimum patch sizes allow flexibility, potentially resulting patches closer original prioritizr solution. choice minimum patch size reflect ecological management considerations - example, larger patches may needed support viable populations reduce edge effects, smaller patches may acceptable highly connected landscapes features don’t require large contiguous areas.","code":"# Calculate reasonable parameters based on planning unit characteristics median_area <- median(st_area(tas)) min_patch_size <- median_area * 10 patch_radius <- sqrt(median_area * 10) result2 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE ) median_area <- median(st_area(tas)) min_patch_size <- median_area * 20 patch_radius <- sqrt(median_area * 10) result3 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE )"},{"path":"/articles/minpatchTasmania.html","id":"visualise-the-minpatch-solution-1","dir":"Articles","previous_headings":"Run different patch sizes","what":"Visualise the minpatch solution","title":"MinPatch in Tasmania","text":"","code":"patchwork::wrap_plots(plot_minpatch(result, title = \"Patch Size x5\"), plot_minpatch(result2, title = \"Patch Size x10\"), plot_minpatch(result3, title = \"Patch Size x20\"), guides = \"collect\", ncol = 3) & theme(legend.position = \"bottom\")"},{"path":"/articles/minpatchTasmania.html","id":"run-different-boundary-penalties","dir":"Articles","previous_headings":"","what":"Run different Boundary Penalties","title":"MinPatch in Tasmania","text":"boundary penalty controls much MinPatch prioritizes spatial compactness “simulated whittling” stage. whittling, MinPatch considers removing planning units patch edges, doesn’t increase total priortizr cost. boundary penalty affects decision penalizing fragmented solutions - higher penalties favour compact patches making costly create additional “edge” selected unselected areas. MinPatch evaluates whether remove unit, calculates change boundary length (units selected neighbours increase boundary removed, units unselected neighbours decrease boundary) multiplies boundary penalty. resulting boundary cost change exceeds unit’s cost, unit removed. datasets like Tasmania long planning unit boundaries relative unit costs, even small boundary penalties can highly influential, potentially preventing unit removals resulting similar solutions across different penalty values. small penalties (e.g., 1e-10) may needed see meaningful differences cases.","code":"# Calculate reasonable parameters based on planning unit characteristics median_area <- median(st_area(tas)) min_patch_size <- median_area * 5 patch_radius <- sqrt(median_area * 10) result4 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 1, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE ) result5 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = min_patch_size, patch_radius = patch_radius, boundary_penalty = 10, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, verbose = FALSE )"},{"path":"/articles/minpatchTasmania.html","id":"visualise-the-final-solution","dir":"Articles","previous_headings":"Run different Boundary Penalties","what":"Visualise the final solution","title":"MinPatch in Tasmania","text":"","code":"patchwork::wrap_plots(plot_prioritizr(result2$solution, col = \"minpatch\", title = \"Boundary Penalty: 0\"), plot_prioritizr(result4$solution, col = \"minpatch\", title = \"Boundary Penalty: 1\"), plot_prioritizr(result5$solution, col = \"minpatch\", title = \"Boundary Penalty: 10\"), guides = \"collect\", ncol = 3) & theme(legend.position = \"bottom\")"},{"path":"/articles/minpatchTasmania.html","id":"visualise-the-differences-in-the-minpatch-solution","dir":"Articles","previous_headings":"Run different Boundary Penalties","what":"Visualise the differences in the minpatch solution","title":"MinPatch in Tasmania","text":"","code":"patchwork::wrap_plots(plot_minpatch(result2, title = \"Boundary Penalty: 0\"), plot_minpatch(result4, title = \"Boundary Penalty: 1\"), plot_minpatch(result5, title = \"Boundary Penalty: 10\"), guides = \"collect\", ncol = 3) & theme(legend.position = \"bottom\")"},{"path":"/authors.html","id":null,"dir":"","previous_headings":"","what":"Authors","title":"Authors and Citation","text":"Jason D. Everett. Author, maintainer. Anthony J. Richardson. Author. Robert J. Smith. Author. Original MinPatch algorithm author","code":""},{"path":"/authors.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Authors and Citation","text":"Everett J, Richardson , Smith R (2026). minpatch: Post-Processing Conservation Planning Solutions Ensure Minimum Patch Sizes. R package version 0.1.0, https://github.com/SpatialPlanning/minpatch.","code":"@Manual{, title = {minpatch: Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes}, author = {Jason D. Everett and Anthony J. Richardson and Robert J. Smith}, year = {2026}, note = {R package version 0.1.0}, url = {https://github.com/SpatialPlanning/minpatch}, }"},{"path":"/index.html","id":"minpatch-for-r-","dir":"","previous_headings":"","what":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Note: still work progress significant bugs may present. Use caution information original implemention MinPatch Marxan QGIS available : https://cluz-systematic-conservation-planning.github.io","code":""},{"path":"/index.html","id":"overview","dir":"","previous_headings":"","what":"Overview","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"R implementation MinPatch algorithm post-processing conservation planning solutions ensure minimum protected area sizes. MinPatch post-processing tool conservation planning solutions ensures protected areas meet user-defined minimum size thresholds. R package implements methodology described Smith et al. (2010) designed work solutions prioritizr package, though can work binary conservation solution.","code":""},{"path":"/index.html","id":"the-problem","dir":"","previous_headings":"Overview","what":"The Problem","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Conservation planning software like Marxan prioritizr can produce solutions many small, fragmented protected areas. solutions may mathematically optimal, small protected areas often: Less ecologically viable expensive manage vulnerable edge effects Less resilient disturbances","code":""},{"path":"/index.html","id":"the-solution","dir":"","previous_headings":"Overview","what":"The Solution","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"MinPatch addresses post-processing conservation solutions three stages: Remove Small Patches: Eliminate protected areas smaller minimum size threshold Add New Patches: Add new areas meet conservation targets using BestPatch algorithm Simulated Whittling: Remove unnecessary planning units maintaining constraints","code":""},{"path":"/index.html","id":"installation","dir":"","previous_headings":"","what":"Installation","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"","code":"# Install from GitHub pak::pak(\"SpatialPlanning/minpatch\")"},{"path":"/index.html","id":"key-features","dir":"","previous_headings":"","what":"Key Features","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Full MinPatch Algorithm: Complete implementation three stages prioritizr Integration: Seamless workflow prioritizr solutions Locked Constraints Support: Automatically respects locked-locked-constraints prioritizr Flexible Parameters: Control minimum patch sizes, patch radius, boundary penalties Comprehensive Reporting: Detailed statistics comparisons Visualization Support: Plot results ggplot2 (optional)","code":""},{"path":[]},{"path":"/index.html","id":"locked-constraints","dir":"","previous_headings":"Algorithm Details","what":"Locked Constraints","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"MinPatch automatically respects locked-locked-constraints prioritizr problems: Locked-constraints (add_locked_in_constraints()): Planning units locked-never removed, regardless patch size whittling stage. units treated “conserved” areas must retained final solution. Locked-constraints (add_locked_out_constraints()): Planning units locked-never selected, even adding new patches meet conservation targets. units completely excluded consideration. locked-units form patches smaller min_patch_size, warning issued, units still preserved solution.","code":""},{"path":"/index.html","id":"stage-1-remove-small-patches","dir":"","previous_headings":"Algorithm Details","what":"Stage 1: Remove Small Patches","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Identifies connected components (patches) solution removes smaller minimum size threshold. Locked-planning units never removed, even form small patches.","code":""},{"path":"/index.html","id":"stage-2-add-new-patches-bestpatch-algorithm","dir":"","previous_headings":"Algorithm Details","what":"Stage 2: Add New Patches (BestPatch Algorithm)","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Uses BestPatch scoring system add new patches: Calculate current conservation levels feature Identify features unmet targets Score potential patches based contribution targets relative cost Add highest-scoring patch repeat BestPatch score calculated :","code":"Score = Σ(feature_contribution / target_gap) / patch_cost"},{"path":"/index.html","id":"stage-3-simulated-whittling","dir":"","previous_headings":"Algorithm Details","what":"Stage 3: Simulated Whittling","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Removes unnecessary planning units iterative process: Identify edge units (boundary selected areas) Calculate whittling scores based feature importance Must cause targets unmet Must make patches small Must increase total cost (boundary penalty > 0) Must split patches non-viable pieces","code":""},{"path":"/index.html","id":"user-inputs-parameters-that-shape-patch-structure","dir":"","previous_headings":"Algorithm Details","what":"User inputs (parameters that shape patch structure)","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"MinPatch uses small set user-defined parameters determine patch structure modified reduce fragmentation. Together, settings control minimum patch size, candidate patches constructed, strongly irregular boundaries penalised. min_patch_size: minimum allowable patch size. Patches smaller threshold removed Stage 1 (except locked-units apply), removals Stage 3 blocked cause patch (split fragment) drop size. patch_radius: Controls candidate patches constructed Stage 2 (BestPatch). Larger radii generally favour adding compact patches (fewer isolated units), can also increase total area added. boundary_penalty (optional): Controls strongly MinPatch discourages irregular patch edges Stage 3 (whittling). Conceptually, boundary_penalty converts patch perimeter (boundary length) “currency” planning-unit cost, edge length can traded area/opportunity cost. boundary_penalty > 0, candidate edge unit removed increase boundary-penalised objective: ΔObjective=(boundary_penalty×Δperimeter)−unit_cost \\Delta \\text{Objective} = (\\text{boundary_penalty} \\times \\Delta \\text{perimeter}) - \\text{unit_cost} ΔObjective>0\\Delta \\text{Objective} > 0, removal blocked increase boundary penalty outweighs cost saved removing unit (.e., make patch edge “expensive”). Setting boundary_penalty = 0 turns check.","code":""},{"path":"/index.html","id":"citation","dir":"","previous_headings":"","what":"Citation","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"use package, please cite original paper implementation:","code":"Smith, R.J., Di Minin, E., Linke, S., Segan, D.B., Possingham, H.P. (2010). An approach for ensuring minimum protected area size in systematic conservation planning. Biological Conservation, 143(10), 2525-2531. Everett J.D., Richardson A.J., Smith R.J. (2025). _minpatch: Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes_. R package version 0.1.0, ."},{"path":"/index.html","id":"license","dir":"","previous_headings":"","what":"License","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"GPL (>= 3)","code":""},{"path":"/index.html","id":"references","dir":"","previous_headings":"","what":"References","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Smith, R.J., Di Minin, E., Linke, S., Segan, D.B., Possingham, H.P. (2010). approach ensuring minimum protected area size systematic conservation planning. Biological Conservation, 143(10), 2525-2531.","code":""},{"path":"/index.html","id":"getting-help","dir":"","previous_headings":"","what":"Getting Help","title":"Post-Processing for Conservation Planning Solutions to Ensure Minimum Patch Sizes","text":"Check package vignette: vignette(\"minpatch\") View function documentation: ?run_minpatch Report bugs: GitHub Issues","code":""},{"path":"/reference/add_new_patches.html","id":null,"dir":"Reference","previous_headings":"","what":"Add new patches to meet conservation targets — add_new_patches","title":"Add new patches to meet conservation targets — add_new_patches","text":"Stage 2 MinPatch: Add new patches using BestPatch algorithm","code":""},{"path":"/reference/add_new_patches.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add new patches to meet conservation targets — add_new_patches","text":"","code":"add_new_patches(minpatch_data, verbose = TRUE)"},{"path":"/reference/add_new_patches.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add new patches to meet conservation targets — add_new_patches","text":"minpatch_data List containing MinPatch data structures (including prioritizr objects) verbose Logical, whether print progress","code":""},{"path":"/reference/add_new_patches.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add new patches to meet conservation targets — add_new_patches","text":"Updated minpatch_data new patches added","code":""},{"path":"/reference/add_patch_centered_on_unit.html","id":null,"dir":"Reference","previous_headings":"","what":"Add patch centered on specified planning unit — add_patch_centered_on_unit","title":"Add patch centered on specified planning unit — add_patch_centered_on_unit","text":"Add patch centered specified planning unit","code":""},{"path":"/reference/add_patch_centered_on_unit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Add patch centered on specified planning unit — add_patch_centered_on_unit","text":"","code":"add_patch_centered_on_unit(minpatch_data, center_unit_id)"},{"path":"/reference/add_patch_centered_on_unit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Add patch centered on specified planning unit — add_patch_centered_on_unit","text":"minpatch_data List containing MinPatch data structures center_unit_id ID unit center patch ","code":""},{"path":"/reference/add_patch_centered_on_unit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Add patch centered on specified planning unit — add_patch_centered_on_unit","text":"Updated unit_dict new patch added","code":""},{"path":"/reference/calculate_best_patch_scores.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","title":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","text":"Implements BestPatch scoring algorithm original paper","code":""},{"path":"/reference/calculate_best_patch_scores.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","text":"","code":"calculate_best_patch_scores(minpatch_data, feature_amounts, unmet_targets)"},{"path":"/reference/calculate_best_patch_scores.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","text":"minpatch_data List containing MinPatch data structures feature_amounts Named vector current conservation amounts unmet_targets Character vector features unmet targets","code":""},{"path":"/reference/calculate_best_patch_scores.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate BestPatch scores for all available planning units — calculate_best_patch_scores","text":"Named vector BestPatch scores","code":""},{"path":"/reference/calculate_cost_summary.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","title":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","text":"Calculates various cost components using prioritizr functions possible","code":""},{"path":"/reference/calculate_cost_summary.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","text":"","code":"calculate_cost_summary(minpatch_data)"},{"path":"/reference/calculate_cost_summary.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/calculate_cost_summary.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate comprehensive cost summary for MinPatch solution — calculate_cost_summary","text":"List containing detailed cost breakdown","code":""},{"path":"/reference/calculate_feature_conservation.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate current feature conservation amounts — calculate_feature_conservation","title":"Calculate current feature conservation amounts — calculate_feature_conservation","text":"Calculates much feature currently conserved","code":""},{"path":"/reference/calculate_feature_conservation.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate current feature conservation amounts — calculate_feature_conservation","text":"","code":"calculate_feature_conservation(minpatch_data)"},{"path":"/reference/calculate_feature_conservation.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate current feature conservation amounts — calculate_feature_conservation","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/calculate_feature_conservation.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate current feature conservation amounts — calculate_feature_conservation","text":"Named vector conserved amounts feature","code":""},{"path":"/reference/calculate_feature_representation.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate feature representation in solution — calculate_feature_representation","title":"Calculate feature representation in solution — calculate_feature_representation","text":"Calculates much conservation feature represented current solution using prioritizr functions possible","code":""},{"path":"/reference/calculate_feature_representation.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate feature representation in solution — calculate_feature_representation","text":"","code":"calculate_feature_representation(minpatch_data)"},{"path":"/reference/calculate_feature_representation.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate feature representation in solution — calculate_feature_representation","text":"minpatch_data List containing MinPatch data structures including prioritizr objects","code":""},{"path":"/reference/calculate_feature_representation.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate feature representation in solution — calculate_feature_representation","text":"Data frame feature representation statistics","code":""},{"path":"/reference/calculate_patch_stats.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate patch statistics — calculate_patch_stats","title":"Calculate patch statistics — calculate_patch_stats","text":"Calculates summary statistics patches including areas counts","code":""},{"path":"/reference/calculate_patch_stats.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate patch statistics — calculate_patch_stats","text":"","code":"calculate_patch_stats(minpatch_data)"},{"path":"/reference/calculate_patch_stats.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate patch statistics — calculate_patch_stats","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/calculate_patch_stats.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate patch statistics — calculate_patch_stats","text":"Updated minpatch_data patch statistics added","code":""},{"path":"/reference/calculate_whittle_scores.html","id":null,"dir":"Reference","previous_headings":"","what":"Calculate whittling scores for edge units — calculate_whittle_scores","title":"Calculate whittling scores for edge units — calculate_whittle_scores","text":"Calculates \"Low Relevance\" score edge unit based feature importance (Equation A2 original paper). Optimized accept pre-computed feature amounts avoid redundant calculations.","code":""},{"path":"/reference/calculate_whittle_scores.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Calculate whittling scores for edge units — calculate_whittle_scores","text":"","code":"calculate_whittle_scores(edge_units, minpatch_data, feature_amounts = NULL)"},{"path":"/reference/calculate_whittle_scores.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Calculate whittling scores for edge units — calculate_whittle_scores","text":"edge_units Character vector edge unit IDs minpatch_data List containing MinPatch data structures feature_amounts Optional pre-computed feature conservation amounts","code":""},{"path":"/reference/calculate_whittle_scores.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Calculate whittling scores for edge units — calculate_whittle_scores","text":"Named vector whittling scores","code":""},{"path":"/reference/can_remove_unit.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if a planning unit can be removed — can_remove_unit","title":"Check if a planning unit can be removed — can_remove_unit","text":"Checks multiple criteria determine removing unit acceptable: 1. violate conservation targets 2. make patch small 3. increase total cost 4. split patches non-viable pieces","code":""},{"path":"/reference/can_remove_unit.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if a planning unit can be removed — can_remove_unit","text":"","code":"can_remove_unit(unit_id, minpatch_data, feature_amounts = NULL)"},{"path":"/reference/can_remove_unit.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if a planning unit can be removed — can_remove_unit","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/can_remove_unit.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if a planning unit can be removed — can_remove_unit","text":"Logical indicating unit can removed","code":""},{"path":"/reference/compare_solutions.html","id":null,"dir":"Reference","previous_headings":"","what":"Compare solutions before and after MinPatch — compare_solutions","title":"Compare solutions before and after MinPatch — compare_solutions","text":"Creates comprehensive comparison key metrics original MinPatch solutions, including overall statistics detailed feature-level analysis","code":""},{"path":"/reference/compare_solutions.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Compare solutions before and after MinPatch — compare_solutions","text":"","code":"compare_solutions(minpatch_result)"},{"path":"/reference/compare_solutions.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Compare solutions before and after MinPatch — compare_solutions","text":"minpatch_result Result run_minpatch function","code":""},{"path":"/reference/compare_solutions.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Compare solutions before and after MinPatch — compare_solutions","text":"List containing: overall: Data frame overall solution comparison features: Data frame feature-level area comparisons summary: List summary statistics feature changes","code":""},{"path":"/reference/compare_solutions.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Compare solutions before and after MinPatch — compare_solutions","text":"","code":"library(prioritizr) #> Warning: package 'prioritizr' was built under R version 4.5.2 library(sf) #> Warning: package 'sf' was built under R version 4.5.2 #> Linking to GEOS 3.13.1, GDAL 3.11.4, PROJ 9.7.0; sf_use_s2() is TRUE library(terra) #> Warning: package 'terra' was built under R version 4.5.2 #> terra 1.8.93 # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, verbose = FALSE ) # Compare solutions comparison <- compare_solutions(result) # Print overall comparison print(comparison$overall) #> Metric Original MinPatch Change Percent_Change #> 1 Selected Planning Units 16.000 18.000 2.000 12.50000 #> 2 Total Area 0.160 0.180 0.020 12.50000 #> 3 Number of Patches 7.000 4.000 -3.000 -42.85714 #> 4 Valid Patches (>= min size) 1.000 2.000 1.000 100.00000 #> 5 Median Patch Size 0.010 0.045 0.035 350.00000 #> 6 Planning Unit Cost 3535.082 3535.082 0.000 0.00000 #> 7 Boundary Cost 0.000 0.000 0.000 NA #> 8 Total Cost 3535.082 3535.082 0.000 0.00000 # Print feature-level comparison print(comparison$features) #> Feature_ID Target Original_Area MinPatch_Area Area_Change Percent_Change #> 1 1 12.670220 14.083429 15.953974 1.87054533 13.28189 #> 2 2 4.774965 5.124808 5.178169 0.05336054 1.04122 #> 3 3 11.029225 11.707674 12.981443 1.27376851 10.87977 #> 4 4 6.489033 6.863962 7.167961 0.30399939 4.42892 #> 5 5 8.613574 9.482534 11.118871 1.63633770 17.25633 #> Original_Target_Met MinPatch_Target_Met Original_Proportion #> 1 TRUE TRUE 1.111538 #> 2 TRUE TRUE 1.073266 #> 3 TRUE TRUE 1.061514 #> 4 TRUE TRUE 1.057779 #> 5 TRUE TRUE 1.100883 #> MinPatch_Proportion #> 1 1.259171 #> 2 1.084441 #> 3 1.177004 #> 4 1.104627 #> 5 1.290855 # Print summary statistics cat(\"Features improved:\", comparison$summary$features_improved, \"\\n\") #> Features improved: 5 cat(\"Targets gained:\", comparison$summary$targets_gained, \"\\n\") #> Targets gained: 0"},{"path":"/reference/create_abundance_matrix.html","id":null,"dir":"Reference","previous_headings":"","what":"Create abundance matrix from planning units — create_abundance_matrix","title":"Create abundance matrix from planning units — create_abundance_matrix","text":"Creates matrix showing amount feature planning unit extracting feature columns directly planning_units using prioritizr problem","code":""},{"path":"/reference/create_abundance_matrix.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create abundance matrix from planning units — create_abundance_matrix","text":"","code":"create_abundance_matrix(planning_units, prioritizr_problem)"},{"path":"/reference/create_abundance_matrix.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create abundance matrix from planning units — create_abundance_matrix","text":"planning_units sf object planning unit geometries feature columns prioritizr_problem prioritizr problem object get feature names","code":""},{"path":"/reference/create_abundance_matrix.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create abundance matrix from planning units — create_abundance_matrix","text":"Named list planning unit contains feature abundances","code":""},{"path":"/reference/create_boundary_matrix.html","id":null,"dir":"Reference","previous_headings":"","what":"Create boundary matrix from planning units — create_boundary_matrix","title":"Create boundary matrix from planning units — create_boundary_matrix","text":"Creates sparse matrix shared boundary lengths adjacent planning units. Returns Matrix::sparseMatrix efficient storage operations. optimized version supports parallel processing via parallelly package. n_cores = 1, runs sequentially parallel overhead.","code":""},{"path":"/reference/create_boundary_matrix.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create boundary matrix from planning units — create_boundary_matrix","text":"","code":"create_boundary_matrix(planning_units, verbose = TRUE, n_cores = NULL)"},{"path":"/reference/create_boundary_matrix.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create boundary matrix from planning units — create_boundary_matrix","text":"planning_units sf object planning unit geometries verbose Logical, whether print progress n_cores Integer, number cores use. NULL, uses availableCores(omit=1). Set 1 sequential processing.","code":""},{"path":"/reference/create_boundary_matrix.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create boundary matrix from planning units — create_boundary_matrix","text":"Matrix::dgCMatrix sparse matrix [,j] shared boundary length","code":""},{"path":"/reference/create_patch_radius_dict.html","id":null,"dir":"Reference","previous_headings":"","what":"Create patch radius dictionary — create_patch_radius_dict","title":"Create patch radius dictionary — create_patch_radius_dict","text":"planning unit, find units within specified patch radius. Optimized version computes full distance matrix instead n times.","code":""},{"path":"/reference/create_patch_radius_dict.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create patch radius dictionary — create_patch_radius_dict","text":"","code":"create_patch_radius_dict(planning_units, patch_radius, verbose = TRUE)"},{"path":"/reference/create_patch_radius_dict.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create patch radius dictionary — create_patch_radius_dict","text":"planning_units sf object planning unit geometries patch_radius radius patch creation","code":""},{"path":"/reference/create_patch_radius_dict.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create patch radius dictionary — create_patch_radius_dict","text":"Named list planning unit contains list units within radius","code":""},{"path":"/reference/create_solution_vector.html","id":null,"dir":"Reference","previous_headings":"","what":"Create solution vector from unit dictionary — create_solution_vector","title":"Create solution vector from unit dictionary — create_solution_vector","text":"Converts internal unit dictionary back binary solution vector","code":""},{"path":"/reference/create_solution_vector.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create solution vector from unit dictionary — create_solution_vector","text":"","code":"create_solution_vector(unit_dict)"},{"path":"/reference/create_solution_vector.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create solution vector from unit dictionary — create_solution_vector","text":"unit_dict Named list containing cost status planning unit","code":""},{"path":"/reference/create_solution_vector.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create solution vector from unit dictionary — create_solution_vector","text":"Binary numeric vector indicating selected planning units","code":""},{"path":"/reference/extract_locked_in_constraints.html","id":null,"dir":"Reference","previous_headings":"","what":"Extract locked-in constraint indices from prioritizr problem — extract_locked_in_constraints","title":"Extract locked-in constraint indices from prioritizr problem — extract_locked_in_constraints","text":"Extract locked-constraint indices prioritizr problem","code":""},{"path":"/reference/extract_locked_in_constraints.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Extract locked-in constraint indices from prioritizr problem — extract_locked_in_constraints","text":"","code":"extract_locked_in_constraints(prioritizr_problem, verbose = TRUE)"},{"path":"/reference/extract_locked_in_constraints.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Extract locked-in constraint indices from prioritizr problem — extract_locked_in_constraints","text":"prioritizr_problem prioritizr problem object verbose Logical, whether print messages","code":""},{"path":"/reference/extract_locked_in_constraints.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Extract locked-in constraint indices from prioritizr problem — extract_locked_in_constraints","text":"Integer vector locked-planning unit indices","code":""},{"path":"/reference/extract_locked_out_constraints.html","id":null,"dir":"Reference","previous_headings":"","what":"Extract locked-out constraint indices from prioritizr problem — extract_locked_out_constraints","title":"Extract locked-out constraint indices from prioritizr problem — extract_locked_out_constraints","text":"Extract locked-constraint indices prioritizr problem","code":""},{"path":"/reference/extract_locked_out_constraints.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Extract locked-out constraint indices from prioritizr problem — extract_locked_out_constraints","text":"","code":"extract_locked_out_constraints(prioritizr_problem, verbose = TRUE)"},{"path":"/reference/extract_locked_out_constraints.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Extract locked-out constraint indices from prioritizr problem — extract_locked_out_constraints","text":"prioritizr_problem prioritizr problem object verbose Logical, whether print messages","code":""},{"path":"/reference/extract_locked_out_constraints.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Extract locked-out constraint indices from prioritizr problem — extract_locked_out_constraints","text":"Integer vector locked-planning unit indices","code":""},{"path":"/reference/find_edge_units.html","id":null,"dir":"Reference","previous_headings":"","what":"Find edge planning units — find_edge_units","title":"Find edge planning units — find_edge_units","text":"Identifies planning units edge selected areas (least one unselected neighbor). Optimized vector pre-allocation.","code":""},{"path":"/reference/find_edge_units.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Find edge planning units — find_edge_units","text":"","code":"find_edge_units(minpatch_data)"},{"path":"/reference/find_edge_units.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Find edge planning units — find_edge_units","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/find_edge_units.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Find edge planning units — find_edge_units","text":"Character vector edge unit IDs","code":""},{"path":"/reference/generate_minpatch_report.html","id":null,"dir":"Reference","previous_headings":"","what":"Generate comprehensive MinPatch report — generate_minpatch_report","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"Creates detailed report MinPatch processing results","code":""},{"path":"/reference/generate_minpatch_report.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"","code":"generate_minpatch_report(minpatch_result)"},{"path":"/reference/generate_minpatch_report.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"minpatch_result Result object run_minpatch function","code":""},{"path":"/reference/generate_minpatch_report.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"List containing formatted report components","code":""},{"path":"/reference/generate_minpatch_report.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Generate comprehensive MinPatch report — generate_minpatch_report","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, verbose = FALSE ) generate_minpatch_report(result) #> $features #> # A tibble: 5 × 9 #> feature met total_amount absolute_target absolute_held absolute_shortfall #> #> 1 feature_1 TRUE 74.5 12.7 16.0 0 #> 2 feature_2 TRUE 28.1 4.77 5.18 0 #> 3 feature_3 TRUE 64.9 11.0 13.0 0 #> 4 feature_4 TRUE 38.2 6.49 7.17 0 #> 5 feature_5 TRUE 50.7 8.61 11.1 0 #> # ℹ 3 more variables: relative_target , relative_held , #> # relative_shortfall #> #> $patch_stats #> time all_patch_count all_patch_area median_all_patch valid_patch_count #> 1 initial 7 0.16 0.010 1 #> 2 final 4 0.18 0.045 2 #> valid_patch_area median_valid_patch #> 1 0.05 0.05 #> 2 0.12 0.06 #> #> $cost #> # A tibble: 1 × 6 #> summary cost n boundary_length boundary_cost total_cost #> #> 1 overall 3535. 18 0 0 3535. #>"},{"path":"/reference/get_solution_with_solution1.html","id":null,"dir":"Reference","previous_headings":"","what":"Ensure a solution has a `solution_1` column — get_solution_with_solution1","title":"Ensure a solution has a `solution_1` column — get_solution_with_solution1","text":"Helper returns `x$solution`, adding `solution_1` column missing copying first column `x$solution` already `x$planning_units`.","code":""},{"path":"/reference/get_solution_with_solution1.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Ensure a solution has a `solution_1` column — get_solution_with_solution1","text":"","code":"get_solution_with_solution1(x)"},{"path":"/reference/get_solution_with_solution1.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Ensure a solution has a `solution_1` column — get_solution_with_solution1","text":"x list-like object containing `solution` `planning_units`.","code":""},{"path":"/reference/get_solution_with_solution1.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Ensure a solution has a `solution_1` column — get_solution_with_solution1","text":"data.frame (tibble) like `x$solution`, guaranteed include `solution_1` suitable column exists.","code":""},{"path":"/reference/identify_unmet_targets.html","id":null,"dir":"Reference","previous_headings":"","what":"Identify features with unmet targets — identify_unmet_targets","title":"Identify features with unmet targets — identify_unmet_targets","text":"Uses prioritizr functions identify unmet targets minpatch_data","code":""},{"path":"/reference/identify_unmet_targets.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Identify features with unmet targets — identify_unmet_targets","text":"","code":"identify_unmet_targets(minpatch_data)"},{"path":"/reference/identify_unmet_targets.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Identify features with unmet targets — identify_unmet_targets","text":"minpatch_data List containing MinPatch data structures including prioritizr objects","code":""},{"path":"/reference/identify_unmet_targets.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Identify features with unmet targets — identify_unmet_targets","text":"Character vector feature IDs unmet targets","code":""},{"path":"/reference/initialize_minpatch_data.html","id":null,"dir":"Reference","previous_headings":"","what":"Initialize MinPatch data structures — initialize_minpatch_data","title":"Initialize MinPatch data structures — initialize_minpatch_data","text":"Creates internal data structures needed MinPatch processing. function extracts locked-locked-constraints prioritizr problem applies status codes: - Status 2 (conserved) locked-units - Status 3 (excluded) locked-units","code":""},{"path":"/reference/initialize_minpatch_data.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Initialize MinPatch data structures — initialize_minpatch_data","text":"","code":"initialize_minpatch_data( solution, planning_units, targets, costs, min_patch_size, patch_radius, boundary_penalty, prioritizr_problem, prioritizr_solution, verbose = TRUE )"},{"path":"/reference/initialize_minpatch_data.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Initialize MinPatch data structures — initialize_minpatch_data","text":"solution Binary solution vector planning_units sf object planning units targets data.frame targets costs numeric vector costs min_patch_size minimum patch size patch_radius patch radius boundary_penalty Boundary penalty value prioritizr_problem prioritizr problem object prioritizr_solution solved prioritizr solution object verbose Logical, whether print progress","code":""},{"path":"/reference/initialize_minpatch_data.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Initialize MinPatch data structures — initialize_minpatch_data","text":"List containing necessary data structures","code":""},{"path":"/reference/make_minpatch_summary_table.html","id":null,"dir":"Reference","previous_headings":"","what":"Make MinPatch summary table — make_minpatch_summary_table","title":"Make MinPatch summary table — make_minpatch_summary_table","text":"Builds single table summarising baseline solution multiple MinPatch solutions. Baseline patch metrics come `compare_solutions(... )$overall[,\"Original\"]`, MinPatch metrics come `compare_solutions(... )$overall[,\"MinPatch\"]`.","code":""},{"path":"/reference/make_minpatch_summary_table.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Make MinPatch summary table — make_minpatch_summary_table","text":"","code":"make_minpatch_summary_table( region_sf, baseline_solution_sf, minpatch_results, mode = c(\"multiplier\", \"boundary_penalty\"), multipliers = NULL, boundary_penalties = NULL, fixed_multiplier = NA_real_, compare_solutions_fun = NULL, baseline_compare_obj = NULL, baseline_elapsed = NA_real_, minpatch_elapsed = NULL, projected_epsg = 32740, baseline_solution_col = NULL, minpatch_solution_col = \"minpatch\", cost_col = \"cost\", baseline_boundary_cost = 0, make_kable = TRUE )"},{"path":"/reference/make_minpatch_summary_table.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Make MinPatch summary table — make_minpatch_summary_table","text":"region_sf sf planning units (used compute median PU area min patch size). baseline_solution_sf sf containing baseline solution selection column PU costs. minpatch_results List MinPatch results; must contain `$solution` (sf). mode One \"multiplier\" \"boundary_penalty\". multipliers Numeric vector; required mode=\"multiplier\". boundary_penalties Numeric vector; required mode=\"boundary_penalty\". fixed_multiplier Numeric scalar used mode=\"boundary_penalty\" (e.g., 5 \"5× median PU area\"). compare_solutions_fun Function returns list `$overall`. NULL, function try find `compare_solutions()` session. baseline_compare_obj Object pass compare_solutions() baseline \"Original\" metrics. NULL, defaults `minpatch_results[[1]]`. baseline_elapsed Baseline runtime (seconds). Optional. minpatch_elapsed MinPatch runtimes (seconds). Optional; must match number runs. projected_epsg EPSG perimeter calcs inputs lon/lat (default 32740). baseline_solution_col Baseline selection column name (1/0). NULL, auto-detects. minpatch_solution_col MinPatch selection column `$solution` (default \"minpatch\"). cost_col PU cost column `baseline_solution_sf` (default \"cost\"). baseline_boundary_cost Baseline boundary cost (default 0). make_kable TRUE, also returns HTML kable.","code":""},{"path":"/reference/make_minpatch_summary_table.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Make MinPatch summary table — make_minpatch_summary_table","text":"list(data = tibble, kable = html NULL)","code":""},{"path":"/reference/make_minpatch_summary_table.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Make MinPatch summary table — make_minpatch_summary_table","text":"Two modes: - mode = \"multiplier\": multiple min patch sizes (uses `multipliers`) - mode = \"boundary_penalty\": one min patch size, multiple boundary penalties (uses `boundary_penalties`)","code":""},{"path":"/reference/make_patch_dict.html","id":null,"dir":"Reference","previous_headings":"","what":"Create patch dictionary from unit dictionary — make_patch_dict","title":"Create patch dictionary from unit dictionary — make_patch_dict","text":"Identifies connected components (patches) current solution using igraph sparse matrix operations. implementation follows wheretowork approach efficient patch identification using matrix subsetting.","code":""},{"path":"/reference/make_patch_dict.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Create patch dictionary from unit dictionary — make_patch_dict","text":"","code":"make_patch_dict(minpatch_data)"},{"path":"/reference/make_patch_dict.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Create patch dictionary from unit dictionary — make_patch_dict","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/make_patch_dict.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Create patch dictionary from unit dictionary — make_patch_dict","text":"Named list patch contains area, unit count, unit IDs","code":""},{"path":"/reference/minpatch-package.html","id":null,"dir":"Reference","previous_headings":"","what":"MinPatch for R: Post-processing prioritizr solutions to ensure minimum patch sizes — minpatch-package","title":"MinPatch for R: Post-processing prioritizr solutions to ensure minimum patch sizes — minpatch-package","text":"package provides functions post-process conservation planning solutions prioritizr ensure protected areas meet user-defined minimum size thresholds, following methodology described Smith et al. (2010).","code":""},{"path":[]},{"path":"/reference/minpatch-package.html","id":"author","dir":"Reference","previous_headings":"","what":"Author","title":"MinPatch for R: Post-processing prioritizr solutions to ensure minimum patch sizes — minpatch-package","text":"Maintainer: Jason D. Everett DrJasonEverett@gmail.com (ORCID) Authors: Anthony J. Richardson .richardson1@uq.edu.au (ORCID) Robert J. Smith (Original MinPatch algorithm author)","code":""},{"path":"/reference/pipe.html","id":null,"dir":"Reference","previous_headings":"","what":"Pipe operator — %>%","title":"Pipe operator — %>%","text":"See magrittr::%>% details.","code":""},{"path":"/reference/pipe.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Pipe operator — %>%","text":"","code":"lhs %>% rhs"},{"path":"/reference/pipe.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Pipe operator — %>%","text":"lhs value magrittr placeholder. rhs function call using magrittr semantics.","code":""},{"path":"/reference/pipe.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Pipe operator — %>%","text":"result calling `rhs(lhs)`.","code":""},{"path":"/reference/plot_minpatch.html","id":null,"dir":"Reference","previous_headings":"","what":"Visualize MinPatch results — plot_minpatch","title":"Visualize MinPatch results — plot_minpatch","text":"Creates simple visualization MinPatch results showing original vs. modified solutions","code":""},{"path":"/reference/plot_minpatch.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Visualize MinPatch results — plot_minpatch","text":"","code":"plot_minpatch(minpatch_result, title = \"MinPatch Results\")"},{"path":"/reference/plot_minpatch.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Visualize MinPatch results — plot_minpatch","text":"minpatch_result Result run_minpatch function title Plot title (optional)","code":""},{"path":"/reference/plot_minpatch.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Visualize MinPatch results — plot_minpatch","text":"ggplot object (ggplot2 available)","code":""},{"path":"/reference/plot_minpatch.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Visualize MinPatch results — plot_minpatch","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, verbose = FALSE ) # Visualize results plot_minpatch(result)"},{"path":"/reference/plot_patch_validity.html","id":null,"dir":"Reference","previous_headings":"","what":"Plot rook-adjacency patches for a MinPatch solution and flag valid/invalid patches — plot_patch_validity","title":"Plot rook-adjacency patches for a MinPatch solution and flag valid/invalid patches — plot_patch_validity","text":"patch rook-connected cluster selected planning units (edge-sharing). patch \"valid\" area >= (multiplier x median PU area), small numeric tolerance avoid floating-point edge cases.","code":""},{"path":"/reference/plot_patch_validity.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plot rook-adjacency patches for a MinPatch solution and flag valid/invalid patches — plot_patch_validity","text":"","code":"plot_patch_validity( multiplier, multipliers, minpatch_results, pu_sf = NULL, return = c(\"plot\", \"counts\", \"patches\"), do_snap = TRUE, snap_size = 1, eps_rel = 1e-09, debug = FALSE )"},{"path":"/reference/plot_patch_validity.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plot rook-adjacency patches for a MinPatch solution and flag valid/invalid patches — plot_patch_validity","text":"multiplier Numeric. MinPatch multiplier plot (must exist `multipliers`). multipliers Numeric vector multipliers (order `minpatch_results`). minpatch_results List. element contains `$solution` sf object column `minpatch`. pu_sf sf. Planning-unit sf used compute median PU area (defaults Seychelles_sf present). return Character. \"plot\" (ggplot), \"counts\" (tibble), \"patches\" (sf patch polygons). do_snap Logical. TRUE, snap geometries adjacency robustness. snap_size Numeric. Grid size passed `lwgeom::st_snap_to_grid()` (CRS units). eps_rel Numeric. Relative tolerance applied area threshold (default 1e-9). debug Logical. TRUE, prints patch table (including diff vs threshold).","code":""},{"path":"/reference/plot_patch_validity.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plot rook-adjacency patches for a MinPatch solution and flag valid/invalid patches — plot_patch_validity","text":"ggplot object, tibble, sf patch polygons (see `return`).","code":""},{"path":"/reference/plot_prioritizr.html","id":null,"dir":"Reference","previous_headings":"","what":"Visualize prioritizr solutions — plot_prioritizr","title":"Visualize prioritizr solutions — plot_prioritizr","text":"Creates simple visualization prioritizr solutions showing selected unselected planning units using ggplot2","code":""},{"path":"/reference/plot_prioritizr.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Visualize prioritizr solutions — plot_prioritizr","text":"","code":"plot_prioritizr(s, col = \"solution_1\", title = \"prioritizr Solution\")"},{"path":"/reference/plot_prioritizr.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Visualize prioritizr solutions — plot_prioritizr","text":"s sf object containing planning units solution data col Column name containing solution values (default = \"solution_1\") title Plot title (default = \"prioritizr Solution\")","code":""},{"path":"/reference/plot_prioritizr.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Visualize prioritizr solutions — plot_prioritizr","text":"ggplot object showing spatial solution","code":""},{"path":"/reference/plot_prioritizr.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Visualize prioritizr solutions — plot_prioritizr","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Plot the solution plot_prioritizr(s) # Plot with custom title and column plot_prioritizr(s, col = \"solution_1\", title = \"My Conservation Plan\")"},{"path":"/reference/plot_solution_grid.html","id":null,"dir":"Reference","previous_headings":"","what":"Plot solution grid across multiple results — plot_solution_grid","title":"Plot solution grid across multiple results — plot_solution_grid","text":"Creates grid prioritizr-style selection maps shared legend. Useful comparing solutions across different boundary penalties MinPatch multipliers.","code":""},{"path":"/reference/plot_solution_grid.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Plot solution grid across multiple results — plot_solution_grid","text":"","code":"plot_solution_grid( results, get_solution_fun, ncol = 3, legend_position = \"bottom\", title_size = 13, titles = NULL, title_fun = NULL, subtitle_values = NULL, title_prefix = \"\", value_label_fun = function(x) format(x, scientific = TRUE, trim = TRUE), include_baseline = FALSE, baseline_solution = NULL, baseline_title = NULL )"},{"path":"/reference/plot_solution_grid.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Plot solution grid across multiple results — plot_solution_grid","text":"results List MinPatch result objects plot get_solution_fun Function extract solution sf object result ncol Number columns grid (default = 3) legend_position Position shared legend (default = \"bottom\") title_size Font size plot titles (default = 13) titles Character vector titles (one per result). Takes priority title options. title_fun Function(, results) returning title string. Used titles NULL. subtitle_values Numeric vector values (e.g., penalties multipliers) titles title_prefix Prefix string subtitle_values titles (e.g., \"Boundary penalty = \") value_label_fun Function format subtitle_values (default formats scientifically) include_baseline Logical, whether include baseline plot (default = FALSE) baseline_solution sf object baseline solution (required include_baseline = TRUE) baseline_title Title baseline plot","code":""},{"path":"/reference/plot_solution_grid.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Plot solution grid across multiple results — plot_solution_grid","text":"patchwork object combining plots","code":""},{"path":"/reference/print_minpatch_summary.html","id":null,"dir":"Reference","previous_headings":"","what":"Print MinPatch results summary — print_minpatch_summary","title":"Print MinPatch results summary — print_minpatch_summary","text":"Prints formatted summary MinPatch processing results","code":""},{"path":"/reference/print_minpatch_summary.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Print MinPatch results summary — print_minpatch_summary","text":"","code":"print_minpatch_summary(minpatch_result)"},{"path":"/reference/print_minpatch_summary.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Print MinPatch results summary — print_minpatch_summary","text":"minpatch_result Result object run_minpatch function","code":""},{"path":"/reference/print_minpatch_summary.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Print MinPatch results summary — print_minpatch_summary","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 5 #> Unmet feature IDs: 1, 2, 3, 4, 5 #> Iteration 1 - Unmet targets: 5 #> Found 85 potential patches with scores #> Best score: 0.002500108 for unit 90 #> Added patch centered on unit 90 #> Iteration 2 - Unmet targets: 4 #> Found 76 potential patches with scores #> Best score: 0.002060928 for unit 78 #> Added patch centered on unit 78 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 7 #> Keystone units: 0 #> New keystone units: 0 #> Scoreable units: 7 #> Unit 73 cannot be removed - adding to keystone set #> Edge units found: 6 #> Keystone units: 1 #> New keystone units: 0 #> Scoreable units: 6 #> Unit 72 cannot be removed - adding to keystone set #> Edge units found: 5 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 5 #> Removed unit 64 at iteration 3 #> Edge units found: 4 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 4 #> Removed unit 63 at iteration 4 #> Edge units found: 4 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 4 #> Removed unit 54 at iteration 5 #> Unit 71 cannot be removed - adding to keystone set #> Unit 61 cannot be removed - adding to keystone set #> Unit 62 cannot be removed - adding to keystone set #> No more edge units to consider - terminating #> Calculating final statistics... #> MinPatch processing complete! print_minpatch_summary(result) #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 7 (valid: 1) #> Final patches: 4 (valid: 2) #> Area change: 0.02 (12.5%) #> #> Cost Breakdown: #> Planning unit cost: 3535.08 #> Boundary cost: 0.00 #> Total cost: 3535.08 #> Selected units: 18 #> #> Feature Representation: #> Total features: 5 #> Targets met: 5 #> Targets unmet: 0 #> Mean proportion: 0.201 #> Total shortfall: 0.00 #> #> #> === End Summary ==="},{"path":"/reference/removal_increases_cost.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would increase cost — removal_increases_cost","title":"Check if removing unit would increase cost — removal_increases_cost","text":"Check removing unit increase cost","code":""},{"path":"/reference/removal_increases_cost.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would increase cost — removal_increases_cost","text":"","code":"removal_increases_cost(unit_id, minpatch_data)"},{"path":"/reference/removal_increases_cost.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would increase cost — removal_increases_cost","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_increases_cost.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would increase cost — removal_increases_cost","text":"Logical indicating removal increase cost","code":""},{"path":"/reference/removal_increases_marxan_cost.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","title":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","text":"Check removing unit increase Marxan cost","code":""},{"path":"/reference/removal_increases_marxan_cost.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","text":"","code":"removal_increases_marxan_cost(unit_id, minpatch_data)"},{"path":"/reference/removal_increases_marxan_cost.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_increases_marxan_cost.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would increase Marxan cost — removal_increases_marxan_cost","text":"Logical indicating removal increase cost","code":""},{"path":"/reference/removal_makes_patch_too_small.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","title":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","text":"Check removing unit make patch small","code":""},{"path":"/reference/removal_makes_patch_too_small.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","text":"","code":"removal_makes_patch_too_small(unit_id, minpatch_data)"},{"path":"/reference/removal_makes_patch_too_small.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_makes_patch_too_small.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would make its patch too small — removal_makes_patch_too_small","text":"Logical indicating removal make patch small","code":""},{"path":"/reference/removal_splits_patch_nonviably.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","title":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","text":"Check removing unit split patch non-viable pieces","code":""},{"path":"/reference/removal_splits_patch_nonviably.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","text":"","code":"removal_splits_patch_nonviably(unit_id, minpatch_data)"},{"path":"/reference/removal_splits_patch_nonviably.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/removal_splits_patch_nonviably.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would split patch into non-viable pieces — removal_splits_patch_nonviably","text":"Logical indicating removal create non-viable patches","code":""},{"path":"/reference/removal_violates_targets.html","id":null,"dir":"Reference","previous_headings":"","what":"Check if removing unit would violate conservation targets — removal_violates_targets","title":"Check if removing unit would violate conservation targets — removal_violates_targets","text":"Optimized accept pre-computed feature amounts avoid redundant calculations.","code":""},{"path":"/reference/removal_violates_targets.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Check if removing unit would violate conservation targets — removal_violates_targets","text":"","code":"removal_violates_targets(unit_id, minpatch_data, feature_amounts = NULL)"},{"path":"/reference/removal_violates_targets.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Check if removing unit would violate conservation targets — removal_violates_targets","text":"unit_id ID unit potentially remove minpatch_data List containing MinPatch data structures feature_amounts Optional pre-computed feature conservation amounts","code":""},{"path":"/reference/removal_violates_targets.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Check if removing unit would violate conservation targets — removal_violates_targets","text":"Logical indicating removal violate targets","code":""},{"path":"/reference/remove_small_patches_from_solution.html","id":null,"dir":"Reference","previous_headings":"","what":"Remove small patches from solution — remove_small_patches_from_solution","title":"Remove small patches from solution — remove_small_patches_from_solution","text":"Stage 1 MinPatch: Remove patches smaller minimum size threshold","code":""},{"path":"/reference/remove_small_patches_from_solution.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Remove small patches from solution — remove_small_patches_from_solution","text":"","code":"remove_small_patches_from_solution(minpatch_data)"},{"path":"/reference/remove_small_patches_from_solution.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Remove small patches from solution — remove_small_patches_from_solution","text":"minpatch_data List containing MinPatch data structures","code":""},{"path":"/reference/remove_small_patches_from_solution.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Remove small patches from solution — remove_small_patches_from_solution","text":"Updated minpatch_data small patches removed","code":""},{"path":"/reference/run_minpatch.html","id":null,"dir":"Reference","previous_headings":"","what":"Run MinPatch algorithm on prioritizr solution — run_minpatch","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"main function applies MinPatch algorithm prioritizr solution ensure protected areas meet minimum size thresholds. function uses prioritizr summary functions possible reduce code duplication ensure consistency prioritizr calculations.","code":""},{"path":"/reference/run_minpatch.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"","code":"run_minpatch( prioritizr_problem, prioritizr_solution, min_patch_size, patch_radius, boundary_penalty = 0, remove_small_patches = TRUE, add_patches = TRUE, whittle_patches = TRUE, solution_column = \"solution_1\", verbose = TRUE, debug_boundary = FALSE, debug_boundary_every = 50 )"},{"path":"/reference/run_minpatch.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"prioritizr_problem prioritizr problem object prioritizr_solution solved prioritizr solution object min_patch_size Minimum patch size threshold patch_radius Radius adding new patches boundary_penalty Boundary penalty value (default = 0) remove_small_patches Logical, whether remove small patches (Stage 1, default = TRUE) add_patches Logical, whether add new patches meet targets (Stage 2, default = TRUE) whittle_patches Logical, whether remove unnecessary units (Stage 3, default = TRUE) solution_column Name solution column (default = \"solution_1\") verbose Logical, whether print progress (default = TRUE) debug_boundary Logical, whether print boundary cost debug info (default = FALSE) debug_boundary_every Integer, print debug info every N iterations (default = 50)","code":""},{"path":"/reference/run_minpatch.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"MinPatch result object enhanced reporting using prioritizr functions","code":""},{"path":"/reference/run_minpatch.html","id":"details","dir":"Reference","previous_headings":"","what":"Details","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"MinPatch algorithm consists three stages: Remove small patches: Removes patches smaller min_patch_size Add new patches: Adds patches meet conservation targets Whittle patches: Removes unnecessary planning units **Locked Constraints**: MinPatch automatically respects locked-locked-constraints prioritizr problems (added via add_locked_in_constraints() add_locked_out_constraints()): **Locked-units**: never removed, regardless patch size whittling. treated \"conserved\" areas must retained. **Locked-units**: never selected, even adding new patches meet conservation targets. completely excluded consideration. locked-units form patches smaller min_patch_size, warning issued, units still preserved. **Important**: set remove_small_patches = TRUE add_patches = FALSE, algorithm may remove patches without compensating, potentially violating conservation targets. cases, warning issued. Consider using add_patches = TRUE smaller min_patch_size maintain target achievement.","code":""},{"path":"/reference/run_minpatch.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Run MinPatch algorithm on prioritizr solution — run_minpatch","text":"","code":"library(prioritizr) library(sf) library(terra) # Get example data from prioritizr dat <- c(get_sim_pu_raster(), get_sim_features()) %>% as.polygons(dissolve = FALSE, values = TRUE) %>% sf::st_as_sf() %>% dplyr::rename(cost = layer) st_crs(dat) <- NA features = colnames(dat) %>% stringr::str_subset(\"feature_\") # Create prioritizr problem p <- problem(dat, features, cost_column = \"cost\") %>% add_min_set_objective() %>% add_relative_targets(0.17) %>% # 17% of each feature add_binary_decisions() %>% add_default_solver(verbose = FALSE) # Solve problem s <- solve(p) # Apply MinPatch with all stages result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Stage 2: Adding new patches... #> Initial unmet targets: 5 #> Unmet feature IDs: 1, 2, 3, 4, 5 #> Iteration 1 - Unmet targets: 5 #> Found 85 potential patches with scores #> Best score: 0.002500108 for unit 90 #> Added patch centered on unit 90 #> Iteration 2 - Unmet targets: 4 #> Found 76 potential patches with scores #> Best score: 0.002060928 for unit 78 #> Added patch centered on unit 78 #> All conservation targets are now met! #> Stage 3: Removing unnecessary planning units... #> Edge units found: 7 #> Keystone units: 0 #> New keystone units: 0 #> Scoreable units: 7 #> Unit 73 cannot be removed - adding to keystone set #> Edge units found: 6 #> Keystone units: 1 #> New keystone units: 0 #> Scoreable units: 6 #> Unit 72 cannot be removed - adding to keystone set #> Edge units found: 5 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 5 #> Removed unit 64 at iteration 3 #> Edge units found: 4 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 4 #> Removed unit 63 at iteration 4 #> Edge units found: 4 #> Keystone units: 2 #> New keystone units: 0 #> Scoreable units: 4 #> Removed unit 54 at iteration 5 #> Unit 71 cannot be removed - adding to keystone set #> Unit 61 cannot be removed - adding to keystone set #> Unit 62 cannot be removed - adding to keystone set #> No more edge units to consider - terminating #> Calculating final statistics... #> MinPatch processing complete! # Apply MinPatch with only Stage 1 and 3 (skip adding patches) result2 <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 0.05, patch_radius = 0.3, add_patches = FALSE ) #> Validating inputs... #> Initializing data structures... #> Calculating boundary matrix (optimized version)... #> Creating patch radius dictionary (optimized)... #> Calculating initial patch statistics... #> Stage 1: Removing small patches... #> Warning: After removing small patches, 5 conservation targets are no longer met. Consider setting add_patches = TRUE to automatically add patches to meet targets, or use a smaller min_patch_size. #> Warning: 5 targets are no longer met after removing small patches #> Unmet feature IDs: 1, 2, 3, 4, 5 #> Stage 2: Skipping addition of new patches... #> Stage 3: Removing unnecessary planning units... #> Edge units found: 4 #> Keystone units: 0 #> New keystone units: 4 #> Scoreable units: 0 #> No units can be removed - all are keystone - terminating #> Calculating final statistics... #> MinPatch processing complete! print_minpatch_summary(result) #> === MinPatch Processing Summary === #> #> Patch Statistics: #> Initial patches: 7 (valid: 1) #> Final patches: 4 (valid: 2) #> Area change: 0.02 (12.5%) #> #> Cost Breakdown: #> Planning unit cost: 3535.08 #> Boundary cost: 0.00 #> Total cost: 3535.08 #> Selected units: 18 #> #> Feature Representation: #> Total features: 5 #> Targets met: 5 #> Targets unmet: 0 #> Mean proportion: 0.201 #> Total shortfall: 0.00 #> #> #> === End Summary ==="},{"path":"/reference/simulated_whittling.html","id":null,"dir":"Reference","previous_headings":"","what":"Simulated whittling to remove unnecessary planning units — simulated_whittling","title":"Simulated whittling to remove unnecessary planning units — simulated_whittling","text":"Stage 3 MinPatch: Remove planning units needed meet targets, reduce fragmentation, meet minimum patch size requirements","code":""},{"path":"/reference/simulated_whittling.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Simulated whittling to remove unnecessary planning units — simulated_whittling","text":"","code":"simulated_whittling(minpatch_data, verbose = TRUE)"},{"path":"/reference/simulated_whittling.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Simulated whittling to remove unnecessary planning units — simulated_whittling","text":"minpatch_data List containing MinPatch data structures (including prioritizr objects) verbose Logical, whether print progress","code":""},{"path":"/reference/simulated_whittling.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Simulated whittling to remove unnecessary planning units — simulated_whittling","text":"Updated minpatch_data unnecessary units removed","code":""},{"path":"/reference/validate_inputs.html","id":null,"dir":"Reference","previous_headings":"","what":"Validate MinPatch inputs — validate_inputs","title":"Validate MinPatch inputs — validate_inputs","text":"Internal function validate inputs MinPatch algorithm, including locked-locked-constraints","code":""},{"path":"/reference/validate_inputs.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Validate MinPatch inputs — validate_inputs","text":"","code":"validate_inputs( solution, planning_units, targets, costs, min_patch_size, patch_radius, boundary_penalty, locked_in_indices = NULL, locked_out_indices = NULL, area_dict = NULL, verbose = TRUE )"},{"path":"/reference/validate_inputs.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Validate MinPatch inputs — validate_inputs","text":"solution Binary solution vector planning_units sf object planning units targets data.frame targets costs numeric vector costs min_patch_size minimum patch size patch_radius patch radius adding patches boundary_penalty Boundary penalty value locked_in_indices Optional indices locked-planning units locked_out_indices Optional indices locked-planning units area_dict Optional area dictionary locked-patch size validation verbose Logical, whether print warnings","code":""},{"path":"/reference/validate_inputs.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Validate MinPatch inputs — validate_inputs","text":"NULL (throws errors validation fails)","code":""},{"path":"/reference/visualize_minpatch_results.html","id":null,"dir":"Reference","previous_headings":"","what":"Visualize MinPatch results — plot_minpatch","title":"Visualize MinPatch results — plot_minpatch","text":"Creates simple visualization MinPatch results showing original vs. modified solutions","code":""},{"path":"/reference/visualize_minpatch_results.html","id":"ref-usage","dir":"Reference","previous_headings":"","what":"Usage","title":"Visualize MinPatch results — plot_minpatch","text":"","code":"plot_minpatch(minpatch_result, title = \"MinPatch Results\")"},{"path":"/reference/visualize_minpatch_results.html","id":"arguments","dir":"Reference","previous_headings":"","what":"Arguments","title":"Visualize MinPatch results — plot_minpatch","text":"minpatch_result Result run_minpatch function title Plot title (optional)","code":""},{"path":"/reference/visualize_minpatch_results.html","id":"value","dir":"Reference","previous_headings":"","what":"Value","title":"Visualize MinPatch results — plot_minpatch","text":"ggplot object (ggplot2 available)","code":""},{"path":"/reference/visualize_minpatch_results.html","id":"ref-examples","dir":"Reference","previous_headings":"","what":"Examples","title":"Visualize MinPatch results — plot_minpatch","text":"","code":"if (FALSE) { # \\dontrun{ # Requires ggplot2 library(ggplot2) # Create example data example_data <- create_example_data(n_units = 25, n_features = 3) # Create prioritizr problem and solve library(prioritizr) p <- problem(example_data$planning_units, cost_column = \"cost\") %>% add_min_set_objective() %>% add_manual_targets(example_data$targets) %>% add_binary_decisions() s <- solve(p) # Run MinPatch result <- run_minpatch( prioritizr_problem = p, prioritizr_solution = s, min_patch_size = 2.0, patch_radius = 1.5 ) # Visualize results plot <- plot_minpatch(result) print(plot) } # }"}]
diff --git a/docs/sitemap.xml b/docs/sitemap.xml
index 7d8dab1..87f89e4 100644
--- a/docs/sitemap.xml
+++ b/docs/sitemap.xml
@@ -1,6 +1,8 @@
/404.html
+/articles/boundary-penalty.html
/articles/index.html
+/articles/minpatch-oceandatr-Seychelles.html
/articles/minpatch.html
/articles/minpatchTasmania.html
/authors.html
@@ -21,16 +23,22 @@
/reference/create_boundary_matrix.html
/reference/create_patch_radius_dict.html
/reference/create_solution_vector.html
+/reference/extract_locked_in_constraints.html
+/reference/extract_locked_out_constraints.html
/reference/find_edge_units.html
/reference/generate_minpatch_report.html
+/reference/get_solution_with_solution1.html
/reference/identify_unmet_targets.html
/reference/index.html
/reference/initialize_minpatch_data.html
+/reference/make_minpatch_summary_table.html
/reference/make_patch_dict.html
/reference/minpatch-package.html
/reference/pipe.html
/reference/plot_minpatch.html
+/reference/plot_patch_validity.html
/reference/plot_prioritizr.html
+/reference/plot_solution_grid.html
/reference/print_minpatch_summary.html
/reference/removal_increases_cost.html
/reference/removal_increases_marxan_cost.html
diff --git a/man/figures/minpatch.png b/man/figures/minpatch.png
new file mode 100644
index 0000000..fa57601
Binary files /dev/null and b/man/figures/minpatch.png differ
diff --git a/man/make_minpatch_summary_table.Rd b/man/make_minpatch_summary_table.Rd
new file mode 100644
index 0000000..34f6899
--- /dev/null
+++ b/man/make_minpatch_summary_table.Rd
@@ -0,0 +1,76 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/output.R
+\name{make_minpatch_summary_table}
+\alias{make_minpatch_summary_table}
+\title{Make MinPatch summary table}
+\usage{
+make_minpatch_summary_table(
+ region_sf,
+ baseline_solution_sf,
+ minpatch_results,
+ mode = c("multiplier", "boundary_penalty"),
+ multipliers = NULL,
+ boundary_penalties = NULL,
+ fixed_multiplier = NA_real_,
+ compare_solutions_fun = NULL,
+ baseline_compare_obj = NULL,
+ baseline_elapsed = NA_real_,
+ minpatch_elapsed = NULL,
+ projected_epsg = 32740,
+ baseline_solution_col = NULL,
+ minpatch_solution_col = "minpatch",
+ cost_col = "cost",
+ baseline_boundary_cost = 0,
+ make_kable = TRUE
+)
+}
+\arguments{
+\item{region_sf}{sf of all planning units (used to compute median PU area for min patch size).}
+
+\item{baseline_solution_sf}{sf containing the baseline solution selection column and PU costs.}
+
+\item{minpatch_results}{List of MinPatch results; each must contain `$solution` (sf).}
+
+\item{mode}{One of "multiplier" or "boundary_penalty".}
+
+\item{multipliers}{Numeric vector; required if mode="multiplier".}
+
+\item{boundary_penalties}{Numeric vector; required if mode="boundary_penalty".}
+
+\item{fixed_multiplier}{Numeric scalar used only if mode="boundary_penalty" (e.g., 5 for "5× median PU area").}
+
+\item{compare_solutions_fun}{Function that returns a list with `$overall`.
+If NULL, the function will try to find `compare_solutions()` in your session.}
+
+\item{baseline_compare_obj}{Object to pass into compare_solutions() for baseline "Original" metrics.
+If NULL, defaults to `minpatch_results[[1]]`.}
+
+\item{baseline_elapsed}{Baseline runtime (seconds). Optional.}
+
+\item{minpatch_elapsed}{MinPatch runtimes (seconds). Optional; must match number of runs.}
+
+\item{projected_epsg}{EPSG for perimeter calcs if inputs are lon/lat (default 32740).}
+
+\item{baseline_solution_col}{Baseline selection column name (1/0). If NULL, auto-detects.}
+
+\item{minpatch_solution_col}{MinPatch selection column in `$solution` (default "minpatch").}
+
+\item{cost_col}{PU cost column in `baseline_solution_sf` (default "cost").}
+
+\item{baseline_boundary_cost}{Baseline boundary cost (default 0).}
+
+\item{make_kable}{If TRUE, also returns an HTML kable.}
+}
+\value{
+list(data = tibble, kable = html or NULL)
+}
+\description{
+Builds a single table summarising a baseline solution and multiple MinPatch solutions.
+Baseline patch metrics come from `compare_solutions(... )$overall[,"Original"]`, while
+MinPatch metrics come from `compare_solutions(... )$overall[,"MinPatch"]`.
+}
+\details{
+Two modes:
+- mode = "multiplier": multiple min patch sizes (uses `multipliers`)
+- mode = "boundary_penalty": one min patch size, multiple boundary penalties (uses `boundary_penalties`)
+}
diff --git a/man/plot_patch_validity.Rd b/man/plot_patch_validity.Rd
new file mode 100644
index 0000000..998fd20
--- /dev/null
+++ b/man/plot_patch_validity.Rd
@@ -0,0 +1,45 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/output.R
+\name{plot_patch_validity}
+\alias{plot_patch_validity}
+\title{Plot rook-adjacency patches for a MinPatch solution and flag valid/invalid patches}
+\usage{
+plot_patch_validity(
+ multiplier,
+ multipliers,
+ minpatch_results,
+ pu_sf = NULL,
+ return = c("plot", "counts", "patches"),
+ do_snap = TRUE,
+ snap_size = 1,
+ eps_rel = 1e-09,
+ debug = FALSE
+)
+}
+\arguments{
+\item{multiplier}{Numeric. The MinPatch multiplier to plot (must exist in `multipliers`).}
+
+\item{multipliers}{Numeric vector of multipliers (same order as `minpatch_results`).}
+
+\item{minpatch_results}{List. Each element contains a `$solution` sf object with column `minpatch`.}
+
+\item{pu_sf}{sf. Planning-unit sf used to compute median PU area (defaults to Seychelles_sf if present).}
+
+\item{return}{Character. "plot" (ggplot), "counts" (tibble), or "patches" (sf patch polygons).}
+
+\item{do_snap}{Logical. If TRUE, snap geometries before adjacency for robustness.}
+
+\item{snap_size}{Numeric. Grid size passed to `lwgeom::st_snap_to_grid()` (CRS units).}
+
+\item{eps_rel}{Numeric. Relative tolerance applied to the area threshold (default 1e-9).}
+
+\item{debug}{Logical. If TRUE, prints patch table (including diff vs threshold).}
+}
+\value{
+ggplot object, or tibble, or sf patch polygons (see `return`).
+}
+\description{
+A patch is a rook-connected cluster of selected planning units (edge-sharing).
+A patch is "valid" if its area >= (multiplier x median PU area), with a small
+numeric tolerance to avoid floating-point edge cases.
+}
diff --git a/man/run_minpatch.Rd b/man/run_minpatch.Rd
index 77bc4fb..e1f51dc 100644
--- a/man/run_minpatch.Rd
+++ b/man/run_minpatch.Rd
@@ -14,7 +14,9 @@ run_minpatch(
add_patches = TRUE,
whittle_patches = TRUE,
solution_column = "solution_1",
- verbose = TRUE
+ verbose = TRUE,
+ debug_boundary = FALSE,
+ debug_boundary_every = 50
)
}
\arguments{
@@ -37,6 +39,10 @@ run_minpatch(
\item{solution_column}{Name of solution column (default = "solution_1")}
\item{verbose}{Logical, whether to print progress (default = TRUE)}
+
+\item{debug_boundary}{Logical, whether to print boundary cost debug info (default = FALSE)}
+
+\item{debug_boundary_every}{Integer, print debug info every N iterations (default = 50)}
}
\value{
MinPatch result object with enhanced reporting using prioritizr functions
diff --git a/minpatch.Rproj b/minpatch.Rproj
index 497f8bf..b9d3f8e 100644
--- a/minpatch.Rproj
+++ b/minpatch.Rproj
@@ -1,4 +1,5 @@
Version: 1.0
+ProjectId: e7f59136-d7bf-450a-af45-49f3edffd1a1
RestoreWorkspace: Default
SaveWorkspace: Default
diff --git a/vignettes/427_601_-133_01_tmp.tif b/vignettes/427_601_-133_01_tmp.tif
new file mode 100644
index 0000000..7ed7288
Binary files /dev/null and b/vignettes/427_601_-133_01_tmp.tif differ
diff --git a/vignettes/_build/feature_set.rds b/vignettes/_build/feature_set.rds
new file mode 100644
index 0000000..3c5b225
Binary files /dev/null and b/vignettes/_build/feature_set.rds differ
diff --git a/vignettes/_build/minpatch_results.rds b/vignettes/_build/minpatch_results.rds
new file mode 100644
index 0000000..59ab0f5
Binary files /dev/null and b/vignettes/_build/minpatch_results.rds differ
diff --git a/vignettes/_build/minpatch_times.rds b/vignettes/_build/minpatch_times.rds
new file mode 100644
index 0000000..ec99aa4
Binary files /dev/null and b/vignettes/_build/minpatch_times.rds differ
diff --git a/vignettes/boundary-penalty.Rmd b/vignettes/boundary-penalty.Rmd
new file mode 100644
index 0000000..db95e77
--- /dev/null
+++ b/vignettes/boundary-penalty.Rmd
@@ -0,0 +1,346 @@
+---
+title: "Boundary penalty in MinPatch"
+output: rmarkdown::html_vignette
+vignette: >
+ %\VignetteIndexEntry{Boundary penalty in MinPatch}
+ %\VignetteEncoding{UTF-8}
+ %\VignetteEngine{knitr::rmarkdown}
+editor_options:
+ markdown:
+ wrap: 72
+---
+
+```{r setup, include=FALSE}
+knitr::opts_chunk$set( echo = FALSE,
+message = FALSE, warning = FALSE )
+```
+
+## Overview
+
+`boundary_penalty` is an optional parameter that discourages irregular
+(high-perimeter) patch shapes during **Stage 3 (Simulated Whittling)**.
+The key idea is that `boundary_penalty` converts **boundary length
+(perimeter)** into the same “currency” as planning-unit cost, so
+MinPatch can trade off saving cost against creating more edge.
+
+In practice:
+
+- higher `boundary_penalty` → stronger preference for smooth, compact
+ patches (less edge)
+
+- lower `boundary_penalty` → more willingness to accept jagged edges
+ if they reduce planning-unit cos
+
+## Where it is used in MinPatch
+
+MinPatch does **not** re-run the optimiser in Stage 3. Instead, it tests
+removing one **edge** planning unit at a time and accepts the removal
+only if:
+
+1. Targets remain met
+
+2. Minimum patch-size requirements remain met (including after splits)
+
+3. The boundary-penalised objective does **not** increase (only if
+ `boundary_penalty > 0`)
+
+This means Stage 3 uses a **local “delta” test** (change in objective)
+rather than solving a new optimisation problem.
+
+## Decision rule (the thing to remember)
+
+Conceptually, Stage 3 evaluates an objective of the form:
+
+$$
+\text{Objective} = \sum(\text{PU cost}) + \text{boundary_penalty} \times (\text{total perimeter})
+$$
+
+When testing removal of a single planning unit, the code computes a
+local change:
+
+$$ \Delta \text{Objective} = (\text{boundary_penalty} \times \Delta \text{perimeter}) - \text{unit_cost} $$
+
+\- If $\Delta \text{Objective} > 0$ → objective increased → **block**
+removal\
+- If $\Delta \text{Objective} \le 0$ → objective stayed the same or
+decreased → **allow** removal (assuming constraints pass)
+
+### Why this looks like “total_cost_change” in the code
+
+In the code, you’ll often see a value like:
+
+`total_cost_change = boundary_cost_change - unit_cost`
+
+That is exactly $\Delta \text{Objective}$ written in code form:
+
+- `boundary_cost_change` corresponds to
+ `boundary_penalty × Δperimeter`
+
+- `unit_cost` is the cost you save by removing the unit
+
+So: `total_cost_change` **is the change in the objective**.
+
+## What does “same currency as cost” mean?
+
+The objective combines two different things:
+
+1. **Planning-unit cost** (whatever you choose: dollars, area,
+ opportunity cost, etc.)
+
+2. **Perimeter** (boundary length: km, m, or “edge count” depending on
+ your data)
+
+To add these together, `boundary_penalty` acts like a **conversion
+rate**: it converts boundary length into “cost units”.
+
+$$\text{Objective} = \sum(\text{PU cost}) + \text{boundary_penalty} \times (\text{total perimeter})$$
+
+### If your PU cost is in dollars
+
+- PU cost might be: $\$ / \text{PU}$ or $\$ / \text{km}^2$
+
+- perimeter is in km
+
+- so `boundary_penalty` behaves like **dollars per km of edge**:
+ $\$ / \text{km}$
+
+**Interpretation:**\
+`boundary_penalty` is the *price you are willing to pay* to avoid 1 km
+of extra boundary.
+
+Example: if `boundary_penalty = 2000`, then adding 1 km of perimeter
+“costs” \$2000 in the objective.
+
+### If your PU cost is area
+
+Sometimes “cost” is literally area (e.g., each PU cost = area in km²).
+Then:
+
+- PU cost is in km²
+
+- perimeter is in km
+
+- `boundary_penalty` behaves like **km² per km**
+
+You can think of this as “equivalent area cost per km of edge”.
+
+**Interpretation:**\
+`boundary_penalty` tells you how much extra *area cost* you are willing
+to accept to avoid 1 km of extra boundary.
+
+So if `boundary_penalty = 0.5`, then an extra 1 km of edge is treated
+like adding 0.5 km² of cost.
+
+### The simplest way to remember it
+
+- **unit_cost** = the savings you get from removing a PU
+
+- **boundary_penalty × Δperimeter** = the “edge bill” you pay (or
+ save) when boundary changes
+
+And Stage 3 only removes a PU when:
+
+$$ \Delta \text{Objective} = (\text{boundary_penalty} \times \Delta \text{perimeter}) - \text{unit_cost} \le 0 $$
+
+So you can read it as:
+
+> Only remove the PU if the edge bill doesn’t outweigh the cost saved.
+
+## Worked examples (with explicit total perimeter)
+
+Assumptions for both examples:
+
+- 4-neighbour adjacency (up/down/left/right)
+- each shared cell edge has length **1 km**
+- `total perimeter` is the perimeter of the **whole selected patch**
+ (in km)
+
+A very useful identity (grid perimeter change):
+
+If a removed PU has $k$ **selected neighbours** (0–4), then removing it
+changes perimeter by:
+
+$$
+\Delta \text{perimeter} = -4 + 2k
+$$
+
+So perimeter **increases** only when $k \ge 3$ (i.e., the PU was
+“shielding” three selected neighbours).
+
+------------------------------------------------------------------------
+
+### Example A — removing an edge PU reduces total perimeter (allowed)
+
+Here the patch has two selected PUs in a line. `U` is an **edge unit**
+because it touches `.` above, below, and right.
+
+**Before removing U**
+
+``` text
+| . | . |
+| S | U |
+| . | . |
+```
+
+Compute the **total perimeter**:
+
+- Two adjacent cells have perimeter =6 km\
+ (Each cell has 4 edges → 8 total, minus 2 for the shared edge → 6)
+
+So:
+
+- $P{\text{before}}$= 6 km
+
+**After removing U**
+
+``` text
+| . | . |
+| S | . |
+| . | . |
+```
+
+- Single cell has perimeter = 4 km
+
+So:
+
+- $P{\text{after}}$ = 4 km
+
+- $\Delta \text{perimeter}$ = 4 - 6 =−2 km
+
+Now apply the Stage 3 boundary check:
+
+Assume:
+
+- `boundary_penalty = 10`
+
+- `unit_cost = 5`
+
+$$\Delta \text{Objective} = (\text{boundary_penalty} \times \Delta \text{perimeter}) - \text{unit_cost}\ \text{= 10}\ \times \ (-2) - 5 = -2 $$
+
+Decision:
+
+- $\Delta \text{Objective} \le 0$ → objective decreased → **allow
+ removal** (if other constraints pass).
+
+------------------------------------------------------------------------
+
+### Example B — removing an edge PU increases total perimeter (blocked), but patch stays connected
+
+Here we use a 3×2 rectangle. Let `U` be the **middle PU on the bottom
+row**.\
+`U` is an **edge unit** because it touches `.` below (outside the
+rectangle).
+
+**Before removing U**
+
+``` text
+| S | S | S |
+| S | U | S |
+```
+
+Compute the **total perimeter**:
+
+A 3×2 rectangle has perimeter:
+
+- $P{\text{before}}$= 2(3+2)=10 km
+
+**After removing U**
+
+``` text
+| S | S | S |
+| S | . | S |
+```
+
+Important: the patch is **still connected** (you can still walk between
+all remaining `S` cells via shared borders on the top row).
+
+Now compute the **total perimeter after**.
+
+We can use the neighbour rule:
+
+- `U` had $k=3$ selected neighbours (left, right, and above are
+ selected; below is not).
+
+- Therefore:
+
+$$
+\Delta \text{perimeter} = -4 + 2k = -4 + 2(3) = +2
+$$
+
+So:
+
+- $P_{\text{after}} = 10 + 2 = 12$ km\
+- $\Delta \text{perimeter} = +2$ km
+
+Now apply the boundary check:
+
+Assume:
+
+- `boundary_penalty = 10`
+
+- `unit_cost = 5`
+
+$$\Delta \text{Objective} = 10 \times (+2) - 5 = +15
+$$
+
+Decision:
+
+- $\Delta \text{Objective} > 0$ → objective increased → **block
+ removal**
+
+**Interpretation:**\
+Removing `U` saves cost, but it *exposes new edge* on its three
+neighbours, increasing total perimeter enough that the
+boundary-penalised objective gets worse.
+
+## Practical guidance for choosing `boundary_penalty`
+
+There isn’t one universal “correct” value because it depends on the
+scale and units of your costs and boundary lengths. A practical
+workflow:
+
+1. **Start with `boundary_penalty = 0`**\
+ This turns off boundary blocking. Whittling then only uses targets +
+ patch-size + split viability rules.
+
+2. **Increase gradually** and observe how solutions change\
+ As you increase it, removals that create extra edge become more
+ likely to be blocked, producing smoother patches.
+
+3. **Do a small sensitivity check**\
+ Try a few values spanning a small range (e.g., 0, low, medium, high)
+ and compare: \`\`\`
+
+- number of patches
+
+- mean/median patch size
+
+- total perimeter
+
+- total planning-unit cost retained/removed \`\`\`
+
+If `boundary_penalty` is too high, Stage 3 can become conservative: it
+may keep “bridges” or edge units because removing them would raise the
+boundary term too much.
+
+## FAQ
+
+**Does Stage 3 re-run the optimiser (prioritizr)?**\
+No. Stage 3 does local, one-unit-at-a-time removal tests. It never
+resolves a new optimisation problem.
+
+**Are we comparing costs to the Stage 2 solution?**\
+Not directly. Stage 3 compares the objective **before vs after removing
+one candidate unit**. It uses $Δ objective$ (local change), not a full
+re-optimisation.
+
+**Is it “per patch” or “whole solution”?**\
+Conceptually the objective is for the whole solution (sum of PU costs +
+boundary penalty term). In code, the change is computed locally using
+the candidate’s neighbour relationships, because only local boundary
+edges change when you remove one unit.
+
+**What if my costs are not area?**\
+That’s fine—`boundary_penalty` still converts perimeter into the same
+units as your chosen cost layer. The interpretation becomes: “how much
+cost am I willing to pay to avoid 1 km of extra edge?”
diff --git a/vignettes/minpatch-oceandatr-Seychelles.Rmd b/vignettes/minpatch-oceandatr-Seychelles.Rmd
new file mode 100644
index 0000000..ec87724
--- /dev/null
+++ b/vignettes/minpatch-oceandatr-Seychelles.Rmd
@@ -0,0 +1,747 @@
+---
+title: "Reducing protected area fragmentation in the Seychelles: A Case Study"
+output: rmarkdown::html_vignette
+vignette: >
+ %\VignetteEncoding{UTF-8}
+ %\VignetteIndexEntry{Reducing protected area fragmentation in the Seychelles: A Case Study}
+ %\VignetteEngine{knitr::rmarkdown}
+editor_options:
+ markdown:
+ wrap: 72
+---
+
+```{r setup, include=FALSE}
+knitr::opts_chunk$set(
+ fig.align = "center",
+ fig.height = 5,
+ fig.width = 7,
+ message = TRUE,
+ warning = TRUE,
+ collapse = TRUE,
+ comment = "#>"
+)
+options(scipen = 999)
+```
+
+# 1. Introduction
+
+In many marine spatial planning problems, the goal is not only to meet
+representation targets at the lowest possible cost, but also to consider
+*how selected areas are arranged in space*. Highly fragmented marine
+protected areas can be difficult to manage and enforce, and small
+isolated patches may not sustain viable populations. Scattered solutions
+can also be difficult to justify to decision-makers and stakeholders.
+
+Two R packages are especially helpful for tackling these issues in a
+marine context.
+
+- The `oceandatr` package offers a practical way to build realistic,
+ analysis-ready marine planning datasets. It helps users obtain
+ planning boundaries (e.g., exclusive economic zones or high-seas
+ regions), generate planning-unit grids, and access global ocean
+ datasets—including bathymetry, geomorphology, seamounts, and
+ environmental conditions—aligned to those grids. Together, these
+ tools make it easier to assemble consistent inputs for marine
+ spatial planning analyses.
+
+- The `minpatch` package is a post-processing tool that modifies
+ conservation planning solutions produced by `prioritizr` to reduce
+ fragmentation. It enforces a user-defined minimum patch size by
+ removing patches that are too small and adding new areas to maintain
+ conservation targets. It then simplifies the solution by removing
+ unnecessary planning units, while tracking how these changes affect
+ patch structure and total cost.
+
+In this vignette, we demonstrate the use of `oceandatr` and `minpatch`
+using a simple marine example from the Seychelles Exclusive Economic
+Zone. We first use `oceandatr` to construct a gridded planning problem
+with a set of marine features and then solve a standard `prioritizr`
+minimum-set problem. We then apply `minpatch` to explore how different
+minimum patch sizes and boundary penalties influence the resulting
+solutions.
+
+# 2. Study region and planning units with oceandatr
+
+## 2.1 Load packages
+
+```{r load-packages, echo=TRUE, message=FALSE, warning=FALSE}
+library(minpatch)
+library(oceandatr)
+library(terra)
+library(sf)
+library(dplyr)
+library(ggplot2)
+library(purrr)
+library(stringr)
+library(tibble)
+library(patchwork)
+library(prioritizr)
+library(kableExtra)
+library(tmap)
+set.seed(123)
+
+build_dir <- file.path(getwd(), "_build")
+dir.create(build_dir, showWarnings = FALSE, recursive = TRUE)
+```
+
+## 2.2 Seychelles EEZ as the planning region
+
+We start by using `get_boundary()` from `oceandatr` to obtain the
+Seychelles Exclusive Economic zone (EEZ). This defines our marine
+planning domain.
+
+```{r planning-domain, echo=TRUE, fig.height=5, fig.width=8, message=FALSE, warning=FALSE, out.width="100%"}
+Seychelles_eez <- get_boundary(name = "Seychelles")
+
+# plot to check we have Seychelles' EEZ
+plot(Seychelles_eez[1],
+ col = "lightgreen",
+ main = "Seychelles EEZ",
+ axes = TRUE)
+```
+
+## 2.3 Choose an equal-area projection
+
+We reproject the planning region into an equal-area projection to ensure
+that area- and distance-based calculations are meaningful.
+
+```{r projection, echo=TRUE}
+# use sf::st_bbox to get the bounding box coordinates (in lon/lat)
+#sf::st_bbox(Seychelles_eez)
+
+# use projectionwizard.org to choose an equal-area projection, then store as a PROJ string
+# https://projectionwizard.org
+
+projection_Seychelles <- "+proj=laea +lon_0=55 +lat_0=-4.5 +datum=WGS84 +units=m +no_defs"
+
+# check CRS if needed
+# sf::st_crs(projection_Seychelles)
+```
+
+## 2.4 Create a planning-unit grid
+
+We now create a grid over the EEZ. To keep runtime moderate, we use a
+fairly coarse resolution. Our units will be in meters.
+
+```{r grid, echo=TRUE, fig.height=5, fig.width=8, message=FALSE, warning=FALSE, out.width="100%"}
+# check which units to use
+# sf::st_crs(projection_Seychelles,
+# parameters = TRUE)$units_gdal
+
+# grid the planning area
+Seychelles_grid <- get_grid(
+ boundary = Seychelles_eez,
+ resolution = 30000, # 30,000 just to test the code but a finer resolution can be opted with a more powerful PC
+ crs = projection_Seychelles)
+
+# project the eez into same projection as grid for plotting
+Seychelles_eez_proj <- Seychelles_eez %>%
+ sf::st_transform(crs = projection_Seychelles) %>%
+ sf::st_geometry()
+
+# plot the grid
+terra::plot(Seychelles_grid,
+ col = "gold3",
+ axes = FALSE,
+ legend = FALSE,
+ main = "Seychelles spatial grid (30 km)")
+plot(Seychelles_eez_proj,
+ add = TRUE,
+ border = "black",
+ lwd = 1)
+```
+
+Each *cell* of `Seychelles_grid` is a planning unit. To build a
+`prioritizr` problem later, it's useful to have a vector version of the
+grid:
+
+```{r pu-polygon, echo=TRUE}
+# convert grid to sf polygons as minpatch only works with sf
+Seychelles_pu <- Seychelles_grid %>%
+ stars::st_as_stars() %>%
+ sf::st_as_sf() %>%
+ dplyr::mutate(
+ id = dplyr::row_number(),
+ cost = as.numeric(sf::st_area(.)) / 1e6 # cost = area in km²
+ ) %>%
+ dplyr::select(-layer)
+```
+
+# 3. Building marine features with oceandatr
+
+We will use a set of features available on `oceandatr`:
+
+- bathymetric depth zones
+- geomorphology - seafloor features (banks, ridges, etc.)
+- knolls
+- seamounts
+- coral habitat
+- environmental zones - clusters of environmental conditions
+
+## 3.1 Prepare feature stack
+
+These layers can easily be obtained using the `get_features` function.
+
+```{r get-features, message=FALSE, warning=FALSE}
+# set seed for reproducibility in the get_enviro_zones() sampling to find optimal cluster number
+set.seed(500)
+
+feature_set <- get_features(spatial_grid = Seychelles_grid) %>%
+ remove_empty_layers()
+
+saveRDS(feature_set, file = file.path(build_dir, "feature_set.rds"))
+
+# tidy up feature data names for nicer mapping
+names(feature_set) <- gsub("_", " ", names(feature_set)) %>% stringr::str_to_sentence()
+```
+
+## 3.2 Plot features
+
+Features are visualised with **tmap**. Planning units are coloured
+*blue* where a feature is present and *grey* where it is absent.
+
+```{r visualise-features, fig.height=15, fig.width=15, message=FALSE, warning=FALSE, out.width="100%"}
+names(feature_set) <- names(feature_set) %>%
+ gsub("_", " ", .) %>%
+ stringr::str_to_sentence()
+
+feature_bin <- feature_set > 0
+
+m <- tm_shape(feature_bin) +
+ tm_raster(
+ col.scale = tm_scale_categorical(
+ values = c("grey70", "royalblue"),
+ labels = c("Absent", "Present")
+ ),
+ col.legend = tm_legend_hide(), # hide legend for this layer
+ col.free = FALSE
+ ) +
+ tm_facets(ncol = 4) +
+ tm_shape(Seychelles_eez) +
+ tm_borders() +
+ tm_layout(
+ panel.label.size = 1.5,
+ legend.show = FALSE )
+
+m +
+ tm_add_legend(
+ type = "fill",
+ labels = c("Absent", "Present"),
+ col = c("grey70", "royalblue"),
+ title = ""
+ ) +
+ tm_layout(
+ legend.outside = TRUE,
+ legend.position = c("center", "bottom"),
+ legend.text.size = 1
+ )
+```
+
+## 3.3 Converting features to sf object and combining it with the PU grid
+
+As MinPatch only works well with sf objects, we convert this feature
+stack into sf. Afterwards, we put features and the grid as one data
+table.
+
+```{r features-to-sf}
+# features to sf
+features <- feature_set %>%
+ stars::st_as_stars() %>% # convert data to sf
+ sf::st_as_sf() %>%
+ dplyr::mutate(id = dplyr::row_number()) %>%
+ sf::st_drop_geometry()
+
+# combining
+Seychelles_sf <- Seychelles_pu %>%
+ dplyr::left_join(
+ as.data.frame(features),
+ by = "id"
+ )
+
+# store feature column names
+feature_set <- names(features) %>%
+ dplyr::setdiff("id")
+```
+
+# 4. Build and solve a `prioritizr` problem
+
+We now build a simple minimum-set problem using `prioritizr`, with
+equal-weight features and a uniform target of 30% for each feature.
+
+```{r build-problem}
+# build the problem using sf planning units + feature columns
+p_base <- prioritizr::problem(
+ x = Seychelles_sf,
+ features = feature_set,
+ cost_column = "cost") %>%
+ add_min_set_objective() %>%
+ add_relative_targets(0.30) %>% # 30% of each feature
+ add_binary_decisions() %>%
+ add_rsymphony_solver(verbose = FALSE) # change this to cbc later
+p_base
+```
+
+We then solve this baseline problem without any fragmentation controls
+and use it as a reference for the MinPatch experiments.
+
+```{r solve-baseline, echo=TRUE, fig.height=5, fig.width=8, message=FALSE, warning=FALSE, out.width="100%"}
+# solve the baseline problem
+t_base <- system.time({
+ s_base <- solve(p_base)
+})
+
+# Plot the baseline solution
+p_base_plot <- plot_prioritizr(s_base) +
+ ggtitle("Baseline (no MinPatch)")
+
+p_base_plot
+```
+
+In this baseline solution, we see that the selected planning units are
+fragmented and scattered across the planning region.
+
+# 5. Set up MinPatch parameters
+
+We define minimum patch sizes relative to the planning units by scaling
+the **median PU area** by `multipliers` (5×, 10×, 20×). This makes the
+thresholds easy to interpret as “roughly how many planning units per
+patch”. We also set a single `patch_radius` for all runs, chosen to
+represent a neighbourhood of about 10 planning units. This radius
+defines the spatial search distance used by MinPatch to identify
+neighbouring planning units when forming, expanding, or merging patches.
+
+```{r minpatch-params, echo=TRUE}
+# median planning-unit area (m² and km²)
+median_pu_area_m2 <- median(st_area(Seychelles_sf))
+median_pu_area_km2 <- median_pu_area_m2 / 1e6
+
+# multipliers relative to the median PU area
+multipliers <- c(5, 10, 20)
+
+# minimum patch sizes in m² and km²
+patch_sizes_m2 <- multipliers * median_pu_area_m2
+patch_sizes_km2 <- patch_sizes_m2 / 1e6
+
+# setting patch radius as the length of 10 PUs
+median_pu_length_m <- sqrt(median_pu_area_m2)
+
+# set radius to 10 PU lengths
+patch_radius <- 10 * median_pu_length_m
+
+# summary table
+minpatch_param_summary <- tibble::tibble(
+ multiplier = multipliers,
+ min_patch_m2 = patch_sizes_m2,
+ min_patch_km2 = patch_sizes_km2,
+ median_pu_m2 = median_pu_area_m2,
+ median_pu_area_km2 = median_pu_area_km2)
+
+# summaries of the different values
+cat("\nMinPatch parameters (relative to planning units):\n")
+
+cat("- Median planning unit area:",
+ round(median_pu_area_km2, 3), "km^2\n\n")
+
+for (i in seq_along(multipliers)) {
+ cat("Multiplier:", multipliers[i], "x median PU area\n")
+ cat(" - Minimum patch size:",
+ round(patch_sizes_km2[i], 2), "km^2\n")
+ cat(" - Corresponds to ≈",
+ round(patch_sizes_km2[i] / median_pu_area_km2, 2),
+ "planning units\n\n")
+}
+
+median_pu_length <- sqrt(median_pu_area_m2) # ~ PU width (m)
+radius_in_pus <- patch_radius / median_pu_length # PU-widths
+
+cat("Patch radius used for all runs:\n")
+cat(" -", round(patch_radius, 0), "m (≈", round(patch_radius/1000, 2), "km)\n")
+cat(" - ≈", round(radius_in_pus, 1), "planning units outward (radius)\n")
+```
+
+# 6. Run MinPatch for different minimum patch sizes
+
+We now run MinPatch once for each minimum patch factor using the
+different multipliers. Each run starts from the same `prioritizr`
+solution but applies different constraints on minimum patch size.
+
+```{r min-patch-sizes, echo=TRUE, message=TRUE, warning=TRUE}
+minpatch_results <- vector("list", length(patch_sizes_m2))
+minpatch_times <- numeric(length(patch_sizes_m2))
+
+for (i in seq_along(patch_sizes_m2)) {
+
+ cat("\n============================================\n")
+ cat("Running MinPatch with min patch area ~",
+ round(patch_sizes_km2[i], 2), "km^2 (",
+ multipliers[i], "x median PU)\n")
+ cat("============================================\n")
+
+ # time the MinPatch run
+ t_mp <- system.time({
+ minpatch_results[[i]] <- run_minpatch(
+ prioritizr_problem = p_base,
+ prioritizr_solution = s_base,
+ min_patch_size = patch_sizes_m2[i],
+ patch_radius = patch_radius,
+ boundary_penalty = 0,
+ remove_small_patches = TRUE,
+ add_patches = TRUE,
+ whittle_patches = TRUE,
+ verbose = TRUE
+ )
+
+ })
+
+ # store elapsed time (seconds)
+ minpatch_times[i] <- t_mp[["elapsed"]]
+}
+
+# name the outputs
+names(minpatch_results) <- paste0("minpatch_", multipliers, "x")
+names(minpatch_times) <- paste0("minpatch_", multipliers, "x")
+
+saveRDS(minpatch_results, file = file.path(build_dir, "minpatch_results.rds"))
+saveRDS(minpatch_times, file = file.path(build_dir, "minpatch_times.rds"))
+```
+
+# 7. Interpreting MinPatch outcomes
+
+In this section, we interpret how MinPatch modifies the baseline
+solution. We compare resulting spatial patterns, patch structure, and
+trade-offs between fragmentation and cost.
+
+## 7.1 Selected planning units (baseline vs MinPatch runs)
+
+We start by showing the selected planning units for the baseline and
+each MinPatch run.
+
+```{r minpatch-2x2, echo=TRUE, fig.height=8, fig.width=10, out.width="100%"}
+patchwork::wrap_plots(
+ plot_prioritizr(s_base, col = "solution_1", title = "Baseline (prioritizr)"),
+ plot_prioritizr(minpatch_results[[1]]$solution, col = "minpatch",
+ title = paste0("MinPatch: ", multipliers[1], "× median PU area")),
+ plot_prioritizr(minpatch_results[[2]]$solution, col = "minpatch",
+ title = paste0("MinPatch: ", multipliers[2], "× median PU area")),
+ plot_prioritizr(minpatch_results[[3]]$solution, col = "minpatch",
+ title = paste0("MinPatch: ", multipliers[3], "× median PU area")),
+ guides = "collect",
+ ncol = 2
+) &
+ theme(legend.position = "bottom")
+```
+
+On the plot, we can see that the baseline solution is highly fragmented,
+with many small and isolated selected planning units. As the minimum
+patch size increases, these small patches are removed or merged, leading
+to fewer, larger, and more spatially coherent patches.
+
+## 7.2 Group summary table (all runs)
+
+Having examined the spatial solutions, we summarise all runs in a single
+table to compare outcomes across different metrics.
+
+```{r minpatch-summary, echo=FALSE, message=FALSE, warning=FALSE, out.width="100%"}
+out <- make_minpatch_summary_table(
+ region_sf = Seychelles_sf,
+ baseline_solution_sf = s_base,
+ minpatch_results = minpatch_results,
+ mode = "multiplier",
+ multipliers = multipliers,
+ baseline_compare_obj = minpatch_results[[1]],
+ compare_solutions_fun = compare_solutions,
+ baseline_elapsed = as.numeric(t_base[["elapsed"]]),
+ minpatch_elapsed = as.numeric(minpatch_times),
+ projected_epsg = 32740,
+ minpatch_solution_col = "minpatch",
+ cost_col = "cost",
+ make_kable = TRUE
+)
+out$kable
+```
+
+Once MinPatch is applied, the solution becomes less fragmented: as the
+multiplier increases, the number of patches decreases and the remaining
+patches become larger (higher median patch size).
+
+## 7.3 Change maps (Added / Removed / Retained / No change)
+
+After comparing runs, we use `plot_minpatch` to map which planning units
+were added, removed, or kept versus the baseline. The maps show how
+MinPatch reshapes the solution to meet minimum patch sizes and cut
+fragmentation.
+
+```{r minpatch-plots, fig.width=8, fig.height=5, out.width="100%"}
+# Create plots for each minpatch run and arrange them with patchwork
+plot_list <- purrr::map2(
+ minpatch_results,
+ multipliers,
+ ~ plot_minpatch(
+ .x,
+ title = paste0("Patch size x", .y)
+ )
+)
+patchwork::wrap_plots(
+ plotlist = plot_list,
+ guides = "collect",
+ ncol = 3
+) &
+ theme(legend.position = "bottom")
+```
+
+## 7.4 Patch-labelled maps
+
+We use a function that labels patches in the MinPatch solutions and
+flags each one as valid (meets the minimum patch-size rule) or invalid
+(below the threshold; if they exist). This makes the patch definition
+explicit and helps interpret the patch counts in the summary table.
+
+Patch counts depend on how “connected” is defined. MinPatch uses
+**rook** **adjacency**, where planning units must share an edge to be in
+the same patch. By contrast, queen adjacency also treats corner-touching
+(diagonal) units as connected. For ecology and management,
+rook-connected patches are often more defensible because they represent
+truly contiguous area, avoid corner-only links, and tend to form more
+compact patch units.
+
+```{r patch-labelled-maps, fig.height=5, fig.width=8, out.width="100%", message=FALSE, warning=FALSE}
+for (m in c(5, 10, 20)) {
+ print(minpatch::plot_patch_validity(
+ multiplier = m,
+ multipliers = multipliers,
+ minpatch_results = minpatch_results,
+ pu_sf = Seychelles_sf,
+ do_snap = FALSE
+ ))
+}
+```
+
+## 7.5 Individual summaries (per run)
+
+Here, we show all the individual summaries for all the runs. These
+provide a detailed breakdown and comparison of the metrics of each
+minimum patch run with the baseline solution, as well as other
+information such as feature level area comparison and feature change
+summaries.
+
+```{r minpatch-all-runs, echo=TRUE}
+for (i in seq_along(minpatch_results)) {
+
+ result <- minpatch_results[[i]]
+ factor_val <- multipliers[i]
+
+ cat("\n\n## Scenario: MinPatch with min patch size = ",
+ factor_val, " × median PU area\n\n", sep = "")
+
+ # MinPatch summary (if it prints text)
+ cat("**MinPatch processing summary**\n\n")
+ print_minpatch_summary(result)
+
+ comparison <- compare_solutions(result)
+
+ cat("\n**Overall solution comparison**\n\n")
+ print(comparison$overall)
+
+ cat("\n**Feature-level area comparison**\n\n")
+ print(comparison$features)
+
+ cat("\n**Feature change summary**\n\n")
+ print(comparison$summary)
+}
+```
+
+# 8. Run different boundary penalties
+
+The boundary penalty controls how strongly MinPatch favours spatially
+compact patches during the whittling stage. At this step, MinPatch
+attempts to remove planning units from the edges of patches while
+maintaining conservation targets. Whether a unit can be removed depends
+on the trade-off between its planning-unit cost and the change in
+boundary cost that would result from its removal.
+
+The boundary penalty scales this trade-off by assigning a cost to
+fragmentation: higher values discourage the creation of additional patch
+edges and therefore promote more compact solutions. In the Seychelles
+example, we explore a small range of boundary penalties to illustrate
+how sensitive the whittling process is to boundary costs under a marine
+grid with relatively uniform planning-unit areas.
+
+```{r minpatch-boundarypenalties}
+# calculate reasonable parameters based on planning unit characteristics
+median_pu_area_m2 <- median(st_area(Seychelles_sf))
+
+# use minpatch size 5x median PU
+# retain the same patch radius earlier
+min_patch_size <- median_pu_area_m2 * 5
+patch_radius
+
+bp3 = 0
+t3 <- system.time({
+ result3 <- run_minpatch(
+ prioritizr_problem = p_base,
+ prioritizr_solution = s_base,
+ min_patch_size = min_patch_size,
+ patch_radius = patch_radius,
+ boundary_penalty = bp3,
+ remove_small_patches = TRUE,
+ add_patches = TRUE,
+ whittle_patches = TRUE,
+ verbose = TRUE
+ )
+})
+cat("result3 runtime (sec):", t3[["elapsed"]], "\n")
+
+bp4 = 0.01
+t4 <- system.time({
+ result4 <- run_minpatch(
+ prioritizr_problem = p_base,
+ prioritizr_solution = s_base,
+ min_patch_size = min_patch_size,
+ patch_radius = patch_radius,
+ boundary_penalty = bp4,
+ remove_small_patches = TRUE,
+ add_patches = TRUE,
+ whittle_patches = TRUE,
+ verbose = TRUE
+ )
+})
+cat("result4 runtime (sec):", t4[["elapsed"]], "\n")
+
+bp5 = 1
+t5 <- system.time({
+ result5 <- run_minpatch(
+ prioritizr_problem = p_base,
+ prioritizr_solution = s_base,
+ min_patch_size = min_patch_size,
+ patch_radius = patch_radius,
+ boundary_penalty = bp5,
+ remove_small_patches = TRUE,
+ add_patches = TRUE,
+ whittle_patches = TRUE,
+ verbose = TRUE
+ )
+})
+cat("result5 runtime (sec):", t5[["elapsed"]], "\n")
+```
+
+# 9. Interpreting MinPatch with boundary penalties outcomes
+
+## 9.1 Selected planning units (baseline vs MinPatch runs)
+
+We compare the spatial solutions across different boundary penalties
+with the baseline solution. All runs used a minimum patch size of **5×
+the median PU area**.
+
+```{r minpatch-bp, echo=TRUE, fig.height=8, fig.width=10, out.width="100%"}
+patchwork::wrap_plots(
+ plot_prioritizr(s_base, col = "solution_1", title = "Baseline (prioritizr)"),
+ plot_prioritizr(result3$solution, col = "minpatch",
+ title = paste0("Boundary penalty: ", bp3)),
+ plot_prioritizr(result4$solution, col = "minpatch",
+ title = paste0("Boundary penalty: ", bp4)),
+ plot_prioritizr(result5$solution, col = "minpatch",
+ title = paste0("Boundary penalty: ", bp5)),
+ guides = "collect",
+ ncol = 2
+) &
+ theme(legend.position = "bottom")
+```
+
+## 9.2 Groups summary table (all runs)
+
+We summarise and compare all runs on the table below.
+
+```{r minpatch-grouptable, echo=TRUE, message=FALSE, warning=FALSE, out.width="100%"}
+boundary_penalties <- c(bp3, bp4, bp5)
+minpatch_results_bp <- list(result3, result4, result5)
+
+out_bp <- make_minpatch_summary_table(
+ region_sf = Seychelles_sf,
+ baseline_solution_sf = s_base,
+ minpatch_results = minpatch_results_bp,
+ mode = "boundary_penalty",
+ boundary_penalties = boundary_penalties,
+ fixed_multiplier = 5, # <- because you used 5× median PU area
+ baseline_compare_obj = minpatch_results_bp[[1]],
+ compare_solutions_fun = compare_solutions, # pass explicitly to avoid namespace issues
+ baseline_elapsed = as.numeric(t_base[["elapsed"]]),
+ minpatch_elapsed = c(t3[["elapsed"]], t4[["elapsed"]], t5[["elapsed"]]),
+ projected_epsg = 32740,
+ minpatch_solution_col = "minpatch",
+ cost_col = "cost",
+ make_kable = TRUE
+)
+out_bp$kable
+```
+
+As the boundary penalty increases, the selected area grows a lot because
+more planning units are included, but the layout becomes cleaner: the
+total perimeter drops and the solution breaks into fewer, larger
+patches. The trade-off is that the solution becomes more expensive
+overall, and the runtime increases slightly.
+
+## 9.3 Change maps (Added / Removed / Retained / No change)
+
+Here, we visualise the MinPatch solutions for different boundary penalty
+values.
+
+```{r boundary-change-maps, fig.height=5, fig.width=8, out.width="100%"}
+# Plot change maps
+patchwork::wrap_plots(
+ plot_minpatch(result3, title = paste0("Boundary Penalty: ", bp3)),
+ plot_minpatch(result4, title = paste0("Boundary Penalty: ", bp4)),
+ plot_minpatch(result5, title = paste0("Boundary Penalty: ", bp5)),
+ guides = "collect",
+ ncol = 3
+) &
+ ggplot2::theme(legend.position = "bottom")
+```
+
+## 9.4 Individual summaries (per run)
+
+We show the individual summaries for every run in detail.
+
+```{r boundary-summaries, echo=TRUE, message=FALSE, warning=FALSE}
+# Run summaries
+boundary_penalties <- c(bp3, bp4, bp5)
+minpatch_results_bp <- list(result3, result4, result5)
+minpatch_runtimes <- c(t3[["elapsed"]], t4[["elapsed"]], t5[["elapsed"]])
+
+for (i in seq_along(minpatch_results_bp)) {
+
+ result <- minpatch_results_bp[[i]]
+ bp <- boundary_penalties[i]
+ runtime_sec <- minpatch_runtimes[i]
+
+ cat("\n\n## Scenario: MinPatch with boundary penalty = ", format(bp, scientific = TRUE), "\n\n", sep = "")
+
+ if (!is.na(runtime_sec)) {
+ cat("**Runtime:** ", sprintf("%.2f", runtime_sec), " seconds\n\n", sep = "")
+ }
+
+ cat("**MinPatch processing summary**\n\n")
+ print_minpatch_summary(result)
+
+ comparison <- compare_solutions(result)
+
+ cat("\n**Overall solution comparison**\n\n")
+ print(comparison$overall)
+
+ cat("\n**Feature-level area comparison**\n\n")
+ print(comparison$features)
+
+ cat("\n**Feature change summary**\n\n")
+ print(comparison$summary)
+}
+```
+
+# 10. Takeaway
+
+Overall, this vignette showed a simple workflow for using MinPatch on a
+marine region. In the Seychelles example, MinPatch quickly collapses a
+highly fragmented baseline into fewer, larger rook-connected patches,
+and increasing the minimum patch multiplier strengthens that
+consolidation. Adding a boundary penalty during whittling pushes
+solutions toward cleaner edges and fewer patch boundaries, but it can
+also drive up selected area and total cost, especially when the penalty
+is large relative to planning-unit cost.
diff --git a/vignettes/minpatchTasmania.Rmd b/vignettes/minpatchTasmania.Rmd
index b539f69..8debe95 100644
--- a/vignettes/minpatchTasmania.Rmd
+++ b/vignettes/minpatchTasmania.Rmd
@@ -5,9 +5,8 @@ vignette: >
%\VignetteIndexEntry{MinPatch in Tasmania}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
-
---
-
+
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
@@ -16,6 +15,7 @@ comment = "#>"
```
# Load packages
+
```{r setup, warning=FALSE, message=FALSE}
library(minpatch)
library(prioritizr)
@@ -66,11 +66,11 @@ s <- solve(p)
```
## Plot prioritizr solution
+
```{r}
plot_prioritizr(s)
```
-
# MinPatch
## Choose a patch size
@@ -93,7 +93,6 @@ cat("- This means patches must be at least", round(min_patch_size/median_area, 3
```
-
## Run minpatch
```{r}
@@ -138,15 +137,14 @@ print(comparison$summary)
```
-
# Run different patch sizes
-The minimum patch size parameter is the core constraint that drives MinPatch behaviour - it determines the threshold below which patches are considered too small and must be either enlarged or removed.
+The minimum patch size parameter is the core constraint that drives MinPatch behaviour - it determines the threshold below which patches are considered too small and must be either enlarged or removed.
+
+- During Stage 1, MinPatch removes all patches smaller than this threshold (except existing protected areas).
+- During Stage 2, it adds new patches large enough to meet this minimum when targets are unmet.
+- During Stage 3 (whittling), it prevents the removal of planning units that would make any patch fall below this threshold.
- * During Stage 1, MinPatch removes all patches smaller than this threshold (except existing protected areas).
- * During Stage 2, it adds new patches large enough to meet this minimum when targets are unmet.
- * During Stage 3 (whittling), it prevents the removal of planning units that would make any patch fall below this threshold.
-
Larger minimum patch sizes result in fewer, bigger patches with potentially higher total area, as MinPatch must ensure every patch meets the size requirement. Smaller minimum patch sizes allow more flexibility, potentially resulting in more patches that are closer to the original *prioritizr* solution. The choice of minimum patch size should reflect ecological or management considerations - for example, larger patches may be needed to support viable populations or reduce edge effects, while smaller patches may be acceptable in highly connected landscapes or for features that don't require large contiguous areas.
```{r}
@@ -195,7 +193,6 @@ patchwork::wrap_plots(plot_minpatch(result, title = "Patch Size x5"),
theme(legend.position = "bottom")
```
-
# Run different Boundary Penalties
The boundary penalty controls how much MinPatch prioritizes spatial compactness during the "simulated whittling" stage. During whittling, MinPatch considers removing planning units from patch edges, but only if doing so doesn't increase the total *priortizr* cost. The boundary penalty affects this decision by penalizing fragmented solutions - higher penalties favour more compact patches by making it costly to create additional "edge" between selected and unselected areas. When MinPatch evaluates whether to remove a unit, it calculates the change in boundary length (units with selected neighbours increase boundary when removed, while units with unselected neighbours decrease boundary) and multiplies this by the boundary penalty. If the resulting boundary cost change exceeds the unit's cost, the unit cannot be removed. In datasets like Tasmania with long planning unit boundaries relative to unit costs, even small boundary penalties can be highly influential, potentially preventing most unit removals and resulting in similar solutions across different penalty values. Very small penalties (e.g., 1e-10) may be needed to see meaningful differences in such cases.
@@ -211,7 +208,7 @@ result4 <- run_minpatch(
prioritizr_solution = s,
min_patch_size = min_patch_size,
patch_radius = patch_radius,
- boundary_penalty = 1e-5,
+ boundary_penalty = 1,
remove_small_patches = TRUE,
add_patches = TRUE,
whittle_patches = TRUE,
@@ -224,7 +221,7 @@ result5 <- run_minpatch(
prioritizr_solution = s,
min_patch_size = min_patch_size,
patch_radius = patch_radius,
- boundary_penalty = 1e-10,
+ boundary_penalty = 10,
remove_small_patches = TRUE,
add_patches = TRUE,
whittle_patches = TRUE,
@@ -233,12 +230,26 @@ result5 <- run_minpatch(
```
-## Visualise the minpatch solution
+## Visualise the final solution
+
+```{r}
+
+patchwork::wrap_plots(plot_prioritizr(result2$solution, col = "minpatch", title = "Boundary Penalty: 0"),
+ plot_prioritizr(result4$solution, col = "minpatch", title = "Boundary Penalty: 1"),
+ plot_prioritizr(result5$solution, col = "minpatch", title = "Boundary Penalty: 10"),
+ guides = "collect",
+ ncol = 3) &
+ theme(legend.position = "bottom")
+
+
+```
+
+## Visualise the differences in the minpatch solution
```{r}
-patchwork::wrap_plots(plot_minpatch(result, title = "Boundary Penalty: 0"),
- plot_minpatch(result4, title = "Boundary Penalty: 1e-5"),
- plot_minpatch(result5, title = "Boundary Penalty: 1e-10"),
+patchwork::wrap_plots(plot_minpatch(result2, title = "Boundary Penalty: 0"),
+ plot_minpatch(result4, title = "Boundary Penalty: 1"),
+ plot_minpatch(result5, title = "Boundary Penalty: 10"),
guides = "collect",
ncol = 3) &
theme(legend.position = "bottom")