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.
- package/AGENT_CONTEXT.md +191 -0
- package/CATALOG.md +329 -0
- package/LICENSE +692 -0
- package/README.md +347 -0
- package/bin/install.mjs +168 -0
- package/docs/comparison-with-alternatives.md +38 -0
- package/docs/global-examples-index.md +103 -0
- package/docs/repository-statistics.md +101 -0
- package/docs/theoretical-foundations.md +188 -0
- package/environment.yaml +106 -0
- package/examples/community/arctic_tundra_vegetation_example.md +247 -0
- package/examples/community/bird_landuse_example.md +63 -0
- package/examples/community/phytoplankton_reservoir_example.md +60 -0
- package/examples/community/reef_fish_indopacific_example.md +221 -0
- package/examples/impact/baci_road_example.md +57 -0
- package/examples/impact/ecosystem_services_atlantic_forest.md +83 -0
- package/examples/impact/forest_loss_borneo_timeseries_example.md +225 -0
- package/examples/occupancy/puma_camera_example.md +61 -0
- package/examples/occupancy/snow_leopard_himalayas_example.md +204 -0
- package/examples/reproducible/whittaker_biome_sdm_example.md +406 -0
- package/examples/sdm/anteater_cerrado_example.md +69 -0
- package/examples/sdm/jaguar_amazon_example.md +80 -0
- package/examples/sdm/koala_climate_change_example.md +170 -0
- package/examples/sdm/wolf_recolonization_europe_example.md +193 -0
- package/package.json +43 -0
- package/renv.lock +194 -0
- package/skills/SKILL_INDEX.json +1020 -0
- package/skills/acoustic-monitoring/SKILL.md +163 -0
- package/skills/acoustic-monitoring/examples/example-prompts.md +100 -0
- package/skills/acoustic-monitoring/examples/temperate_forest_birds_example.md +285 -0
- package/skills/acoustic-monitoring/resources/acoustic-indices-reference.md +93 -0
- package/skills/acoustic-monitoring/resources/soundscape-ecology-guide.md +90 -0
- package/skills/acoustic-monitoring/resources/species-id-tools-comparison.md +89 -0
- package/skills/acoustic-monitoring/scripts/batch_species_detection.py +360 -0
- package/skills/acoustic-monitoring/scripts/compute_acoustic_indices.R +235 -0
- package/skills/acoustic-monitoring/scripts/compute_acoustic_indices.py +374 -0
- package/skills/biostatistics-workbench/SKILL.md +140 -0
- package/skills/biostatistics-workbench/examples/example-prompts.md +39 -0
- package/skills/biostatistics-workbench/resources/effect-size-reference.md +81 -0
- package/skills/biostatistics-workbench/resources/glm-family-link-reference.md +47 -0
- package/skills/biostatistics-workbench/resources/test-selection-guide.md +93 -0
- package/skills/biostatistics-workbench/scripts/glm_pipeline.R +78 -0
- package/skills/biostatistics-workbench/scripts/glm_pipeline.py +210 -0
- package/skills/camera-trap-processing/SKILL.md +159 -0
- package/skills/camera-trap-processing/examples/example-prompts.md +103 -0
- package/skills/camera-trap-processing/examples/leopard_serengeti_example.md +231 -0
- package/skills/camera-trap-processing/resources/activity-patterns-reference.md +113 -0
- package/skills/camera-trap-processing/resources/camtrapR-workflow-guide.md +130 -0
- package/skills/camera-trap-processing/resources/detection-event-definition-guide.md +89 -0
- package/skills/camera-trap-processing/scripts/estimate_activity.R +169 -0
- package/skills/camera-trap-processing/scripts/process_camtrap_data.R +179 -0
- package/skills/camera-trap-processing/scripts/process_camtrap_data.py +192 -0
- package/skills/community-ecology-ordination/SKILL.md +133 -0
- package/skills/community-ecology-ordination/examples/example-prompts.md +35 -0
- package/skills/community-ecology-ordination/resources/dissimilarity-metric-guide.md +53 -0
- package/skills/community-ecology-ordination/resources/nmds-interpretation-guide.md +104 -0
- package/skills/community-ecology-ordination/scripts/__pycache__/community_analysis.cpython-311.pyc +0 -0
- package/skills/community-ecology-ordination/scripts/community_analysis.R +143 -0
- package/skills/community-ecology-ordination/scripts/community_analysis.py +231 -0
- package/skills/ecological-data-foundation/SKILL.md +129 -0
- package/skills/ecological-data-foundation/examples/example-prompts.md +40 -0
- package/skills/ecological-data-foundation/resources/coordinate-cleaning-flags.md +66 -0
- package/skills/ecological-data-foundation/resources/darwin-core-glossary.md +91 -0
- package/skills/ecological-data-foundation/resources/data-citation-guide.md +265 -0
- package/skills/ecological-data-foundation/resources/gbif-data-citation-guide.md +193 -0
- package/skills/ecological-data-foundation/resources/qa-checklist.md +83 -0
- package/skills/ecological-data-foundation/scripts/__pycache__/clean_occurrences.cpython-311.pyc +0 -0
- package/skills/ecological-data-foundation/scripts/__pycache__/download_from_ebird.cpython-311.pyc +0 -0
- package/skills/ecological-data-foundation/scripts/__pycache__/download_from_inat.cpython-311.pyc +0 -0
- package/skills/ecological-data-foundation/scripts/__pycache__/download_from_iucn.cpython-311.pyc +0 -0
- package/skills/ecological-data-foundation/scripts/__pycache__/download_from_obis.cpython-311.pyc +0 -0
- package/skills/ecological-data-foundation/scripts/clean_occurrences.R +230 -0
- package/skills/ecological-data-foundation/scripts/clean_occurrences.py +268 -0
- package/skills/ecological-data-foundation/scripts/download_from_ebird.R +251 -0
- package/skills/ecological-data-foundation/scripts/download_from_ebird.py +364 -0
- package/skills/ecological-data-foundation/scripts/download_from_gbif.R +315 -0
- package/skills/ecological-data-foundation/scripts/download_from_gbif.py +407 -0
- package/skills/ecological-data-foundation/scripts/download_from_inat.R +238 -0
- package/skills/ecological-data-foundation/scripts/download_from_inat.py +304 -0
- package/skills/ecological-data-foundation/scripts/download_from_iucn.R +273 -0
- package/skills/ecological-data-foundation/scripts/download_from_iucn.py +344 -0
- package/skills/ecological-data-foundation/scripts/download_from_obis.R +248 -0
- package/skills/ecological-data-foundation/scripts/download_from_obis.py +318 -0
- package/skills/ecological-impact-assessment/SKILL.md +123 -0
- package/skills/ecological-impact-assessment/examples/example-prompts.md +32 -0
- package/skills/ecological-impact-assessment/resources/baci-design-guide.md +55 -0
- package/skills/ecological-impact-assessment/resources/fragmentation-metrics-reference.md +86 -0
- package/skills/ecological-impact-assessment/resources/pressure-index-template.md +78 -0
- package/skills/ecological-impact-assessment/resources/study-design-guide.md +168 -0
- package/skills/ecological-impact-assessment/scripts/baci_analysis.R +161 -0
- package/skills/ecological-impact-assessment/scripts/fragmentation_analysis.py +141 -0
- package/skills/ecological-impact-assessment/scripts/power_analysis_baci.R +274 -0
- package/skills/ecosystem-services-assessment/SKILL.md +125 -0
- package/skills/ecosystem-services-assessment/examples/example-prompts.md +24 -0
- package/skills/ecosystem-services-assessment/resources/es-indicator-reference.md +45 -0
- package/skills/ecosystem-services-assessment/resources/invest-parameter-guide.md +86 -0
- package/skills/ecosystem-services-assessment/resources/rusle-coefficients.md +88 -0
- package/skills/ecosystem-services-assessment/scripts/__pycache__/compute_es.cpython-311.pyc +0 -0
- package/skills/ecosystem-services-assessment/scripts/compute_es.py +189 -0
- package/skills/ecosystem-services-assessment/scripts/tradeoff_analysis.R +161 -0
- package/skills/environmental-time-series/SKILL.md +125 -0
- package/skills/environmental-time-series/examples/example-prompts.md +33 -0
- package/skills/environmental-time-series/resources/anomaly-indices-reference.md +88 -0
- package/skills/environmental-time-series/resources/bfast-parameter-guide.md +69 -0
- package/skills/environmental-time-series/scripts/__pycache__/recovery_trajectory.cpython-311.pyc +0 -0
- package/skills/environmental-time-series/scripts/__pycache__/trend_analysis.cpython-311.pyc +0 -0
- package/skills/environmental-time-series/scripts/recovery_trajectory.R +305 -0
- package/skills/environmental-time-series/scripts/recovery_trajectory.py +178 -0
- package/skills/environmental-time-series/scripts/trend_analysis.R +192 -0
- package/skills/environmental-time-series/scripts/trend_analysis.py +184 -0
- package/skills/geoprocessing-for-ecology/SKILL.md +123 -0
- package/skills/geoprocessing-for-ecology/examples/example-prompts.md +32 -0
- package/skills/geoprocessing-for-ecology/resources/crs-reference.md +62 -0
- package/skills/geoprocessing-for-ecology/resources/global-predictor-sources.md +331 -0
- package/skills/geoprocessing-for-ecology/resources/resampling-methods.md +57 -0
- package/skills/geoprocessing-for-ecology/scripts/__pycache__/download_predictors.cpython-311.pyc +0 -0
- package/skills/geoprocessing-for-ecology/scripts/download_predictors.R +239 -0
- package/skills/geoprocessing-for-ecology/scripts/download_predictors.py +379 -0
- package/skills/geoprocessing-for-ecology/scripts/stack_and_extract.R +224 -0
- package/skills/geoprocessing-for-ecology/scripts/stack_and_extract.py +172 -0
- package/skills/landscape-connectivity/SKILL.md +170 -0
- package/skills/landscape-connectivity/examples/example-prompts.md +96 -0
- package/skills/landscape-connectivity/examples/jaguar_mesoamerica_corridor_example.md +271 -0
- package/skills/landscape-connectivity/resources/circuitscape-parameter-guide.md +155 -0
- package/skills/landscape-connectivity/resources/graph-theory-for-ecology.md +134 -0
- package/skills/landscape-connectivity/resources/resistance-surface-guide.md +141 -0
- package/skills/landscape-connectivity/scripts/connectivity_analysis.py +387 -0
- package/skills/landscape-connectivity/scripts/connectivity_metrics.R +274 -0
- package/skills/landscape-connectivity/scripts/resistance_surface.R +239 -0
- package/skills/model-validation-and-uncertainty/SKILL.md +131 -0
- package/skills/model-validation-and-uncertainty/examples/example-prompts.md +30 -0
- package/skills/model-validation-and-uncertainty/resources/extrapolation-risk-guide.md +236 -0
- package/skills/model-validation-and-uncertainty/resources/metric-selection-guide.md +52 -0
- package/skills/model-validation-and-uncertainty/resources/threshold-selection-guide.md +64 -0
- package/skills/model-validation-and-uncertainty/scripts/__pycache__/validate_model.cpython-311.pyc +0 -0
- package/skills/model-validation-and-uncertainty/scripts/extrapolation_risk.R +315 -0
- package/skills/model-validation-and-uncertainty/scripts/validate_model.py +226 -0
- package/skills/model-validation-and-uncertainty/scripts/validate_sdm.R +162 -0
- package/skills/occupancy-and-detection/SKILL.md +126 -0
- package/skills/occupancy-and-detection/examples/example-prompts.md +33 -0
- package/skills/occupancy-and-detection/resources/detection-history-format.md +100 -0
- package/skills/occupancy-and-detection/resources/occupancy-study-design.md +47 -0
- package/skills/occupancy-and-detection/scripts/__pycache__/occupancy_analysis.cpython-311.pyc +0 -0
- package/skills/occupancy-and-detection/scripts/occupancy_analysis.R +160 -0
- package/skills/occupancy-and-detection/scripts/occupancy_analysis.py +159 -0
- package/skills/population-viability-analysis/SKILL.md +161 -0
- package/skills/population-viability-analysis/examples/african_elephant_pva_example.md +266 -0
- package/skills/population-viability-analysis/examples/example-prompts.md +95 -0
- package/skills/population-viability-analysis/resources/extinction-risk-thresholds.md +128 -0
- package/skills/population-viability-analysis/resources/matrix-model-guide.md +139 -0
- package/skills/population-viability-analysis/resources/sensitivity-elasticity-reference.md +182 -0
- package/skills/population-viability-analysis/scripts/matrix_pva.R +258 -0
- package/skills/population-viability-analysis/scripts/pva_analysis.py +442 -0
- package/skills/population-viability-analysis/scripts/stochastic_pva.R +353 -0
- package/skills/predictive-modeling-best-practices/SKILL.md +136 -0
- package/skills/predictive-modeling-best-practices/examples/example-prompts.md +58 -0
- package/skills/predictive-modeling-best-practices/resources/collinearity-decision-tree.md +65 -0
- package/skills/predictive-modeling-best-practices/resources/sampling-bias-correction.md +267 -0
- package/skills/predictive-modeling-best-practices/resources/spatial-cv-guide.md +73 -0
- package/skills/predictive-modeling-best-practices/scripts/__pycache__/spatial_cv.cpython-311.pyc +0 -0
- package/skills/predictive-modeling-best-practices/scripts/collinearity_check.R +112 -0
- package/skills/predictive-modeling-best-practices/scripts/spatial_cv.py +182 -0
- package/skills/reproducible-ecology-pipeline/SKILL.md +139 -0
- package/skills/reproducible-ecology-pipeline/examples/example-prompts.md +35 -0
- package/skills/reproducible-ecology-pipeline/resources/directory-structure-template.md +94 -0
- package/skills/reproducible-ecology-pipeline/resources/params-yaml-template.yaml +84 -0
- package/skills/reproducible-ecology-pipeline/resources/reproducibility-checklist-template.md +66 -0
- package/skills/reproducible-ecology-pipeline/scripts/generate_file_manifest.py +110 -0
- package/skills/reproducible-ecology-pipeline/scripts/init_project.sh +53 -0
- package/skills/spatial-prioritization/SKILL.md +162 -0
- package/skills/spatial-prioritization/examples/biodiversity_hotspot_prioritization_example.md +289 -0
- package/skills/spatial-prioritization/examples/example-prompts.md +93 -0
- package/skills/spatial-prioritization/resources/cost-surface-reference.md +130 -0
- package/skills/spatial-prioritization/resources/marxan-vs-prioritizr-comparison.md +125 -0
- package/skills/spatial-prioritization/resources/prioritizr-formulation-guide.md +188 -0
- package/skills/spatial-prioritization/resources/representation-targets-guide.md +186 -0
- package/skills/spatial-prioritization/scripts/prioritization_sensitivity.R +320 -0
- package/skills/spatial-prioritization/scripts/run_prioritization.R +336 -0
- package/skills/species-distribution-modeling/SKILL.md +139 -0
- package/skills/species-distribution-modeling/examples/example-prompts.md +36 -0
- package/skills/species-distribution-modeling/resources/algorithm-comparison.md +25 -0
- package/skills/species-distribution-modeling/resources/calibration-area-guide.md +71 -0
- package/skills/species-distribution-modeling/resources/climate-scenario-preparation.md +170 -0
- package/skills/species-distribution-modeling/resources/maxent-calibration-guide.md +211 -0
- package/skills/species-distribution-modeling/resources/sdm-checklist.md +37 -0
- package/skills/species-distribution-modeling/scripts/predict_distribution.R +236 -0
- package/skills/species-distribution-modeling/scripts/predict_distribution.py +286 -0
- package/skills/species-distribution-modeling/scripts/prepare_future_layers.R +351 -0
- package/skills/species-distribution-modeling/scripts/project_scenarios.R +220 -0
- package/skills/species-distribution-modeling/scripts/run_ensemble_sdm.R +99 -0
- package/skills/species-distribution-modeling/scripts/sdm_pipeline.py +318 -0
- package/skills/species-distribution-modeling/scripts/tune_maxnet.R +344 -0
- package/templates/SKILL_TEMPLATE.md +225 -0
- package/templates/checklists/data-submission-checklist.md +38 -0
- package/templates/checklists/post-analysis-checklist.md +55 -0
- package/templates/checklists/pre-analysis-checklist.md +31 -0
- package/templates/prompts/debug-skill.md +47 -0
- package/templates/prompts/invoke-skill.md +34 -0
- package/templates/prompts/invoke-workflow.md +45 -0
- package/templates/reports/technical-report-template.md +80 -0
- package/templates/scripts/logger_setup.R +79 -0
- package/templates/scripts/logger_setup.py +119 -0
- package/templates/scripts/params_loader.R +28 -0
- package/templates/scripts/params_loader.py +38 -0
- package/workflows/analyze-community-structure/WORKFLOW.md +72 -0
- package/workflows/analyze-environmental-change/WORKFLOW.md +73 -0
- package/workflows/assess-ecological-impact/WORKFLOW.md +75 -0
- package/workflows/assess-ecosystem-services/WORKFLOW.md +68 -0
- package/workflows/assess-landscape-connectivity/WORKFLOW.md +84 -0
- package/workflows/build-fire-risk-map/WORKFLOW.md +79 -0
- package/workflows/produce-technical-report/WORKFLOW.md +113 -0
- package/workflows/run-camera-trap-occupancy/WORKFLOW.md +87 -0
- package/workflows/run-conservation-prioritization/WORKFLOW.md +89 -0
- package/workflows/run-multispecies-screening/WORKFLOW.md +197 -0
- package/workflows/run-occupancy-analysis/WORKFLOW.md +74 -0
- package/workflows/run-population-viability/WORKFLOW.md +90 -0
- 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)
|