ecological-agent-skills 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/AGENT_CONTEXT.md +191 -0
  2. package/CATALOG.md +329 -0
  3. package/LICENSE +692 -0
  4. package/README.md +347 -0
  5. package/bin/install.mjs +168 -0
  6. package/docs/comparison-with-alternatives.md +38 -0
  7. package/docs/global-examples-index.md +103 -0
  8. package/docs/repository-statistics.md +101 -0
  9. package/docs/theoretical-foundations.md +188 -0
  10. package/environment.yaml +106 -0
  11. package/examples/community/arctic_tundra_vegetation_example.md +247 -0
  12. package/examples/community/bird_landuse_example.md +63 -0
  13. package/examples/community/phytoplankton_reservoir_example.md +60 -0
  14. package/examples/community/reef_fish_indopacific_example.md +221 -0
  15. package/examples/impact/baci_road_example.md +57 -0
  16. package/examples/impact/ecosystem_services_atlantic_forest.md +83 -0
  17. package/examples/impact/forest_loss_borneo_timeseries_example.md +225 -0
  18. package/examples/occupancy/puma_camera_example.md +61 -0
  19. package/examples/occupancy/snow_leopard_himalayas_example.md +204 -0
  20. package/examples/reproducible/whittaker_biome_sdm_example.md +406 -0
  21. package/examples/sdm/anteater_cerrado_example.md +69 -0
  22. package/examples/sdm/jaguar_amazon_example.md +80 -0
  23. package/examples/sdm/koala_climate_change_example.md +170 -0
  24. package/examples/sdm/wolf_recolonization_europe_example.md +193 -0
  25. package/package.json +43 -0
  26. package/renv.lock +194 -0
  27. package/skills/SKILL_INDEX.json +1020 -0
  28. package/skills/acoustic-monitoring/SKILL.md +163 -0
  29. package/skills/acoustic-monitoring/examples/example-prompts.md +100 -0
  30. package/skills/acoustic-monitoring/examples/temperate_forest_birds_example.md +285 -0
  31. package/skills/acoustic-monitoring/resources/acoustic-indices-reference.md +93 -0
  32. package/skills/acoustic-monitoring/resources/soundscape-ecology-guide.md +90 -0
  33. package/skills/acoustic-monitoring/resources/species-id-tools-comparison.md +89 -0
  34. package/skills/acoustic-monitoring/scripts/batch_species_detection.py +360 -0
  35. package/skills/acoustic-monitoring/scripts/compute_acoustic_indices.R +235 -0
  36. package/skills/acoustic-monitoring/scripts/compute_acoustic_indices.py +374 -0
  37. package/skills/biostatistics-workbench/SKILL.md +140 -0
  38. package/skills/biostatistics-workbench/examples/example-prompts.md +39 -0
  39. package/skills/biostatistics-workbench/resources/effect-size-reference.md +81 -0
  40. package/skills/biostatistics-workbench/resources/glm-family-link-reference.md +47 -0
  41. package/skills/biostatistics-workbench/resources/test-selection-guide.md +93 -0
  42. package/skills/biostatistics-workbench/scripts/glm_pipeline.R +78 -0
  43. package/skills/biostatistics-workbench/scripts/glm_pipeline.py +210 -0
  44. package/skills/camera-trap-processing/SKILL.md +159 -0
  45. package/skills/camera-trap-processing/examples/example-prompts.md +103 -0
  46. package/skills/camera-trap-processing/examples/leopard_serengeti_example.md +231 -0
  47. package/skills/camera-trap-processing/resources/activity-patterns-reference.md +113 -0
  48. package/skills/camera-trap-processing/resources/camtrapR-workflow-guide.md +130 -0
  49. package/skills/camera-trap-processing/resources/detection-event-definition-guide.md +89 -0
  50. package/skills/camera-trap-processing/scripts/estimate_activity.R +169 -0
  51. package/skills/camera-trap-processing/scripts/process_camtrap_data.R +179 -0
  52. package/skills/camera-trap-processing/scripts/process_camtrap_data.py +192 -0
  53. package/skills/community-ecology-ordination/SKILL.md +133 -0
  54. package/skills/community-ecology-ordination/examples/example-prompts.md +35 -0
  55. package/skills/community-ecology-ordination/resources/dissimilarity-metric-guide.md +53 -0
  56. package/skills/community-ecology-ordination/resources/nmds-interpretation-guide.md +104 -0
  57. package/skills/community-ecology-ordination/scripts/__pycache__/community_analysis.cpython-311.pyc +0 -0
  58. package/skills/community-ecology-ordination/scripts/community_analysis.R +143 -0
  59. package/skills/community-ecology-ordination/scripts/community_analysis.py +231 -0
  60. package/skills/ecological-data-foundation/SKILL.md +129 -0
  61. package/skills/ecological-data-foundation/examples/example-prompts.md +40 -0
  62. package/skills/ecological-data-foundation/resources/coordinate-cleaning-flags.md +66 -0
  63. package/skills/ecological-data-foundation/resources/darwin-core-glossary.md +91 -0
  64. package/skills/ecological-data-foundation/resources/data-citation-guide.md +265 -0
  65. package/skills/ecological-data-foundation/resources/gbif-data-citation-guide.md +193 -0
  66. package/skills/ecological-data-foundation/resources/qa-checklist.md +83 -0
  67. package/skills/ecological-data-foundation/scripts/__pycache__/clean_occurrences.cpython-311.pyc +0 -0
  68. package/skills/ecological-data-foundation/scripts/__pycache__/download_from_ebird.cpython-311.pyc +0 -0
  69. package/skills/ecological-data-foundation/scripts/__pycache__/download_from_inat.cpython-311.pyc +0 -0
  70. package/skills/ecological-data-foundation/scripts/__pycache__/download_from_iucn.cpython-311.pyc +0 -0
  71. package/skills/ecological-data-foundation/scripts/__pycache__/download_from_obis.cpython-311.pyc +0 -0
  72. package/skills/ecological-data-foundation/scripts/clean_occurrences.R +230 -0
  73. package/skills/ecological-data-foundation/scripts/clean_occurrences.py +268 -0
  74. package/skills/ecological-data-foundation/scripts/download_from_ebird.R +251 -0
  75. package/skills/ecological-data-foundation/scripts/download_from_ebird.py +364 -0
  76. package/skills/ecological-data-foundation/scripts/download_from_gbif.R +315 -0
  77. package/skills/ecological-data-foundation/scripts/download_from_gbif.py +407 -0
  78. package/skills/ecological-data-foundation/scripts/download_from_inat.R +238 -0
  79. package/skills/ecological-data-foundation/scripts/download_from_inat.py +304 -0
  80. package/skills/ecological-data-foundation/scripts/download_from_iucn.R +273 -0
  81. package/skills/ecological-data-foundation/scripts/download_from_iucn.py +344 -0
  82. package/skills/ecological-data-foundation/scripts/download_from_obis.R +248 -0
  83. package/skills/ecological-data-foundation/scripts/download_from_obis.py +318 -0
  84. package/skills/ecological-impact-assessment/SKILL.md +123 -0
  85. package/skills/ecological-impact-assessment/examples/example-prompts.md +32 -0
  86. package/skills/ecological-impact-assessment/resources/baci-design-guide.md +55 -0
  87. package/skills/ecological-impact-assessment/resources/fragmentation-metrics-reference.md +86 -0
  88. package/skills/ecological-impact-assessment/resources/pressure-index-template.md +78 -0
  89. package/skills/ecological-impact-assessment/resources/study-design-guide.md +168 -0
  90. package/skills/ecological-impact-assessment/scripts/baci_analysis.R +161 -0
  91. package/skills/ecological-impact-assessment/scripts/fragmentation_analysis.py +141 -0
  92. package/skills/ecological-impact-assessment/scripts/power_analysis_baci.R +274 -0
  93. package/skills/ecosystem-services-assessment/SKILL.md +125 -0
  94. package/skills/ecosystem-services-assessment/examples/example-prompts.md +24 -0
  95. package/skills/ecosystem-services-assessment/resources/es-indicator-reference.md +45 -0
  96. package/skills/ecosystem-services-assessment/resources/invest-parameter-guide.md +86 -0
  97. package/skills/ecosystem-services-assessment/resources/rusle-coefficients.md +88 -0
  98. package/skills/ecosystem-services-assessment/scripts/__pycache__/compute_es.cpython-311.pyc +0 -0
  99. package/skills/ecosystem-services-assessment/scripts/compute_es.py +189 -0
  100. package/skills/ecosystem-services-assessment/scripts/tradeoff_analysis.R +161 -0
  101. package/skills/environmental-time-series/SKILL.md +125 -0
  102. package/skills/environmental-time-series/examples/example-prompts.md +33 -0
  103. package/skills/environmental-time-series/resources/anomaly-indices-reference.md +88 -0
  104. package/skills/environmental-time-series/resources/bfast-parameter-guide.md +69 -0
  105. package/skills/environmental-time-series/scripts/__pycache__/recovery_trajectory.cpython-311.pyc +0 -0
  106. package/skills/environmental-time-series/scripts/__pycache__/trend_analysis.cpython-311.pyc +0 -0
  107. package/skills/environmental-time-series/scripts/recovery_trajectory.R +305 -0
  108. package/skills/environmental-time-series/scripts/recovery_trajectory.py +178 -0
  109. package/skills/environmental-time-series/scripts/trend_analysis.R +192 -0
  110. package/skills/environmental-time-series/scripts/trend_analysis.py +184 -0
  111. package/skills/geoprocessing-for-ecology/SKILL.md +123 -0
  112. package/skills/geoprocessing-for-ecology/examples/example-prompts.md +32 -0
  113. package/skills/geoprocessing-for-ecology/resources/crs-reference.md +62 -0
  114. package/skills/geoprocessing-for-ecology/resources/global-predictor-sources.md +331 -0
  115. package/skills/geoprocessing-for-ecology/resources/resampling-methods.md +57 -0
  116. package/skills/geoprocessing-for-ecology/scripts/__pycache__/download_predictors.cpython-311.pyc +0 -0
  117. package/skills/geoprocessing-for-ecology/scripts/download_predictors.R +239 -0
  118. package/skills/geoprocessing-for-ecology/scripts/download_predictors.py +379 -0
  119. package/skills/geoprocessing-for-ecology/scripts/stack_and_extract.R +224 -0
  120. package/skills/geoprocessing-for-ecology/scripts/stack_and_extract.py +172 -0
  121. package/skills/landscape-connectivity/SKILL.md +170 -0
  122. package/skills/landscape-connectivity/examples/example-prompts.md +96 -0
  123. package/skills/landscape-connectivity/examples/jaguar_mesoamerica_corridor_example.md +271 -0
  124. package/skills/landscape-connectivity/resources/circuitscape-parameter-guide.md +155 -0
  125. package/skills/landscape-connectivity/resources/graph-theory-for-ecology.md +134 -0
  126. package/skills/landscape-connectivity/resources/resistance-surface-guide.md +141 -0
  127. package/skills/landscape-connectivity/scripts/connectivity_analysis.py +387 -0
  128. package/skills/landscape-connectivity/scripts/connectivity_metrics.R +274 -0
  129. package/skills/landscape-connectivity/scripts/resistance_surface.R +239 -0
  130. package/skills/model-validation-and-uncertainty/SKILL.md +131 -0
  131. package/skills/model-validation-and-uncertainty/examples/example-prompts.md +30 -0
  132. package/skills/model-validation-and-uncertainty/resources/extrapolation-risk-guide.md +236 -0
  133. package/skills/model-validation-and-uncertainty/resources/metric-selection-guide.md +52 -0
  134. package/skills/model-validation-and-uncertainty/resources/threshold-selection-guide.md +64 -0
  135. package/skills/model-validation-and-uncertainty/scripts/__pycache__/validate_model.cpython-311.pyc +0 -0
  136. package/skills/model-validation-and-uncertainty/scripts/extrapolation_risk.R +315 -0
  137. package/skills/model-validation-and-uncertainty/scripts/validate_model.py +226 -0
  138. package/skills/model-validation-and-uncertainty/scripts/validate_sdm.R +162 -0
  139. package/skills/occupancy-and-detection/SKILL.md +126 -0
  140. package/skills/occupancy-and-detection/examples/example-prompts.md +33 -0
  141. package/skills/occupancy-and-detection/resources/detection-history-format.md +100 -0
  142. package/skills/occupancy-and-detection/resources/occupancy-study-design.md +47 -0
  143. package/skills/occupancy-and-detection/scripts/__pycache__/occupancy_analysis.cpython-311.pyc +0 -0
  144. package/skills/occupancy-and-detection/scripts/occupancy_analysis.R +160 -0
  145. package/skills/occupancy-and-detection/scripts/occupancy_analysis.py +159 -0
  146. package/skills/population-viability-analysis/SKILL.md +161 -0
  147. package/skills/population-viability-analysis/examples/african_elephant_pva_example.md +266 -0
  148. package/skills/population-viability-analysis/examples/example-prompts.md +95 -0
  149. package/skills/population-viability-analysis/resources/extinction-risk-thresholds.md +128 -0
  150. package/skills/population-viability-analysis/resources/matrix-model-guide.md +139 -0
  151. package/skills/population-viability-analysis/resources/sensitivity-elasticity-reference.md +182 -0
  152. package/skills/population-viability-analysis/scripts/matrix_pva.R +258 -0
  153. package/skills/population-viability-analysis/scripts/pva_analysis.py +442 -0
  154. package/skills/population-viability-analysis/scripts/stochastic_pva.R +353 -0
  155. package/skills/predictive-modeling-best-practices/SKILL.md +136 -0
  156. package/skills/predictive-modeling-best-practices/examples/example-prompts.md +58 -0
  157. package/skills/predictive-modeling-best-practices/resources/collinearity-decision-tree.md +65 -0
  158. package/skills/predictive-modeling-best-practices/resources/sampling-bias-correction.md +267 -0
  159. package/skills/predictive-modeling-best-practices/resources/spatial-cv-guide.md +73 -0
  160. package/skills/predictive-modeling-best-practices/scripts/__pycache__/spatial_cv.cpython-311.pyc +0 -0
  161. package/skills/predictive-modeling-best-practices/scripts/collinearity_check.R +112 -0
  162. package/skills/predictive-modeling-best-practices/scripts/spatial_cv.py +182 -0
  163. package/skills/reproducible-ecology-pipeline/SKILL.md +139 -0
  164. package/skills/reproducible-ecology-pipeline/examples/example-prompts.md +35 -0
  165. package/skills/reproducible-ecology-pipeline/resources/directory-structure-template.md +94 -0
  166. package/skills/reproducible-ecology-pipeline/resources/params-yaml-template.yaml +84 -0
  167. package/skills/reproducible-ecology-pipeline/resources/reproducibility-checklist-template.md +66 -0
  168. package/skills/reproducible-ecology-pipeline/scripts/generate_file_manifest.py +110 -0
  169. package/skills/reproducible-ecology-pipeline/scripts/init_project.sh +53 -0
  170. package/skills/spatial-prioritization/SKILL.md +162 -0
  171. package/skills/spatial-prioritization/examples/biodiversity_hotspot_prioritization_example.md +289 -0
  172. package/skills/spatial-prioritization/examples/example-prompts.md +93 -0
  173. package/skills/spatial-prioritization/resources/cost-surface-reference.md +130 -0
  174. package/skills/spatial-prioritization/resources/marxan-vs-prioritizr-comparison.md +125 -0
  175. package/skills/spatial-prioritization/resources/prioritizr-formulation-guide.md +188 -0
  176. package/skills/spatial-prioritization/resources/representation-targets-guide.md +186 -0
  177. package/skills/spatial-prioritization/scripts/prioritization_sensitivity.R +320 -0
  178. package/skills/spatial-prioritization/scripts/run_prioritization.R +336 -0
  179. package/skills/species-distribution-modeling/SKILL.md +139 -0
  180. package/skills/species-distribution-modeling/examples/example-prompts.md +36 -0
  181. package/skills/species-distribution-modeling/resources/algorithm-comparison.md +25 -0
  182. package/skills/species-distribution-modeling/resources/calibration-area-guide.md +71 -0
  183. package/skills/species-distribution-modeling/resources/climate-scenario-preparation.md +170 -0
  184. package/skills/species-distribution-modeling/resources/maxent-calibration-guide.md +211 -0
  185. package/skills/species-distribution-modeling/resources/sdm-checklist.md +37 -0
  186. package/skills/species-distribution-modeling/scripts/predict_distribution.R +236 -0
  187. package/skills/species-distribution-modeling/scripts/predict_distribution.py +286 -0
  188. package/skills/species-distribution-modeling/scripts/prepare_future_layers.R +351 -0
  189. package/skills/species-distribution-modeling/scripts/project_scenarios.R +220 -0
  190. package/skills/species-distribution-modeling/scripts/run_ensemble_sdm.R +99 -0
  191. package/skills/species-distribution-modeling/scripts/sdm_pipeline.py +318 -0
  192. package/skills/species-distribution-modeling/scripts/tune_maxnet.R +344 -0
  193. package/templates/SKILL_TEMPLATE.md +225 -0
  194. package/templates/checklists/data-submission-checklist.md +38 -0
  195. package/templates/checklists/post-analysis-checklist.md +55 -0
  196. package/templates/checklists/pre-analysis-checklist.md +31 -0
  197. package/templates/prompts/debug-skill.md +47 -0
  198. package/templates/prompts/invoke-skill.md +34 -0
  199. package/templates/prompts/invoke-workflow.md +45 -0
  200. package/templates/reports/technical-report-template.md +80 -0
  201. package/templates/scripts/logger_setup.R +79 -0
  202. package/templates/scripts/logger_setup.py +119 -0
  203. package/templates/scripts/params_loader.R +28 -0
  204. package/templates/scripts/params_loader.py +38 -0
  205. package/workflows/analyze-community-structure/WORKFLOW.md +72 -0
  206. package/workflows/analyze-environmental-change/WORKFLOW.md +73 -0
  207. package/workflows/assess-ecological-impact/WORKFLOW.md +75 -0
  208. package/workflows/assess-ecosystem-services/WORKFLOW.md +68 -0
  209. package/workflows/assess-landscape-connectivity/WORKFLOW.md +84 -0
  210. package/workflows/build-fire-risk-map/WORKFLOW.md +79 -0
  211. package/workflows/produce-technical-report/WORKFLOW.md +113 -0
  212. package/workflows/run-camera-trap-occupancy/WORKFLOW.md +87 -0
  213. package/workflows/run-conservation-prioritization/WORKFLOW.md +89 -0
  214. package/workflows/run-multispecies-screening/WORKFLOW.md +197 -0
  215. package/workflows/run-occupancy-analysis/WORKFLOW.md +74 -0
  216. package/workflows/run-population-viability/WORKFLOW.md +90 -0
  217. package/workflows/run-sdm-study/WORKFLOW.md +99 -0
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: spatial-prioritization
3
+ description: "Solves systematic conservation planning problems using integer linear programming (prioritizr), Marxan, or Zonation for protected area design. Use this skill when the user mentions conservation planning, 30x30 targets, Marxan, Zonation, prioritizr, irreplaceability, boundary length modifier (BLM), minimum set problems, representation targets, systematic conservation, or protected area network design."
4
+ skill_version: 1.0.0
5
+ ---
6
+
7
+ # Skill: spatial-prioritization
8
+
9
+ **Domain:** Conservation planning · prioritizr · Marxan · Zonation · Reserve design · 30×30
10
+
11
+ ---
12
+
13
+ ## Purpose
14
+
15
+ Guides the agent through systematic conservation planning to identify priority areas that efficiently represent biodiversity targets under cost constraints. Covers problem formulation (minimum-set, maximum-coverage), planning unit design, target definition, cost surface selection, connectivity penalties, and solving with integer linear programming. Produces priority maps, irreplaceability surfaces, and cost-effectiveness curves for decision-makers.
16
+
17
+ ---
18
+
19
+ ## When to Invoke
20
+
21
+ Invoke this skill when:
22
+
23
+ - The user requests conservation area prioritisation, gap analysis, or reserve design
24
+ - Systematic conservation planning (Marxan, Zonation, prioritizr) is needed
25
+ - Protected area network adequacy must be evaluated against biodiversity targets
26
+ - A 30×30 or other area-based conservation target must be spatially allocated
27
+ - Trade-offs between cost and biodiversity representation must be quantified
28
+
29
+ **trigger_keywords:** `conservation planning`, `protected areas`, `Marxan`, `Zonation`, `prioritizr`, `reserve design`, `systematic conservation`, `30x30`, `biodiversity target`, `irreplaceability`, `complementarity`, `gap analysis`, `cost-effectiveness`, `planning unit`, `boundary length modifier`
30
+
31
+ ---
32
+
33
+ ## Inputs
34
+
35
+ | Input | Format | Required |
36
+ |---|---|---|
37
+ | Species suitability or distribution stack | GeoTIFF (multiband) | Required |
38
+ | Cost surface raster | GeoTIFF | Required |
39
+ | Study area polygon | SHP or GPKG | Required |
40
+ | Conservation targets per species (%) | CSV | Recommended |
41
+ | Locked-in areas (existing protected areas) | SHP or GPKG | Optional |
42
+ | Locked-out areas (exclusion zones) | SHP or GPKG | Optional |
43
+ | Connectivity layer (habitat patches or resistance surface) | GeoTIFF or GPKG | Optional |
44
+
45
+ ---
46
+
47
+ ## Outputs
48
+
49
+ | Output | Description |
50
+ |---|---|
51
+ | `priority_solution.tif` | Binary raster: 1 = selected planning unit, 0 = not selected |
52
+ | `irreplaceability.tif` | Selection frequency across portfolio of near-optimal solutions |
53
+ | `targets_achieved.csv` | % of target met per species/feature in the solution |
54
+ | `cost_effectiveness_curve.png` | Total cost vs. % targets achieved across budget levels |
55
+ | `solution_summary.md` | Narrative: total area, cost, targets met, connectivity score |
56
+ | `sensitivity_results.csv` | Solution metrics across BLM and target combinations |
57
+
58
+ ---
59
+
60
+ ## Steps
61
+
62
+ 1. **Define planning units**
63
+ Use the study area raster cells as planning units (pixel-based) or create a hexagonal
64
+ grid at the appropriate resolution. Confirm CRS matches all input layers.
65
+ Record planning unit size and type in `decision_log.md`.
66
+
67
+ 2. **Prepare features (biodiversity layers)**
68
+ Load species suitability stack from `species-distribution-modeling` skill.
69
+ Optionally include ecosystem service layers from `ecosystem-services-assessment`.
70
+ Normalise all feature layers to [0, 1] if combining different units.
71
+
72
+ 3. **Define targets**
73
+ Default: 30% of each species' total distribution within the study area.
74
+ If IUCN threat status data are available, apply higher targets for threatened species
75
+ (CR: 50%, EN: 40%, VU: 30%).
76
+ Save target table as `params/targets.csv`.
77
+
78
+ 4. **Build and solve the prioritisation problem** *(invoke `run_prioritization.R`)*
79
+ Create problem with `prioritizr::problem()`, add targets, cost, and penalties.
80
+ Set locked-in (existing protected areas) and locked-out (excluded) zones.
81
+ Add connectivity penalty using `add_boundary_penalties(penalty = BLM)`.
82
+ Solve with `highs` solver (default) or `symphony` (fallback).
83
+
84
+ 5. **Evaluate solution**
85
+ Check `targets_achieved.csv`: all features must reach ≥ 80% of target.
86
+ If any feature < 80%: budget is insufficient; run `prioritization_sensitivity.R`.
87
+ Calculate irreplaceability as selection frequency across 100 near-optimal solutions.
88
+
89
+ 6. **Generate cost-effectiveness curve** *(invoke `prioritization_sensitivity.R`)*
90
+ Solve at 5 budget levels (50%, 75%, 100%, 125%, 150% of selected solution cost).
91
+ Plot cumulative targets achieved vs. total cost.
92
+ Present to decision-maker as trade-off summary.
93
+
94
+ 7. **Validate and document**
95
+ Verify `priority_solution.tif` covers the expected % of study area.
96
+ Record solver used, BLM value, target definitions, and any infeasible scenarios
97
+ in `decision_log.md`.
98
+
99
+ ---
100
+
101
+ ## Decision Points
102
+
103
+ | Condition | Diagnosis | Recommended Action |
104
+ |---|---|---|
105
+ | No feasible solution found in 1000 iterations | Budget too restrictive or targets unachievable | Relax targets (reduce by 5% increments) or increase budget; document trade-off |
106
+ | < 80% of targets achieved in solution | Budget insufficient for full representation | Present cost-effectiveness curve; let decision-maker choose acceptable representation level |
107
+ | Irreplaceability > 0.8 for a deforested area | Critical irreplaceable area is already lost | Escalate to restoration analysis; include in gap analysis narrative |
108
+ | BLM = 0 gives highly fragmented solution | Connectivity not enforced | Calibrate BLM: increase until solution compactness is acceptable without losing > 10% of targets |
109
+ | Existing protected areas already meet targets | No additional areas needed | Perform gap analysis only; report which species remain under-represented |
110
+
111
+ ---
112
+
113
+ ## Key Decisions to Document
114
+
115
+ Record the following in `decision_log.md` after running this skill:
116
+
117
+ - Planning unit type (pixel vs. hexagon), size, and CRS used
118
+ - Target percentages per feature and their justification (30×30, IUCN status, etc.)
119
+ - Cost surface used (area proxy, economic value, opportunity cost) and its source
120
+ - BLM value applied and how it was calibrated
121
+ - Solver used (highs, symphony, gurobi) and whether any scenarios were infeasible
122
+
123
+ ---
124
+
125
+ ## Tools and Libraries
126
+
127
+ **R**
128
+ ```r
129
+ suppressPackageStartupMessages(library(prioritizr)) # conservation planning problem
130
+ suppressPackageStartupMessages(library(highs)) # HiGHS solver (recommended, free)
131
+ suppressPackageStartupMessages(library(terra)) # raster handling
132
+ suppressPackageStartupMessages(library(sf)) # vector handling
133
+ suppressPackageStartupMessages(library(dplyr)) # data manipulation
134
+ suppressPackageStartupMessages(library(ggplot2)) # cost-effectiveness plots
135
+ ```
136
+
137
+ **Python** *(support layer only — core analysis in R)*
138
+ ```python
139
+ import geopandas as gpd # spatial data inspection
140
+ import rasterio # raster inspection
141
+ import numpy as np # array operations
142
+ from pathlib import Path # file system
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Resources
148
+
149
+ - [`skills/spatial-prioritization/resources/prioritizr-formulation-guide.md`](resources/prioritizr-formulation-guide.md) — Problem types, planning units, targets, costs, BLM, solvers, and locked zones
150
+ - [`skills/spatial-prioritization/resources/marxan-vs-prioritizr-comparison.md`](resources/marxan-vs-prioritizr-comparison.md) — Comparison of Marxan, Zonation, prioritizr, and OPT; migration guide
151
+ - [`skills/spatial-prioritization/resources/cost-surface-reference.md`](resources/cost-surface-reference.md) — Cost proxy options, global data sources, normalisation, and sensitivity to cost
152
+ - [`skills/spatial-prioritization/resources/representation-targets-guide.md`](resources/representation-targets-guide.md) — 10%/17%/30% targets, species-level targets, fine-filter vs. coarse-filter approaches
153
+
154
+ ---
155
+
156
+ ## Notes
157
+
158
+ - **prioritizr requires a solver:** The `highs` package provides a free, high-performance solver sufficient for most problems. `gurobi` is faster for very large problems but requires an academic licence. Do not attempt to solve without an explicit solver; `prioritizr` will error.
159
+ - **Boundary length modifier (BLM) requires calibration:** BLM = 0 produces the cheapest but most fragmented solution. Too high a BLM increases cost dramatically. Calibrate by plotting cost vs. total boundary length across BLM values (built into `prioritization_sensitivity.R`).
160
+ - **Irreplaceability ≠ priority:** A planning unit with irreplaceability = 1.0 is selected in every near-optimal solution but may be inexpensive. A unit with irreplaceability = 0.3 is selected only in some solutions. Report both the solution map and irreplaceability map.
161
+ - **Planning unit size affects resolution and computation time:** 1 km² pixels for a country-scale analysis may produce millions of planning units, making ILP very slow. Aggregate to 10 km² for national-scale problems; use 1 km² only for regional scales.
162
+ - **Locked-in areas must be in the same CRS as planning units:** CRS mismatch between existing protected areas and planning units is a common error that causes protected areas to be ignored in the solution.
@@ -0,0 +1,289 @@
1
+ ---
2
+ skill_id: spatial-prioritization
3
+ example_type: full_walkthrough
4
+ taxon: Multi-species (Atlantic Forest endemic vertebrates)
5
+ region: Southern Bahia, Brazil — Atlantic Forest hotspot
6
+ ---
7
+
8
+ # Atlantic Forest Biodiversity Hotspot Prioritization — Full Walkthrough
9
+
10
+ ## Study Context
11
+
12
+ **Location:** Southern Bahia, Brazil — one of the world's most biodiverse (and threatened) Atlantic Forest remnants
13
+ **Objective:** Design a minimum-cost protected area expansion to meet 30% representation targets for 85 endemic vertebrate species while maximising compactness (coridor potential)
14
+ **Planning units:** 5×5 km grid cells, n = 3,240 valid PUs
15
+ **Features:** 85 species SDM suitability layers (MaxEnt, continuous 0–1)
16
+ **Cost:** Opportunity cost surface based on agricultural land value (R$/ha/yr)
17
+ **Existing PAs:** 12 federal and state reserves (already locked in, covering ~8% of area)
18
+
19
+ ---
20
+
21
+ ## Step 1 — Data Preparation
22
+
23
+ ### Planning unit cost surface
24
+
25
+ ```r
26
+ suppressPackageStartupMessages(library(terra))
27
+ suppressPackageStartupMessages(library(sf))
28
+
29
+ # Agricultural opportunity cost: sugarcane + cattle + timber
30
+ sugarcane <- rast("data/sugarcane_revenue_reais_ha.tif")
31
+ cattle <- rast("data/cattle_TLU_ha.tif") * 180 # R$/TLU/yr
32
+ timber <- rast("data/timber_potential_reais_ha.tif")
33
+ opp_cost <- max(sugarcane, cattle, timber, na.rm = TRUE)
34
+ opp_cost <- aggregate(opp_cost, fact = 5, fun = "mean") # resample to 5km
35
+
36
+ # Minimum floor
37
+ opp_cost[opp_cost < 1] <- 1
38
+ writeRaster(opp_cost, "data/pu_cost_5km.tif", overwrite = TRUE)
39
+ cat(sprintf("Cost range: %.0f – %.0f R$/ha/yr\n",
40
+ global(opp_cost, "min")[[1]], global(opp_cost, "max")[[1]]))
41
+ ```
42
+
43
+ **Cost statistics:**
44
+
45
+ | Statistic | Value (R$/ha/yr) |
46
+ |-----------|-----------------|
47
+ | Min | 1.0 |
48
+ | Median | 845 |
49
+ | Mean | 1,240 |
50
+ | Q95 | 3,890 |
51
+ | Max | 8,750 |
52
+
53
+ **Decision:** Log-transform cost for analysis to reduce influence of 5 outlier PUs with costs > 7,000 R$/ha/yr. Decision logged with rationale.
54
+
55
+ ### Feature layers preparation
56
+
57
+ ```r
58
+ feat_files <- list.files("data/sdm_continuous/", pattern = ".tif$", full.names = TRUE)
59
+ features <- rast(feat_files)
60
+ features <- resample(features, rast("data/pu_cost_5km.tif"), method = "bilinear")
61
+
62
+ # Check feature distribution: remove features with < 5 PUs with suitability > 0.3
63
+ feat_sums <- global(features > 0.3, "sum", na.rm = TRUE)
64
+ low_feat <- names(features)[feat_sums[[1]] < 5]
65
+ cat(sprintf("Removing %d features with very restricted range.\n", length(low_feat)))
66
+ features <- features[[!names(features) %in% low_feat]]
67
+ ```
68
+
69
+ **Result:** 3 species removed (range < 5 PUs); 82 features retained.
70
+
71
+ ---
72
+
73
+ ## Step 2 — Baseline Prioritization (Minimum Set, 30% Targets)
74
+
75
+ ### Target computation (IUCN-based)
76
+
77
+ ```r
78
+ species_meta <- read.csv("data/species_iucn_status.csv")
79
+ # Columns: species, iucn_category, current_protected_fraction
80
+
81
+ set_iucn_targets <- function(species_df) {
82
+ raw <- dplyr::case_when(
83
+ species_df$iucn_category == "CR" ~ 0.60,
84
+ species_df$iucn_category == "EN" ~ 0.50,
85
+ species_df$iucn_category == "VU" ~ 0.40,
86
+ species_df$iucn_category == "NT" ~ 0.30,
87
+ TRUE ~ 0.17
88
+ )
89
+ pmax(0, raw - species_df$current_protected_fraction)
90
+ }
91
+
92
+ targets_df <- set_iucn_targets(species_meta)
93
+ # Distribution: CR: 0.42 median (gap after 18% already protected)
94
+ # EN: 0.35; VU: 0.28; NT: 0.22
95
+ write.csv(data.frame(feature_name = names(features), target = targets_df),
96
+ "data/iucn_targets.csv", row.names = FALSE)
97
+ ```
98
+
99
+ ### Run baseline prioritization
100
+
101
+ ```bash
102
+ Rscript run_prioritization.R \
103
+ data/pu_cost_5km.tif \
104
+ data/sdm_continuous/ \
105
+ outputs/baseline/ \
106
+ data/iucn_targets.csv \
107
+ data/existing_PAs_5km.tif \
108
+ data/excluded_urban_water.tif \
109
+ 0 \
110
+ NA
111
+ ```
112
+
113
+ **Baseline results:**
114
+
115
+ | Metric | Value |
116
+ |--------|-------|
117
+ | PUs selected | 487 / 3,240 (15.0%) |
118
+ | Total cost | R$ 234M/yr opportunity cost |
119
+ | Features with targets met | 79 / 82 (96.3%) |
120
+ | 3 features below target | *Diclidurus ingens* (CR, 0.41 vs 0.42 target), 2 VU bats |
121
+
122
+ **Action:** The 3 shortfall species have extremely restricted ranges. Manual inspection shows their key PUs are in a locked-out urban buffer. Decision: remove urban buffer restriction for those 3 PUs (no development plans confirmed) → re-run.
123
+
124
+ ---
125
+
126
+ ## Step 3 — Sensitivity Analysis
127
+
128
+ ```bash
129
+ Rscript prioritization_sensitivity.R \
130
+ data/pu_cost_5km.tif \
131
+ data/sdm_continuous/ \
132
+ outputs/sensitivity/ \
133
+ data/iucn_targets.csv \
134
+ data/existing_PAs_5km.tif \
135
+ data/excluded_urban_water.tif
136
+ ```
137
+
138
+ ### BLM calibration
139
+
140
+ | BLM | Cost (R$M/yr) | Boundary length | PUs selected |
141
+ |-----|--------------|-----------------|-------------|
142
+ | 0 | 234 | 18,240 | 487 |
143
+ | 0.001 | 238 | 15,120 | 492 |
144
+ | 0.01 | 251 | 11,880 | 508 |
145
+ | 0.05 | 279 | 8,640 | 531 |
146
+ | 0.1 | 312 | 7,200 | 565 |
147
+
148
+ **Decision:** BLM = 0.01 selected (elbow point — 7% cost increase reduces boundary by 35%). This creates significantly more compact, corridor-compatible solutions.
149
+
150
+ ### Target sensitivity
151
+
152
+ | Target scaling | Mean target | Cost (R$M/yr) | Features met |
153
+ |---------------|-------------|--------------|-------------|
154
+ | 50% | 0.17 | 98 | 82/82 |
155
+ | 75% | 0.26 | 167 | 82/82 |
156
+ | 100% (baseline) | 0.35 | 251 | 82/82 |
157
+ | 125% | 0.44 | 389 | 80/82 |
158
+ | 150% | 0.52 | 521 | 76/82 |
159
+
160
+ **Finding:** Cost increases 3× from 50% to 150% target scaling. Targets above ~130% become infeasible for 2+ species given locked-out area constraints.
161
+
162
+ ---
163
+
164
+ ## Step 4 — Final Solution (BLM = 0.01)
165
+
166
+ ```bash
167
+ Rscript run_prioritization.R \
168
+ data/pu_cost_5km.tif \
169
+ data/sdm_continuous/ \
170
+ outputs/final/ \
171
+ data/iucn_targets.csv \
172
+ data/existing_PAs_5km.tif \
173
+ data/excluded_urban_water.tif \
174
+ 0.01 \
175
+ NA
176
+ ```
177
+
178
+ **Final solution results:**
179
+
180
+ | Metric | Value |
181
+ |--------|-------|
182
+ | PUs selected | 508 (15.7%) |
183
+ | Total opportunity cost | R$ 251M/yr |
184
+ | Area selected | ~12,700 km² |
185
+ | Combined with existing PAs | ~14,600 km² (20.8% of study area) |
186
+ | Features with targets met | 82 / 82 (100%) |
187
+ | Gap to optimality (HiGHS) | 0.8% |
188
+
189
+ **Irreplaceability analysis:**
190
+
191
+ ```r
192
+ irr <- rast("outputs/final/irreplaceability.tif")
193
+ # High-priority areas (top 10% irreplaceability, not yet in PA system)
194
+ high_irr_unprotected <- (irr >= quantile(values(irr), 0.90, na.rm = TRUE)) &
195
+ (rast("data/existing_PAs_5km.tif") == 0)
196
+ n_high_irr_unprotected <- sum(values(high_irr_unprotected) == 1, na.rm = TRUE)
197
+ cat(sprintf("High-irreplaceability, unprotected PUs: %d (%.0f km²)\n",
198
+ n_high_irr_unprotected, n_high_irr_unprotected * 25))
199
+ ```
200
+
201
+ **Result:** 47 PUs (1,175 km²) have irreplaceability > 0.90 and are not currently protected. These are the highest-priority acquisition targets.
202
+
203
+ ---
204
+
205
+ ## Step 5 — Portfolio Irreplaceability Map
206
+
207
+ ```r
208
+ freq <- rast("outputs/sensitivity/portfolio_frequency.tif")
209
+ # Areas selected in > 75% of scenarios = robust priorities
210
+ robust_areas <- freq >= 0.75
211
+ robust_area_km2 <- sum(values(robust_areas) == 1, na.rm = TRUE) * 25
212
+ cat(sprintf("Robust priority areas (selected in >75%% scenarios): %.0f km²\n",
213
+ robust_area_km2))
214
+ ```
215
+
216
+ **Result:** 8,450 km² selected in > 75% of all scenarios. These areas are recommended as the highest-priority acquisition regardless of cost or target assumptions.
217
+
218
+ ---
219
+
220
+ ## Step 6 — Final Outputs
221
+
222
+ ```
223
+ outputs/
224
+ ├── baseline/
225
+ │ ├── solution.tif # 487 PUs selected
226
+ │ ├── feature_representation.csv # 82 species, 79 met targets
227
+ │ ├── cost_summary.csv
228
+ │ └── irreplaceability.tif
229
+ ├── final/ # BLM = 0.01
230
+ │ ├── solution.tif # 508 PUs, all 82 targets met
231
+ │ ├── feature_representation.csv
232
+ │ ├── irreplaceability.tif
233
+ │ └── prioritization_map.png
234
+ └── sensitivity/
235
+ ├── blm_calibration.csv
236
+ ├── blm_calibration_plot.png
237
+ ├── target_sensitivity.csv
238
+ ├── cost_scenario_sensitivity.csv
239
+ └── portfolio_frequency.tif # robust priorities (75% threshold)
240
+ ```
241
+
242
+ ---
243
+
244
+ ## Summary Table
245
+
246
+ | Metric | Value |
247
+ |--------|-------|
248
+ | Planning units | 3,240 (5×5 km) |
249
+ | Features analysed | 82 Atlantic Forest endemics |
250
+ | Existing PA coverage | 8.0% (locked in) |
251
+ | Solution area | 15.7% (+ existing = 20.8%) |
252
+ | All targets met | Yes (82/82) |
253
+ | Opportunity cost | R$ 251M/yr |
254
+ | Gap to optimality | 0.8% |
255
+ | Robust priority area (>75% freq) | 8,450 km² |
256
+ | Irreplaceable unprotected area | 1,175 km² (47 PUs) |
257
+
258
+ ---
259
+
260
+ ## Decision Log
261
+
262
+ ```yaml
263
+ - date: 2026-02-01
264
+ skill_id: spatial-prioritization
265
+ decision: "Log-transform cost; outlier PUs capped at 95th percentile"
266
+ rationale: "5 PUs with cost > 7000 R$/ha dominated solution; log-transform standard practice"
267
+ outputs: ["data/pu_cost_5km.tif"]
268
+
269
+ - date: 2026-02-02
270
+ skill_id: spatial-prioritization
271
+ decision: "BLM = 0.01 selected for final solution"
272
+ rationale: "Elbow of cost-compactness curve; 7% cost increase for 35% boundary reduction"
273
+ outputs: ["outputs/sensitivity/blm_calibration.csv"]
274
+
275
+ - date: 2026-02-03
276
+ skill_id: spatial-prioritization
277
+ decision: "Urban buffer exception for 3 CR bat species PUs"
278
+ rationale: "No confirmed development plans in those PUs; verified with municipal zoning"
279
+ outputs: ["outputs/final/solution.tif"]
280
+ ```
281
+
282
+ ---
283
+
284
+ ## References
285
+
286
+ - Hanson, J.O. et al. (2024). prioritizr: Systematic conservation prioritization in R. *Methods in Ecology and Evolution*, 15(8), 1337–1344. DOI: 10.1111/2041-210X.14376
287
+ - CBD (2022). *Kunming-Montreal Global Biodiversity Framework*, Target 3. COP15.
288
+ - Rodrigues, A.S.L. et al. (2004). Effectiveness of the global protected area network in representing species diversity. *Nature*, 428, 640–643. DOI: 10.1038/nature02422
289
+ - Wilson, K.A. et al. (2009). Conserving biodiversity efficiently: what to do, where, and when. *PLOS Biology*, 7(9), e1000175. DOI: 10.1371/journal.pbio.1000175
@@ -0,0 +1,93 @@
1
+ ---
2
+ skill_id: spatial-prioritization
3
+ example_count: 5
4
+ ---
5
+
6
+ # Spatial Prioritization — Example Prompts
7
+
8
+ ## Scenario 1: 30×30 Target Achievement — Tropical Country
9
+
10
+ **Context:** A tropical nation needs to expand its protected area network from current 14% coverage to 30% by 2030 (Kunming-Montreal GBF Target 3).
11
+
12
+ **Prompt:**
13
+ > "I have SDM suitability layers for 120 species and an opportunity cost surface for a tropical country. Design a minimum-cost protected area network that achieves 30% representation for all species AND covers at least 30% of total land area. Lock in existing PAs."
14
+
15
+ **Expected workflow:**
16
+ 1. Build feature layers (120 species SDM suitability rasters)
17
+ 2. Set targets = 0.30 for all features; add area constraint via `add_min_set_objective`
18
+ 3. Lock in WDPA existing PAs via `locked_in_raster`
19
+ 4. `run_prioritization.R pu_cost.tif features/ outputs/ 0.30 wdpa.tif`
20
+ 5. Check `feature_representation.csv` — verify all 120 features ≥ 30%
21
+ 6. Map solution; compute % land area covered
22
+
23
+ **Key decision points:**
24
+ - If any species < 30% → check if its range is fully within locked-out areas
25
+ - If solution selects > 40% of land → check if some targets can be relaxed for LC species
26
+
27
+ ---
28
+
29
+ ## Scenario 2: Fixed Budget Conservation Planning
30
+
31
+ **Context:** NGO has USD 10M to acquire land in a biodiversity hotspot. Maximise conservation benefit within this budget.
32
+
33
+ **Prompt:**
34
+ > "With a budget of USD 10M, select the set of land parcels (planning units) that maximises the number of threatened species (IUCN CR, EN, VU) whose representation targets are met. Use IUCN-based targets (CR=60%, EN=50%, VU=40%)."
35
+
36
+ **Expected workflow:**
37
+ 1. Compute IUCN targets via `set_iucn_targets()` from resource guide
38
+ 2. Switch to `add_max_features_objective(budget = 10e6)`
39
+ 3. `run_prioritization.R pu_cost.tif features/ outputs/ targets_iucn.csv NA NA 0 10000000`
40
+ 4. Load `feature_representation.csv` → count species above/below target
41
+ 5. Report cost-effectiveness: threatened species-targets met per million USD
42
+
43
+ ---
44
+
45
+ ## Scenario 3: Corridor Design Between Two National Parks
46
+
47
+ **Context:** Two national parks need to be connected by a wildlife corridor. Identify the minimum-cost land parcels connecting them while protecting 40% of corridor species.
48
+
49
+ **Prompt:**
50
+ > "Design a wildlife corridor connecting Park A and Park B. Select planning units that form a connected corridor while meeting 40% representation targets for 25 focal species. Lock in both parks; exclude urban areas."
51
+
52
+ **Expected workflow:**
53
+ 1. Lock in both parks as locked_in_raster
54
+ 2. Lock out urban + water as locked_out_raster
55
+ 3. Set BLM = 0.05 to encourage compact, connected solution
56
+ 4. `run_prioritization.R pu.tif features/ outputs/ 0.40 parks.tif urban_water.tif 0.05`
57
+ 5. Verify solution forms a connected path (use igraph on solution raster)
58
+ 6. Report total corridor length and width at narrowest point
59
+
60
+ ---
61
+
62
+ ## Scenario 4: Sensitivity Analysis for Policy Decision
63
+
64
+ **Context:** Government needs to defend a new protected area design to stakeholders. Show how the solution is robust to cost uncertainty and target choice.
65
+
66
+ **Prompt:**
67
+ > "Run a sensitivity analysis on the conservation solution: (1) BLM calibration to find the optimal compactness, (2) test target scalings from 50% to 150% of the baseline 30% target, (3) test cost uncertainty ±30%. Produce a portfolio irreplaceability map."
68
+
69
+ **Expected workflow:**
70
+ 1. `prioritization_sensitivity.R pu.tif features/ outputs/ 0.30 locked_in.tif locked_out.tif`
71
+ 2. Load `blm_calibration.csv` → plot cost-compactness tradeoff → select elbow BLM
72
+ 3. Load `target_sensitivity.csv` → show cost doubles going from 50% to 150% scaling
73
+ 4. Load `portfolio_frequency.tif` → map areas selected in > 75% of scenarios = robust priorities
74
+
75
+ ---
76
+
77
+ ## Scenario 5: Marine Protected Area Network
78
+
79
+ **Context:** Marine spatial planning for a coastal nation. Design an MPA network covering 30% of territorial waters while protecting key fish spawning areas and coral reefs.
80
+
81
+ **Prompt:**
82
+ > "I have marine biodiversity feature layers (fish spawning areas, coral, seagrass, endemic species SDMs) and a planning unit grid representing fishing revenue as opportunity cost. Design an MPA network meeting 30% targets for all features while minimising cost to fishers."
83
+
84
+ **Expected workflow:**
85
+ 1. Planning units: 1 km² grid cells covering territorial waters; cost = annual fishing revenue
86
+ 2. Features: spawning aggregations, coral cover, seagrass, endemic species suitability
87
+ 3. Locked-out: shipping lanes (navigational requirement), oil/gas concessions
88
+ 4. `run_prioritization.R marine_pu.tif marine_features/ outputs/ 0.30 NA shipping.tif 0.01`
89
+ 5. Report: total fishing revenue lost in solution vs % marine area protected
90
+
91
+ **Key decision points:**
92
+ - If fishing revenue cost unavailable → use inverse of fishing effort as proxy
93
+ - If no-take zones are mandatory in PA regulations → add no-take constraint as binary feature layer
@@ -0,0 +1,130 @@
1
+ ---
2
+ resource_id: cost-surface-reference
3
+ skill_id: spatial-prioritization
4
+ ---
5
+
6
+ # Cost Surface Reference for Conservation Planning
7
+
8
+ ## Types of Conservation Cost
9
+
10
+ | Cost type | Definition | Data source | Bias risk |
11
+ |-----------|-----------|-------------|-----------|
12
+ | **Land acquisition** | Market value of land | Cadastral databases, government assessments | Overvalues urban-adjacent land |
13
+ | **Opportunity cost** | Foregone income if land is protected | Crop productivity + livestock density | Ignores non-market values |
14
+ | **Management cost** | Annual monitoring, enforcement, restoration | Government agency budgets | Variable by governance context |
15
+ | **Transaction cost** | Legal, administrative negotiation costs | Expert estimate | Often ignored; can dominate |
16
+ | **Area (proxy)** | Equal cost per PU (1 per cell) | Always available | No economic information |
17
+
18
+ **Recommended hierarchy:** Opportunity cost > land acquisition cost > area proxy. Use area proxy only when no cost data is available.
19
+
20
+ ---
21
+
22
+ ## Opportunity Cost Construction
23
+
24
+ ```r
25
+ suppressPackageStartupMessages(library(terra))
26
+
27
+ # Components of agricultural opportunity cost
28
+ crop_value <- rast("data/crop_net_revenue_usd_ha.tif") # $/ha/yr
29
+ livestock_value <- rast("data/livestock_density_TLU_ha.tif") # TLU/ha → $/ha/yr
30
+ timber_value <- rast("data/timber_potential_usd_ha.tif") # $/ha
31
+
32
+ # Weight livestock by price per TLU (e.g., 200 USD/TLU/yr)
33
+ livestock_opp <- livestock_value * 200
34
+
35
+ # Total opportunity cost = max of competing land uses
36
+ opp_cost <- max(crop_value, livestock_opp, timber_value, na.rm = TRUE)
37
+
38
+ # Replace NA with low cost (not zero — zero cost attracts solver spuriously)
39
+ opp_cost[is.na(opp_cost)] <- 1
40
+ writeRaster(opp_cost, "data/opportunity_cost.tif", overwrite = TRUE)
41
+ ```
42
+
43
+ ---
44
+
45
+ ## Cost Normalisation
46
+
47
+ ```r
48
+ # Option 1: Standardise to 0–1 (useful for comparing solutions across regions)
49
+ cost_norm <- (opp_cost - global(opp_cost, "min", na.rm = TRUE)[[1]]) /
50
+ (global(opp_cost, "max", na.rm = TRUE)[[1]] -
51
+ global(opp_cost, "min", na.rm = TRUE)[[1]])
52
+
53
+ # Option 2: Log-transform (reduces influence of outlier high-cost cells)
54
+ cost_log <- log1p(opp_cost)
55
+
56
+ # Option 3: Percentile cap (cap at 95th percentile to remove outliers)
57
+ q95 <- quantile(values(opp_cost), 0.95, na.rm = TRUE)
58
+ cost_capped <- min(opp_cost, q95)
59
+ ```
60
+
61
+ **Choose normalisation based on cost distribution.** If cost is approximately log-normal (common for land values), log-transform avoids a few high-cost cells dominating the solution.
62
+
63
+ ---
64
+
65
+ ## Cost Surface Uncertainty
66
+
67
+ Cost data often has high uncertainty. Test prioritisation robustness:
68
+
69
+ ```r
70
+ # Run prioritization under 3 cost scenarios
71
+ cost_low <- cost * 0.7 # -30% uncertainty
72
+ cost_base <- cost
73
+ cost_high <- cost * 1.3 # +30% uncertainty
74
+
75
+ run_scenario <- function(cost_raster, name) {
76
+ p <- problem(cost_raster, features) %>%
77
+ add_min_set_objective() %>%
78
+ add_relative_targets(targets) %>%
79
+ add_highs_solver(gap = 0.01) %>%
80
+ add_binary_decisions()
81
+ s <- solve(p)
82
+ data.frame(scenario = name,
83
+ cost = eval_cost_summary(p, s)$cost,
84
+ n_pu = sum(values(s), na.rm = TRUE))
85
+ }
86
+
87
+ scenarios <- lapply(list(cost_low, cost_base, cost_high),
88
+ list("low", "base", "high"),
89
+ function(c, n) run_scenario(c, n))
90
+ ```
91
+
92
+ ---
93
+
94
+ ## Locked-In and Locked-Out Planning Units
95
+
96
+ | Category | Definition | R implementation |
97
+ |----------|-----------|-----------------|
98
+ | **Locked-in** | Must be selected (existing PAs, community reserves) | `add_locked_in_constraints(locked_in_raster)` |
99
+ | **Locked-out** | Cannot be selected (developed areas, water bodies, military) | `add_locked_out_constraints(locked_out_raster)` |
100
+ | **Semi-locked** | Selected only if budget allows | Remove from locked constraints; let solver decide |
101
+
102
+ ```r
103
+ # Build locked-in raster from protected area shapefile
104
+ pas <- st_read("data/wdpa_protected_areas.shp", quiet = TRUE)
105
+ locked_in <- rasterize(vect(pas), cost, field = 1, background = 0)
106
+ locked_in[is.na(cost)] <- NA # match planning unit extent
107
+
108
+ # Build locked-out from urban and water polygons
109
+ excluded <- st_read("data/urban_water_excluded.shp", quiet = TRUE)
110
+ locked_out <- rasterize(vect(excluded), cost, field = 1, background = 0)
111
+ locked_out[is.na(cost)] <- NA
112
+ ```
113
+
114
+ ---
115
+
116
+ ## Pitfalls
117
+
118
+ - **Zero-cost cells attracting all PUs:** If cost = 0 for some PUs, the solver trivially selects them. Use a minimum cost floor (e.g., 1) for all non-excluded PUs.
119
+ - **Cost misalignment with features:** If cost raster is at 10-km resolution but features at 1-km, resampling introduces spatial mismatch. Always build cost at the same resolution as the analysis.
120
+ - **Ignoring non-market values:** Pure opportunity cost undervalues inaccessible, culturally important, or wilderness areas. Consider supplementing with ecosystem service values.
121
+ - **Using area as cost when land access is the real constraint:** In many developing-country contexts, political access (community agreements, government willingness) is the real cost, not land value. Document which cost metric was used and why.
122
+ - **Static cost surface:** Land values change over time. For 30-year planning horizons, consider projected future opportunity cost (discounted NPV of land use revenues).
123
+
124
+ ---
125
+
126
+ ## References
127
+
128
+ - Naidoo, R. & Iwamura, T. (2007). Global-scale mapping of economic benefits from agricultural lands. *Biological Conservation*, 140(1–2), 40–49. DOI: 10.1016/j.biocon.2007.07.025
129
+ - Polasky, S. et al. (2008). Where to put things? Spatial land management to sustain biodiversity and economic returns. *Biological Conservation*, 141(6), 1505–1524. DOI: 10.1016/j.biocon.2008.03.022
130
+ - Ferraro, P.J. (2003). Asymmetric information and contract design for payments for environmental services. *Ecological Economics*, 65(4), 810–821. DOI: 10.1016/j.ecolecon.2007.07.029