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,172 @@
1
+ #!/usr/bin/env python3
2
+ # ecological-agent-skills / Copyright (C) 2026 Francisco Diego Barros Barata
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ """
6
+ stack_and_extract.py
7
+ Clip rasters to study area and extract values at points.
8
+ Usage: python stack_and_extract.py <raster_dir> <points_csv> <studyarea_shp> <output_dir>
9
+ Requires: rasterio, geopandas, rasterstats, numpy, pandas
10
+ """
11
+ import logging
12
+ import sys
13
+ from datetime import datetime
14
+ from pathlib import Path
15
+
16
+ SKILL_NAME = "geoprocessing-for-ecology"
17
+ _LOG_DIR = Path("logs")
18
+ _LOG_DIR.mkdir(parents=True, exist_ok=True)
19
+ _log_file = _LOG_DIR / f"skill_{SKILL_NAME}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
20
+ logging.basicConfig(
21
+ level=logging.INFO,
22
+ format="[%(asctime)s] [%(levelname)s] [" + SKILL_NAME + "] %(message)s",
23
+ datefmt="%Y-%m-%d %H:%M:%S",
24
+ handlers=[
25
+ logging.StreamHandler(sys.stdout),
26
+ logging.FileHandler(_log_file, encoding="utf-8"),
27
+ ],
28
+ )
29
+ logger = logging.getLogger(SKILL_NAME)
30
+
31
+ def log_step(n: int, desc: str) -> None:
32
+ logger.info("-- STEP %d: %s", n, desc)
33
+
34
+ def log_decision(var: str, val, why: str) -> None:
35
+ logger.info("DECISION | %s = %s | %s", var, val, why)
36
+
37
+ import os
38
+ import numpy as np
39
+ import pandas as pd
40
+ import geopandas as gpd
41
+ import rasterio
42
+ from rasterio.mask import mask as rio_mask
43
+ from rasterio.warp import reproject, Resampling, calculate_default_transform
44
+ from shapely.geometry import mapping
45
+
46
+
47
+ def reproject_raster(src_path, dst_path, dst_crs):
48
+ with rasterio.open(src_path) as src:
49
+ transform, width, height = calculate_default_transform(
50
+ src.crs, dst_crs, src.width, src.height, *src.bounds)
51
+ kwargs = src.meta.copy()
52
+ kwargs.update({"crs": dst_crs, "transform": transform, "width": width, "height": height})
53
+ with rasterio.open(dst_path, "w", **kwargs) as dst:
54
+ for i in range(1, src.count + 1):
55
+ reproject(source=rasterio.band(src, i), destination=rasterio.band(dst, i),
56
+ src_transform=src.transform, src_crs=src.crs,
57
+ dst_transform=transform, dst_crs=dst_crs,
58
+ resampling=Resampling.bilinear)
59
+
60
+ def clip_to_area(raster_path, area_geom, out_path):
61
+ with rasterio.open(raster_path) as src:
62
+ out_image, out_transform = rio_mask(src, [mapping(area_geom)], crop=True)
63
+ out_meta = src.meta.copy()
64
+ out_meta.update({"transform": out_transform, "width": out_image.shape[2],
65
+ "height": out_image.shape[1]})
66
+ with rasterio.open(out_path, "w", **out_meta) as dst:
67
+ dst.write(out_image)
68
+
69
+ def extract_values(raster_paths, points_df, lon_col="decimalLongitude", lat_col="decimalLatitude"):
70
+ from rasterstats import point_query
71
+ result_df = points_df.copy()
72
+ coords = list(zip(points_df[lon_col], points_df[lat_col]))
73
+ for rpath in raster_paths:
74
+ varname = Path(rpath).stem
75
+ vals = point_query(coords, rpath, interpolate="bilinear")
76
+ result_df[varname] = vals
77
+ return result_df
78
+
79
+ def main():
80
+ raster_dir = sys.argv[1] if len(sys.argv) > 1 else "data/predictors/raw"
81
+ points_file = sys.argv[2] if len(sys.argv) > 2 else "data/processed/data_clean.csv"
82
+ area_file = sys.argv[3] if len(sys.argv) > 3 else "data/spatial/study_area.shp"
83
+ output_dir = Path(sys.argv[4]) if len(sys.argv) > 4 else Path("data/processed")
84
+ output_dir.mkdir(parents=True, exist_ok=True)
85
+
86
+ log_decision("raster_dir", raster_dir, "Directory containing raw predictor GeoTIFFs")
87
+ log_decision("points_file", points_file, "CSV of occurrence/sample points with coordinates")
88
+ log_decision("area_file", area_file, "Shapefile defining the study area extent for clipping")
89
+ log_decision("output_dir", str(output_dir), "Directory for clipped rasters and extracted values")
90
+
91
+ if not Path(raster_dir).exists():
92
+ logger.error(
93
+ "Input nao encontrado: %s\n"
94
+ " Causa provavel: passo anterior nao concluiu.\n"
95
+ " Skill anterior que deveria ter produzido este input: reproducible-ecology-pipeline",
96
+ raster_dir
97
+ )
98
+ sys.exit(1)
99
+
100
+ if not Path(points_file).exists():
101
+ logger.error(
102
+ "Input nao encontrado: %s\n"
103
+ " Causa provavel: passo anterior nao concluiu.\n"
104
+ " Skill anterior que deveria ter produzido este input: reproducible-ecology-pipeline",
105
+ points_file
106
+ )
107
+ sys.exit(1)
108
+
109
+ if not Path(area_file).exists():
110
+ logger.error(
111
+ "Input nao encontrado: %s\n"
112
+ " Causa provavel: passo anterior nao concluiu.\n"
113
+ " Skill anterior que deveria ter produzido este input: reproducible-ecology-pipeline",
114
+ area_file
115
+ )
116
+ sys.exit(1)
117
+
118
+ try:
119
+ log_step(1, "Discovering raster files in raster directory")
120
+ tif_files = sorted(Path(raster_dir).glob("*.tif"))
121
+ logger.info("Rasters found: %d", len(tif_files))
122
+ if len(tif_files) == 0:
123
+ logger.warning(
124
+ "No .tif files found in %s. Check raster_dir path or file extension.", raster_dir
125
+ )
126
+
127
+ log_step(2, "Loading study area geometry")
128
+ area = gpd.read_file(area_file)
129
+ area_geom = area.geometry.unary_union
130
+
131
+ log_step(3, "Clipping rasters to study area extent")
132
+ clipped_dir = output_dir / "predictors_clipped"
133
+ clipped_dir.mkdir(exist_ok=True)
134
+ clipped_paths = []
135
+ for tif in tif_files:
136
+ out_path = clipped_dir / tif.name
137
+ clip_to_area(str(tif), area_geom, str(out_path))
138
+ clipped_paths.append(str(out_path))
139
+ logger.info(" Clipped: %s", tif.name)
140
+
141
+ log_step(4, "Loading point data and extracting raster values")
142
+ pts = pd.read_csv(points_file)
143
+ logger.info("Points loaded: %d", len(pts))
144
+ pts_env = extract_values(clipped_paths, pts)
145
+ pts_env.to_csv(output_dir / "points_with_env.csv", index=False)
146
+
147
+ complete = pts_env.dropna().shape[0]
148
+ incomplete = len(pts) - complete
149
+ logger.info(
150
+ "Points with complete env data: %d/%d", complete, len(pts)
151
+ )
152
+ if incomplete > 0:
153
+ logger.warning(
154
+ "%d points have missing values after extraction — likely fall outside raster extent.",
155
+ incomplete
156
+ )
157
+ logger.info("Output written to: %s", output_dir / "points_with_env.csv")
158
+
159
+ except FileNotFoundError as e:
160
+ logger.error(
161
+ "Input file not found: %s\n"
162
+ " Expected output from: reproducible-ecology-pipeline\n"
163
+ " Check that previous step completed.",
164
+ e
165
+ )
166
+ raise
167
+ except Exception as e:
168
+ logger.error("Unexpected error in stack and extract: %s", e)
169
+ raise
170
+
171
+ if __name__ == "__main__":
172
+ main()
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: landscape-connectivity
3
+ description: "Analyzes landscape connectivity using graph theory, resistance surfaces, and corridor identification for conservation planning. Use this skill when the user mentions habitat connectivity, wildlife corridors, Circuitscape, least-cost paths, resistance surfaces, IIC/dPC connectivity metrics, stepping stones, patch importance ranking, betweenness centrality, fragmentation analysis, landscape graphs, or pinchpoint identification."
4
+ skill_version: 1.0.0
5
+ ---
6
+
7
+ # Skill: landscape-connectivity
8
+
9
+ **Domain:** Landscape connectivity · Graph theory · Circuitscape · Corridor · Resistance surface
10
+
11
+ ---
12
+
13
+ ## Purpose
14
+
15
+ Guides the agent through quantifying structural and functional landscape connectivity, identifying wildlife corridors, and calculating the contribution of individual habitat patches to overall network connectivity. Covers resistance surface construction, graph-based connectivity indices (IIC, PC, dPC), Circuitscape current mapping, and least-cost path analysis for corridor delineation.
16
+
17
+ ---
18
+
19
+ ## When to Invoke
20
+
21
+ Invoke this skill when:
22
+
23
+ - The user asks about habitat connectivity, wildlife corridors, or dispersal pathways
24
+ - Patches of habitat must be ranked by their importance to overall connectivity
25
+ - A resistance surface is needed for Circuitscape or least-cost path modelling
26
+ - The impact of habitat loss or fragmentation on connectivity must be quantified
27
+ - Gene flow, dispersal barriers, or landscape permeability are the focus
28
+
29
+ **trigger_keywords:** `habitat connectivity`, `wildlife corridor`, `dispersal`, `gene flow`, `landscape graph`, `resistance surface`, `least-cost path`, `circuit theory`, `Circuitscape`, `IIC`, `PC`, `dPC`, `corridor width`, `patch importance`, `landscape permeability`
30
+
31
+ ---
32
+
33
+ ## Inputs
34
+
35
+ | Input | Format | Required |
36
+ |---|---|---|
37
+ | Habitat/land cover raster | GeoTIFF | Required |
38
+ | Study area polygon | SHP or GPKG | Required |
39
+ | Dispersal distance or dispersal kernel parameters | CSV or text | Required |
40
+ | Resistance value table (land cover class → resistance) | CSV | Recommended |
41
+ | Existing protected areas or focal nodes | SHP or GPKG | Optional |
42
+ | Habitat class codes | Integer codes or CSV lookup | Optional |
43
+
44
+ ---
45
+
46
+ ## Outputs
47
+
48
+ | Output | Description |
49
+ |---|---|
50
+ | `patch_metrics.csv` | Area, perimeter, IIC, PC, dIIC, dPC per patch |
51
+ | `connectivity_summary.csv` | Global IIC, PC, and top-10 patches by dPC |
52
+ | `top_patches_map.tif` | Raster with dPC value per patch |
53
+ | `connectivity_graph.png` | Habitat patch network diagram |
54
+ | `resistance_surface.tif` | Resistance surface for Circuitscape input |
55
+ | `patch_betweenness.csv` | Betweenness centrality per patch (graph metric) |
56
+ | `least_cost_paths.gpkg` | Least-cost path lines between patch pairs |
57
+
58
+ ---
59
+
60
+ ## Steps
61
+
62
+ 1. **Prepare habitat patches**
63
+ Load land cover raster and mask to study area using `geoprocessing-for-ecology`.
64
+ Reclassify to binary habitat/non-habitat. Extract discrete habitat patches
65
+ with `terra::patches()` or `landscapemetrics::get_patches()`.
66
+ Filter patches below minimum area threshold (document threshold in `decision_log.md`).
67
+
68
+ 2. **Build resistance surface** *(invoke `resistance_surface.R`)*
69
+ Reclassify land cover using the resistance table.
70
+ Apply optional focal smoothing to avoid hard edges.
71
+ Validate: all land cover classes must have an assigned resistance value.
72
+ Output: `resistance_surface.tif`.
73
+
74
+ 3. **Calculate distance matrix between patches**
75
+ Compute pairwise Euclidean or least-cost distances between patch centroids.
76
+ If dispersal distance threshold is unknown, use 3 values for sensitivity analysis:
77
+ conservative (25th percentile), medium (median), optimistic (75th percentile).
78
+
79
+ 4. **Compute connectivity indices** *(invoke `connectivity_metrics.R`)*
80
+ Calculate IIC and PC for each dispersal distance scenario.
81
+ Compute dIIC and dPC (patch removal importance) for all patches.
82
+ Rank patches by dPC to identify conservation priorities.
83
+
84
+ 5. **Run Circuitscape for corridor mapping** *(optional, requires Circuitscape installed)*
85
+ Export focal nodes (top-N patches by dPC) and resistance surface.
86
+ Run in `all-to-one` mode to produce cumulative current density map.
87
+ Interpret high-current corridors as priority dispersal pathways.
88
+
89
+ 6. **Delineate least-cost paths** *(invoke `connectivity_analysis.py`)*
90
+ Use `scikit-image.graph.route_through_array()` for least-cost paths.
91
+ Output vector lines between top-priority patch pairs.
92
+
93
+ 7. **Validate outputs and record decisions**
94
+ Check dPC values sum approximately to the global PC.
95
+ Record dispersal distance scenario, resistance table source, and minimum patch area
96
+ in `decision_log.md`.
97
+
98
+ ---
99
+
100
+ ## Decision Points
101
+
102
+ | Condition | Diagnosis | Recommended Action |
103
+ |---|---|---|
104
+ | Dispersal distance unknown | Sensitivity of results to this parameter is high | Run analysis at 3 distances (conservative, medium, optimistic); report range of outcomes |
105
+ | Landscape < 10 patches | Graph metrics statistically unstable | Use fragmentation metrics from ecological-impact-assessment skill instead |
106
+ | IIC or PC < 0.01 | Extremely low connectivity | Prioritise restoration before corridor analysis; identify bottleneck patches |
107
+ | dPC > 0.8 for a patch already deforested | High-priority patch lost | Escalate to restoration planning; include in gap analysis |
108
+ | All patches isolated (no edges at chosen distance) | Threshold too restrictive | Increase dispersal distance or use resistance-based (cost) distance threshold |
109
+
110
+ ---
111
+
112
+ ## Key Decisions to Document
113
+
114
+ Record the following in `decision_log.md` after running this skill:
115
+
116
+ - Dispersal distance(s) used and their biological justification
117
+ - Source of resistance values (literature, telemetry, expert opinion)
118
+ - Minimum patch area threshold applied and rationale
119
+ - Whether Circuitscape was run; mode used (pairwise, all-to-one, one-to-all)
120
+ - Top-N patches selected for focal node analysis
121
+
122
+ ---
123
+
124
+ ## Tools and Libraries
125
+
126
+ **R**
127
+ ```r
128
+ suppressPackageStartupMessages(library(terra)) # raster operations
129
+ suppressPackageStartupMessages(library(sf)) # vector operations
130
+ suppressPackageStartupMessages(library(landscapemetrics)) # patch extraction and metrics
131
+ suppressPackageStartupMessages(library(igraph)) # graph theory (IIC, PC, betweenness)
132
+ suppressPackageStartupMessages(library(dplyr)) # data manipulation
133
+ suppressPackageStartupMessages(library(ggplot2)) # plotting
134
+ ```
135
+
136
+ **Python**
137
+ ```python
138
+ import networkx as nx # graph metrics (betweenness centrality)
139
+ from skimage.graph import route_through_array # least-cost path
140
+ import geopandas as gpd # vector operations
141
+ import rasterio # raster reading
142
+ import numpy as np # numerical operations
143
+ from pathlib import Path # file system
144
+ ```
145
+
146
+ **CLI**
147
+ ```bash
148
+ # Circuitscape (optional, must be installed)
149
+ circuitscape_4_0 <config_file.ini>
150
+ # or Julia-based CS4
151
+ julia -e "using Circuitscape; compute(<config_file.ini>)"
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Resources
157
+
158
+ - [`skills/landscape-connectivity/resources/graph-theory-for-ecology.md`](resources/graph-theory-for-ecology.md) — IIC, PC, dPC formulas, interpretation, and dispersal threshold definition
159
+ - [`skills/landscape-connectivity/resources/resistance-surface-guide.md`](resources/resistance-surface-guide.md) — Resistance value assignment by taxon, calibration methods, and Circuitscape export
160
+ - [`skills/landscape-connectivity/resources/circuitscape-parameter-guide.md`](resources/circuitscape-parameter-guide.md) — Circuitscape modes, focal nodes, solver selection, and output interpretation
161
+
162
+ ---
163
+
164
+ ## Notes
165
+
166
+ - **IIC and PC are scale-dependent:** Both indices depend on the total landscape area (`A`). Always report the landscape extent used in calculation to enable comparison across studies.
167
+ - **Euclidean distance ≠ functional connectivity:** Distance-based IIC/PC ignores landscape resistance. Use cost-distance for species sensitive to habitat matrix quality (e.g., forest-dependent species crossing agricultural land).
168
+ - **Circuitscape can be slow for large landscapes:** For rasters > 10 million cells, use the Julia implementation (Circuitscape.jl) which is 10–50× faster than the Python version.
169
+ - **Betweenness centrality is a proxy, not a measure of flow:** High betweenness patches are on many shortest paths but may not have high actual dispersal if resistance is high elsewhere.
170
+ - **Patch size confounds dPC:** Large patches tend to have high dPC because their removal drastically reduces total habitat area. Always report patch area alongside dPC to distinguish area effect from connectivity effect.
@@ -0,0 +1,96 @@
1
+ ---
2
+ skill_id: landscape-connectivity
3
+ example_count: 5
4
+ ---
5
+
6
+ # Landscape Connectivity — Example Prompts
7
+
8
+ ## Scenario 1: Forest Fragment Connectivity Assessment
9
+
10
+ **Context:** Atlantic Forest remnants, southeastern Brazil. 85 patches of varying size in a 10,000 km² landscape dominated by pasture and sugarcane.
11
+
12
+ **Prompt:**
13
+ > "I have a shapefile of 85 Atlantic Forest patches in southeastern Brazil. Compute IIC and PC for the landscape at dispersal distances of 500 m, 1 km, and 2 km (to cover uncertainty about ocelot dispersal). Rank patches by dPC and identify the top 10 most important patches for connectivity."
14
+
15
+ **Expected workflow:**
16
+ 1. `connectivity_metrics.R patches.shp outputs/ 1000` (repeat for 500, 2000)
17
+ 2. Compare IIC and PC across three dmax values — check robustness
18
+ 3. Rank by `dPC_pct` descending; report top 10 patches with area and dPC
19
+ 4. Flag patches with dPC > 5% as high-priority conservation targets
20
+
21
+ **Key decision points:**
22
+ - If IIC changes > 50% between dmax = 500 and 2000 m → conduct formal sensitivity analysis before interpreting
23
+ - If top patches overlap with unprotected private land → identify restoration opportunity
24
+
25
+ ---
26
+
27
+ ## Scenario 2: Corridor Identification with Circuitscape
28
+
29
+ **Context:** Two forest blocks separated by 15 km of agricultural land. Identify the most likely movement corridors for pumas and test which land cover types are least permeable.
30
+
31
+ **Prompt:**
32
+ > "I have a resistance surface for pumas based on land cover type. Two forest blocks are the source and destination patches. Run Circuitscape in pairwise mode, identify pinch points in the current map, and test how corridor connectivity changes if the highway between the blocks is removed."
33
+
34
+ **Expected workflow:**
35
+ 1. `resistance_surface.R landcover.tif resistance_table.csv outputs/ dem.tif roads.shp`
36
+ 2. Run Circuitscape via `run_circuitscape()` function (pairwise, 2 nodes)
37
+ 3. Load `*_cum_curmap.asc`; extract top 5% current cells as pinch points
38
+ 4. Remove highway from resistance surface → re-run Circuitscape → compare effective resistance
39
+
40
+ **Expected findings:**
41
+ - Pinch points likely at river crossings and road underpasses
42
+ - Removing highway typically reduces effective resistance by 30–60%
43
+
44
+ ---
45
+
46
+ ## Scenario 3: Stepping Stone Prioritisation for Rewilding
47
+
48
+ **Context:** Degraded agricultural landscape. 12 existing forest patches; 30 candidate restoration sites. Identify which restoration sites most increase connectivity (dPC gain).
49
+
50
+ **Prompt:**
51
+ > "I have 12 existing forest patches and 30 candidate restoration polygons. For each candidate site, calculate how much it would increase PC if restored. Rank candidates by connectivity gain and identify the top 5 restoration priorities."
52
+
53
+ **Expected workflow:**
54
+ 1. Compute PC for existing 12 patches
55
+ 2. For each candidate: add it to patch set → recompute PC → ΔPC = PC_new − PC_baseline
56
+ 3. Rank candidates by ΔPC descending
57
+ 4. Report top 5 candidates with area, cost estimate, and ΔPC
58
+ 5. Map showing candidate sites coloured by ΔPC gain
59
+
60
+ **Key decision points:**
61
+ - If multiple candidates have similar ΔPC → add cost layer and rank by ΔPC/ha
62
+ - If candidate overlaps with existing connectivity graph → betweenness centrality gain is secondary metric
63
+
64
+ ---
65
+
66
+ ## Scenario 4: Genetic Isolation by Resistance Test
67
+
68
+ **Context:** 8 populations of a montane amphibian sampled for genetic markers. Test whether resistance distance (based on land cover) predicts genetic differentiation better than Euclidean distance.
69
+
70
+ **Prompt:**
71
+ > "I have FST values for 8 amphibian populations and a resistance surface based on elevation and land cover. Compute pairwise resistance distances using Circuitscape, then compare IBR (isolation by resistance) vs IBD (isolation by distance) models using a partial Mantel test."
72
+
73
+ **Expected workflow:**
74
+ 1. `resistance_surface.R` → combined resistance for amphibians
75
+ 2. Circuitscape pairwise for 8 population locations → effective resistance matrix
76
+ 3. Partial Mantel test: `FST ~ resistance | Euclidean` using `vegan::mantel.partial()`
77
+ 4. Report: if resistance explains more variance than distance → resistance model supported
78
+
79
+ ---
80
+
81
+ ## Scenario 5: Connectivity Change Before and After Deforestation
82
+
83
+ **Context:** Long-term forest loss monitoring. Compute connectivity for years 2000, 2010, 2020 to quantify fragmentation over time.
84
+
85
+ **Prompt:**
86
+ > "I have three land cover maps (2000, 2010, 2020) for a deforestation frontier in Amazonia. Compute IIC and PC for each year at dmax = 2 km. Quantify patch loss, connectivity loss, and identify which patches were permanently deforested."
87
+
88
+ **Expected workflow:**
89
+ 1. `connectivity_metrics.R` for each year → `landscape_summary.csv` per year
90
+ 2. Join summaries; compute annual % change in IIC, PC, n_patches, mean_area
91
+ 3. Identify patches present in 2000 but absent in 2020 → permanent deforestation
92
+ 4. Plot IIC/PC trend over time with error bars from dmax sensitivity (500–5000 m)
93
+
94
+ **Expected findings:**
95
+ - Typical Amazonian deforestation frontier: IIC losses of 15–40% per decade
96
+ - PC declines faster than IIC at early stages (many small patches lose probabilistic connections before binary connectivity is severed)