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,184 @@
|
|
|
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
|
+
trend_analysis.py
|
|
7
|
+
Mann-Kendall trend + Sen's slope + anomaly detection.
|
|
8
|
+
Usage: python trend_analysis.py <timeseries_csv> <output_dir> [frequency]
|
|
9
|
+
Requires: pandas, numpy, pymannkendall, matplotlib, scipy
|
|
10
|
+
"""
|
|
11
|
+
import logging
|
|
12
|
+
import sys
|
|
13
|
+
from datetime import datetime
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
SKILL_NAME = "environmental-time-series"
|
|
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 numpy as np
|
|
38
|
+
import pandas as pd
|
|
39
|
+
import matplotlib.pyplot as plt
|
|
40
|
+
from scipy import stats
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
import pymannkendall as mk
|
|
44
|
+
HAS_MK = True
|
|
45
|
+
except ImportError:
|
|
46
|
+
HAS_MK = False
|
|
47
|
+
logger.warning("pymannkendall not installed. Falling back to linear regression. pip install pymannkendall")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def seasonal_decompose_simple(values, freq):
|
|
51
|
+
"""Simple additive seasonal decomposition."""
|
|
52
|
+
n = len(values)
|
|
53
|
+
trend = pd.Series(values).rolling(window=freq, center=True, min_periods=1).mean().values
|
|
54
|
+
detrended = values - trend
|
|
55
|
+
seasonal = np.array([np.nanmean(detrended[i::freq]) for i in range(freq)])
|
|
56
|
+
seasonal_full = np.tile(seasonal, n // freq + 1)[:n]
|
|
57
|
+
remainder = values - trend - seasonal_full
|
|
58
|
+
return trend, seasonal_full, remainder
|
|
59
|
+
|
|
60
|
+
def compute_anomalies(values, baseline_n):
|
|
61
|
+
mu = np.nanmean(values[:baseline_n])
|
|
62
|
+
sd = np.nanstd(values[:baseline_n])
|
|
63
|
+
return (values - mu) / (sd + 1e-10)
|
|
64
|
+
|
|
65
|
+
def main():
|
|
66
|
+
ts_file = sys.argv[1] if len(sys.argv) > 1 else "data/ndvi_series.csv"
|
|
67
|
+
output_dir = Path(sys.argv[2]) if len(sys.argv) > 2 else Path("outputs/timeseries")
|
|
68
|
+
freq = int(sys.argv[3]) if len(sys.argv) > 3 else 12
|
|
69
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
70
|
+
|
|
71
|
+
log_decision("ts_file", ts_file, "Input time series CSV")
|
|
72
|
+
log_decision("freq", freq, "Expected seasonal frequency (12 = monthly annual cycle)")
|
|
73
|
+
log_decision("output_dir", str(output_dir), "Directory for trend analysis outputs")
|
|
74
|
+
|
|
75
|
+
if not Path(ts_file).exists():
|
|
76
|
+
logger.error(
|
|
77
|
+
"Input nao encontrado: %s\n"
|
|
78
|
+
" Causa provavel: passo anterior nao concluiu.\n"
|
|
79
|
+
" Skill anterior que deveria ter produzido este input: geoprocessing-for-ecology",
|
|
80
|
+
ts_file
|
|
81
|
+
)
|
|
82
|
+
sys.exit(1)
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
log_step(1, "Loading time series data")
|
|
86
|
+
dat = pd.read_csv(ts_file)
|
|
87
|
+
val_col = "value" if "value" in dat.columns else dat.columns[-1]
|
|
88
|
+
values = dat[val_col].values.astype(float)
|
|
89
|
+
logger.info("Series length: %d | Frequency: %d", len(values), freq)
|
|
90
|
+
|
|
91
|
+
n_nan = int(np.sum(np.isnan(values)))
|
|
92
|
+
if n_nan > 0:
|
|
93
|
+
logger.warning(
|
|
94
|
+
"%d NaN values detected in series. These will be ignored in statistical tests.",
|
|
95
|
+
n_nan
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
log_step(2, "Running trend test (Mann-Kendall or linear regression)")
|
|
99
|
+
# Mann-Kendall
|
|
100
|
+
if HAS_MK:
|
|
101
|
+
res = mk.original_test(values)
|
|
102
|
+
logger.info(
|
|
103
|
+
"Mann-Kendall: tau = %.4f | p = %.4f | slope = %.6f/obs",
|
|
104
|
+
res.Tau, res.p, res.slope
|
|
105
|
+
)
|
|
106
|
+
log_decision("trend_method", "Mann-Kendall",
|
|
107
|
+
"Preferred non-parametric test for monotonic trends in ecological series")
|
|
108
|
+
trend_df = pd.DataFrame({"tau": [res.Tau], "p_value": [res.p],
|
|
109
|
+
"sens_slope": [res.slope],
|
|
110
|
+
"trend": [res.trend]})
|
|
111
|
+
trend_df.to_csv(output_dir / "trend_results.csv", index=False)
|
|
112
|
+
else:
|
|
113
|
+
log_decision("trend_method", "linear regression",
|
|
114
|
+
"Fallback — pymannkendall not available")
|
|
115
|
+
slope, intercept, r, p, se = stats.linregress(np.arange(len(values)), values)
|
|
116
|
+
logger.info(
|
|
117
|
+
"Linear regression (MK unavailable): slope = %.6f | p = %.4f", slope, p
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
log_step(3, "Seasonal decomposition")
|
|
121
|
+
# Decomposition
|
|
122
|
+
if len(values) >= 2 * freq:
|
|
123
|
+
trend_comp, seasonal_comp, remainder = seasonal_decompose_simple(values, freq)
|
|
124
|
+
fig, axes = plt.subplots(4, 1, figsize=(10, 10), sharex=True)
|
|
125
|
+
for ax, data, title in zip(axes, [values, trend_comp, seasonal_comp, remainder],
|
|
126
|
+
["Observed", "Trend", "Seasonal", "Remainder"]):
|
|
127
|
+
ax.plot(data); ax.set_title(title); ax.grid(alpha=0.3)
|
|
128
|
+
plt.suptitle("Time Series Decomposition")
|
|
129
|
+
plt.tight_layout()
|
|
130
|
+
plt.savefig(output_dir / "decomposition_plot.png", dpi=150)
|
|
131
|
+
plt.close()
|
|
132
|
+
logger.info("Decomposition plot saved to: %s", output_dir / "decomposition_plot.png")
|
|
133
|
+
else:
|
|
134
|
+
logger.warning(
|
|
135
|
+
"Series length (%d) < 2 * freq (%d). Skipping seasonal decomposition.",
|
|
136
|
+
len(values), 2 * freq
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
log_step(4, "Computing anomaly Z-scores")
|
|
140
|
+
# Anomalies
|
|
141
|
+
baseline_n = min(freq * 10, len(values) // 2)
|
|
142
|
+
log_decision("baseline_n", baseline_n,
|
|
143
|
+
"First N observations used as anomaly baseline (min of 10 cycles, half series)")
|
|
144
|
+
anomalies = compute_anomalies(values, baseline_n)
|
|
145
|
+
dat["anomaly_z"] = anomalies
|
|
146
|
+
|
|
147
|
+
n_extreme = int(np.sum(np.abs(anomalies) > 2))
|
|
148
|
+
if n_extreme > 0:
|
|
149
|
+
logger.warning(
|
|
150
|
+
"%d observations exceed ±2 SD anomaly threshold — review for data quality or ecological extremes.",
|
|
151
|
+
n_extreme
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
dat.to_csv(output_dir / "anomaly_series.csv", index=False)
|
|
155
|
+
|
|
156
|
+
log_step(5, "Generating anomaly bar plot")
|
|
157
|
+
fig, ax = plt.subplots(figsize=(10, 4))
|
|
158
|
+
ax.bar(range(len(anomalies)), anomalies,
|
|
159
|
+
color=np.where(anomalies < 0, "#d6604d", "#4393c3"), alpha=0.8)
|
|
160
|
+
ax.axhline(-1.5, color="orange", linestyle="--", label="±1.5σ")
|
|
161
|
+
ax.axhline(1.5, color="orange", linestyle="--")
|
|
162
|
+
ax.axhline(-2, color="red", linestyle="--", label="±2σ")
|
|
163
|
+
ax.axhline(2, color="red", linestyle="--")
|
|
164
|
+
ax.set_xlabel("Time step"); ax.set_ylabel("Standardised anomaly (z)")
|
|
165
|
+
ax.set_title(f"Anomalies (baseline = first {baseline_n} obs)")
|
|
166
|
+
ax.legend(); plt.tight_layout()
|
|
167
|
+
plt.savefig(output_dir / "anomaly_plot.png", dpi=150)
|
|
168
|
+
plt.close()
|
|
169
|
+
logger.info("Outputs written to: %s", output_dir)
|
|
170
|
+
|
|
171
|
+
except FileNotFoundError as e:
|
|
172
|
+
logger.error(
|
|
173
|
+
"Input file not found: %s\n"
|
|
174
|
+
" Expected output from: geoprocessing-for-ecology\n"
|
|
175
|
+
" Check that previous step completed.",
|
|
176
|
+
e
|
|
177
|
+
)
|
|
178
|
+
raise
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.error("Unexpected error in trend analysis: %s", e)
|
|
181
|
+
raise
|
|
182
|
+
|
|
183
|
+
if __name__ == "__main__":
|
|
184
|
+
main()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: geoprocessing-for-ecology
|
|
3
|
+
description: "Handles spatial data operations: reprojection, raster stacking, clipping, extraction, and environmental predictor downloads for ecological analyses. Use this skill when the user needs CRS reprojection, raster masking or cropping, spatial extraction, buffer creation, raster resampling, spatial joins, GeoTIFF processing, shapefile operations, WorldClim/CHELSA/ERA5 predictor downloads, GDAL operations, or predictor stack preparation."
|
|
4
|
+
skill_version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: geoprocessing-for-ecology
|
|
8
|
+
|
|
9
|
+
**Domain:** Spatial analysis · CRS · Raster · Vector · Extraction
|
|
10
|
+
**Phase:** 1 — Foundation
|
|
11
|
+
**Used by:** run-sdm-study, assess-ecological-impact, build-fire-risk-map, analyze-environmental-change, assess-ecosystem-services
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Purpose
|
|
16
|
+
|
|
17
|
+
Guides the agent through spatial data operations needed in quantitative ecology: coordinate reference system management, raster and vector processing, spatial masking, buffer creation, intersection, and extraction of environmental values at occurrence or sample points.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## When to Invoke
|
|
22
|
+
|
|
23
|
+
- Reprojecting layers to a common CRS
|
|
24
|
+
- Clipping rasters or shapefiles to a study area
|
|
25
|
+
- Extracting environmental variables at point locations
|
|
26
|
+
- Creating buffers around sites or features
|
|
27
|
+
- Computing landscape metrics from a land cover layer
|
|
28
|
+
- Stacking multi-band rasters for modeling
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Inputs
|
|
33
|
+
|
|
34
|
+
| Input | Format | Required |
|
|
35
|
+
|-------|--------|----------|
|
|
36
|
+
| Occurrence / sample points | CSV with lat/lon, SHP, GPKG | Yes |
|
|
37
|
+
| Study area polygon | SHP, GPKG, GeoJSON | Yes |
|
|
38
|
+
| Environmental rasters | GeoTIFF, NetCDF | Yes |
|
|
39
|
+
| Land cover or habitat layer | GeoTIFF, SHP | Optional |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Outputs
|
|
44
|
+
|
|
45
|
+
| Output | Description |
|
|
46
|
+
|--------|-------------|
|
|
47
|
+
| `layers_reprojected/` | All layers in common CRS |
|
|
48
|
+
| `predictors_stack.tif` | Aligned, masked raster stack |
|
|
49
|
+
| `points_with_env.csv` | Points with extracted environmental values |
|
|
50
|
+
| `study_area_buffered.gpkg` | Study area with optional buffer |
|
|
51
|
+
| `spatial_qa_report.md` | CRS audit and alignment report |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Steps
|
|
56
|
+
|
|
57
|
+
### 1. CRS Audit
|
|
58
|
+
- Identify the CRS of each input layer
|
|
59
|
+
- Define the project CRS (default: EPSG:4326 for global; UTM zone for regional)
|
|
60
|
+
- Reproject all layers to the project CRS
|
|
61
|
+
- Document the CRS choice and rationale
|
|
62
|
+
|
|
63
|
+
### 2. Extent and Resolution Alignment
|
|
64
|
+
- Define the study area extent from the provided polygon
|
|
65
|
+
- Clip all rasters to the study area extent
|
|
66
|
+
- Resample rasters to a common resolution (define target resolution explicitly)
|
|
67
|
+
- Apply the study area mask to remove cells outside the polygon
|
|
68
|
+
|
|
69
|
+
### 3. Vector Operations
|
|
70
|
+
- Dissolve or simplify study area polygon if needed
|
|
71
|
+
- Create buffers (specify distance and unit)
|
|
72
|
+
- Perform intersection or spatial join between occurrence points and polygon layers
|
|
73
|
+
- Check for topology errors (self-intersections, gaps)
|
|
74
|
+
|
|
75
|
+
### 4. Environmental Extraction
|
|
76
|
+
- Extract raster values at occurrence and background/pseudo-absence points
|
|
77
|
+
- Handle NA values (edge cells, masked areas): document strategy
|
|
78
|
+
- Check for spatial autocorrelation in extracted values if relevant
|
|
79
|
+
|
|
80
|
+
### 5. Raster Stack QA
|
|
81
|
+
- Verify all rasters share the same extent, resolution, CRS, and NA mask
|
|
82
|
+
- Report NA cell counts per layer
|
|
83
|
+
- Visualise layer histograms for anomaly detection
|
|
84
|
+
|
|
85
|
+
### 6. Generate Outputs
|
|
86
|
+
- Write reprojected layers to `layers_reprojected/`
|
|
87
|
+
- Write stacked predictor raster to `predictors_stack.tif`
|
|
88
|
+
- Write points with environmental values to `points_with_env.csv`
|
|
89
|
+
- Write spatial QA report
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Key Decisions to Document
|
|
94
|
+
|
|
95
|
+
- Project CRS (EPSG code)
|
|
96
|
+
- Target raster resolution (and rationale)
|
|
97
|
+
- Resampling method (nearest, bilinear, cubic)
|
|
98
|
+
- NA handling strategy
|
|
99
|
+
- Buffer distance (if used)
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Tools and Libraries
|
|
104
|
+
|
|
105
|
+
**R:** `terra`, `sf`, `rgdal`, `raster`, `landscapemetrics`
|
|
106
|
+
**Python:** `rasterio`, `geopandas`, `pyproj`, `rasterstats`, `fiona`
|
|
107
|
+
**CLI:** `GDAL/OGR` (gdalwarp, ogr2ogr, gdal_calc)
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Resources
|
|
112
|
+
|
|
113
|
+
- `resources/crs-reference.md` — common CRS codes for South American biomes
|
|
114
|
+
- `resources/resampling-methods.md` — when to use each resampling method
|
|
115
|
+
- `examples/` — example extraction workflows
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Notes
|
|
120
|
+
|
|
121
|
+
- Always keep original layers intact; write outputs to separate directories
|
|
122
|
+
- Document the GDAL version used for reproducibility
|
|
123
|
+
- For very large rasters (>4 GB), use tiled/chunked processing
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Example Invocation Prompts — geoprocessing-for-ecology
|
|
2
|
+
|
|
3
|
+
## Stack and Extract
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Load skill: geoprocessing-for-ecology
|
|
7
|
+
Task: I have 19 WorldClim v2.1 rasters (bio1–bio19) at 2.5 arcmin resolution
|
|
8
|
+
and a set of jaguar occurrence points (occ_clean.csv).
|
|
9
|
+
Study area: Amazon biome polygon (amazon_biome.shp).
|
|
10
|
+
1. Reproject everything to EPSG:4326 (already geographic, just check).
|
|
11
|
+
2. Clip rasters to Amazon extent + 1° buffer.
|
|
12
|
+
3. Extract bioclim values at occurrence and 10,000 background points.
|
|
13
|
+
4. Output: predictors_stack.tif, points_with_env.csv.
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Reprojection and Masking
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Load skill: geoprocessing-for-ecology
|
|
20
|
+
Task: Reproject landcover_mapbiomas_2022.tif from SIRGAS2000 (EPSG:4674)
|
|
21
|
+
to WGS84 UTM 22S (EPSG:32722). Then mask to the study area polygon
|
|
22
|
+
(cerrado_boundary.gpkg). Use nearest-neighbour resampling. Target resolution: 30m.
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Buffer and Intersection
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Load skill: geoprocessing-for-ecology
|
|
29
|
+
Task: Create a 5 km buffer around all hydroelectric dam points (dams.shp).
|
|
30
|
+
Intersect with the Atlantic Forest remnant polygons (af_remnants.gpkg).
|
|
31
|
+
Report: total forest area within 5 km of dams (ha), and number of dam-affected patches.
|
|
32
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# CRS Reference — South American Biomes
|
|
2
|
+
|
|
3
|
+
## When to Use Each CRS
|
|
4
|
+
|
|
5
|
+
| Use Case | Recommended CRS | EPSG |
|
|
6
|
+
|----------|----------------|------|
|
|
7
|
+
| Global or continental analysis | WGS 84 Geographic | 4326 |
|
|
8
|
+
| Brazil national (planar, metres) | SIRGAS 2000 / UTM | varies by zone |
|
|
9
|
+
| Area calculations, Amazon biome | SIRGAS 2000 UTM Zone 20S | 31980 |
|
|
10
|
+
| Area calculations, Cerrado | SIRGAS 2000 UTM Zone 22S | 31982 |
|
|
11
|
+
| Area calculations, Atlantic Forest | SIRGAS 2000 UTM Zone 23S | 31983 |
|
|
12
|
+
| Area calculations, Pantanal | SIRGAS 2000 UTM Zone 21S | 31981 |
|
|
13
|
+
| Equal-area, continental South America | South America Albers Equal Area | 102033 |
|
|
14
|
+
| Distance calculations | WGS 84 / World Mercator | 3395 |
|
|
15
|
+
| Remote sensing (MODIS native) | Sinusoidal | 6842 |
|
|
16
|
+
| Landsat / Sentinel (Brazil) | WGS84 UTM zone (varies) | 32718–32724 |
|
|
17
|
+
|
|
18
|
+
## UTM Zone Reference — Brazil
|
|
19
|
+
|
|
20
|
+
| Zone | Longitude Range | States |
|
|
21
|
+
|------|----------------|--------|
|
|
22
|
+
| 18S (32718) | 72°W – 66°W | Far western Acre |
|
|
23
|
+
| 19S (32719) | 66°W – 60°W | Acre, Amazonas W |
|
|
24
|
+
| 20S (32720) | 60°W – 54°W | Amazonas, Pará W, Rondônia |
|
|
25
|
+
| 21S (32721) | 54°W – 48°W | MT, MS, PA centre |
|
|
26
|
+
| 22S (32722) | 48°W – 42°W | GO, TO, MG W |
|
|
27
|
+
| 23S (32723) | 42°W – 36°W | SP, RJ, ES, MG E |
|
|
28
|
+
| 24S (32724) | 36°W – 30°W | BA, SE, AL coast |
|
|
29
|
+
| 25S (32725) | 30°W – 24°W | Fernando de Noronha |
|
|
30
|
+
|
|
31
|
+
## Reprojection Commands
|
|
32
|
+
|
|
33
|
+
### R (terra)
|
|
34
|
+
```r
|
|
35
|
+
library(terra)
|
|
36
|
+
r <- rast("layer.tif")
|
|
37
|
+
r_proj <- project(r, "EPSG:31982") # Bilinear for continuous; near for categorical
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Python (rasterio + pyproj)
|
|
41
|
+
```python
|
|
42
|
+
import rasterio
|
|
43
|
+
from rasterio.warp import reproject, Resampling, calculate_default_transform
|
|
44
|
+
|
|
45
|
+
with rasterio.open("layer.tif") as src:
|
|
46
|
+
transform, width, height = calculate_default_transform(
|
|
47
|
+
src.crs, "EPSG:31982", src.width, src.height, *src.bounds)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### GDAL CLI
|
|
51
|
+
```bash
|
|
52
|
+
gdalwarp -t_srs EPSG:31982 -r bilinear input.tif output.tif
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Resampling Method Guide
|
|
56
|
+
|
|
57
|
+
| Data Type | Method | Note |
|
|
58
|
+
|-----------|--------|------|
|
|
59
|
+
| Continuous (elevation, temperature) | Bilinear | Smooth interpolation |
|
|
60
|
+
| Categorical (land cover, soil class) | Nearest neighbour | Preserves class values |
|
|
61
|
+
| Count or integer | Nearest neighbour or mode | |
|
|
62
|
+
| High-precision DEM | Cubic | Smoother gradients |
|