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,318 @@
|
|
|
1
|
+
# ecological-agent-skills / Copyright (C) 2026 Francisco Diego Barros Barata
|
|
2
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
3
|
+
|
|
4
|
+
"""Download marine occurrence records from OBIS (Ocean Biodiversity Information System).
|
|
5
|
+
|
|
6
|
+
Usage: python download_from_obis.py <species_name_or_list_csv> <output_dir> [year_from] [year_to] [wkt_geometry]
|
|
7
|
+
|
|
8
|
+
Arguments:
|
|
9
|
+
species_name_or_list_csv : Species name (e.g., "Chelonia mydas") or path to CSV
|
|
10
|
+
with column 'scientificName'
|
|
11
|
+
output_dir : Directory for outputs (created if absent)
|
|
12
|
+
year_from : Minimum year of observation (default: 1950)
|
|
13
|
+
year_to : Maximum year of observation (default: current year)
|
|
14
|
+
wkt_geometry : WKT polygon to restrict query (optional)
|
|
15
|
+
|
|
16
|
+
Outputs (per species):
|
|
17
|
+
occurrences_raw_OBIS_{species}_{date}.csv — standardised occurrence records
|
|
18
|
+
download_metadata_OBIS_{species}.txt — download provenance and citation
|
|
19
|
+
|
|
20
|
+
Standard output schema:
|
|
21
|
+
species, decimalLatitude, decimalLongitude, eventDate, countryCode,
|
|
22
|
+
basisOfRecord, coordinateUncertaintyInMeters, datasetName, occurrenceID,
|
|
23
|
+
source, download_doi
|
|
24
|
+
Extra OBIS columns:
|
|
25
|
+
depth, marine
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
import logging
|
|
29
|
+
import sys
|
|
30
|
+
from datetime import datetime
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
SKILL_NAME = "ecological-data-foundation"
|
|
34
|
+
_LOG_DIR = Path("logs")
|
|
35
|
+
_LOG_DIR.mkdir(parents=True, exist_ok=True)
|
|
36
|
+
_log_file = _LOG_DIR / f"skill_{SKILL_NAME}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
|
|
37
|
+
logging.basicConfig(
|
|
38
|
+
level=logging.INFO,
|
|
39
|
+
format="[%(asctime)s] [%(levelname)s] [" + SKILL_NAME + "] %(message)s",
|
|
40
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
41
|
+
handlers=[
|
|
42
|
+
logging.StreamHandler(sys.stdout),
|
|
43
|
+
logging.FileHandler(_log_file, encoding="utf-8"),
|
|
44
|
+
],
|
|
45
|
+
)
|
|
46
|
+
logger = logging.getLogger(SKILL_NAME)
|
|
47
|
+
|
|
48
|
+
def log_step(n: int, desc: str) -> None:
|
|
49
|
+
logger.info("-- STEP %d: %s", n, desc)
|
|
50
|
+
|
|
51
|
+
def log_decision(var: str, val, why: str) -> None:
|
|
52
|
+
logger.info("DECISION | %s = %s | %s", var, val, why)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
from datetime import date
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
import requests
|
|
59
|
+
import pandas as pd
|
|
60
|
+
except ImportError as e:
|
|
61
|
+
logger.error(
|
|
62
|
+
"Dependencia ausente: %s\n Instale com: pip install requests pandas\n Skill anterior: ecological-data-foundation",
|
|
63
|
+
e,
|
|
64
|
+
)
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# ── Constants ─────────────────────────────────────────────────────────────────
|
|
69
|
+
OBIS_API_BASE = "https://api.obis.org/v3"
|
|
70
|
+
PAGE_SIZE = 5000 # OBIS max per request
|
|
71
|
+
BAD_FLAGS = {"NO_COORD", "ZERO_COORD", "ON_LAND", "DEPTH_EXCEEDS_BATH"}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# ── Helper functions ──────────────────────────────────────────────────────────
|
|
75
|
+
|
|
76
|
+
def fetch_obis(scientificname: str, year_from: int, year_to: int,
|
|
77
|
+
wkt_geometry: str | None) -> list[dict]:
|
|
78
|
+
"""Fetch all OBIS records for a species, handling pagination."""
|
|
79
|
+
url = f"{OBIS_API_BASE}/occurrence"
|
|
80
|
+
offset = 0
|
|
81
|
+
all_results: list[dict] = []
|
|
82
|
+
|
|
83
|
+
while True:
|
|
84
|
+
params: dict = {
|
|
85
|
+
"scientificname": scientificname,
|
|
86
|
+
"absence": "exclude",
|
|
87
|
+
"startdate": f"{year_from}-01-01",
|
|
88
|
+
"enddate": f"{year_to}-12-31",
|
|
89
|
+
"size": PAGE_SIZE,
|
|
90
|
+
"offset": offset,
|
|
91
|
+
}
|
|
92
|
+
if wkt_geometry:
|
|
93
|
+
params["geometry"] = wkt_geometry
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
resp = requests.get(url, params=params, timeout=60)
|
|
97
|
+
resp.raise_for_status()
|
|
98
|
+
data = resp.json()
|
|
99
|
+
except requests.RequestException as e:
|
|
100
|
+
logger.error(
|
|
101
|
+
"Falha na requisicao OBIS (offset=%d, especie='%s'): %s\n Causa provavel: sem conexao com a internet ou API OBIS indisponivel.\n Verifique: https://api.obis.org/\n Skill anterior: ecological-data-foundation",
|
|
102
|
+
offset, scientificname, e,
|
|
103
|
+
)
|
|
104
|
+
raise
|
|
105
|
+
|
|
106
|
+
results = data.get("results", [])
|
|
107
|
+
if not results:
|
|
108
|
+
break
|
|
109
|
+
|
|
110
|
+
all_results.extend(results)
|
|
111
|
+
logger.info("Pagina offset=%d: %d registros recuperados (total: %d)",
|
|
112
|
+
offset, len(results), len(all_results))
|
|
113
|
+
|
|
114
|
+
total = data.get("total", 0)
|
|
115
|
+
if len(all_results) >= total:
|
|
116
|
+
break
|
|
117
|
+
if len(results) < PAGE_SIZE:
|
|
118
|
+
break
|
|
119
|
+
offset += PAGE_SIZE
|
|
120
|
+
|
|
121
|
+
return all_results
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def apply_quality_flags(records: list[dict]) -> list[dict]:
|
|
125
|
+
"""Remove records with known OBIS quality issues."""
|
|
126
|
+
filtered = []
|
|
127
|
+
n_flagged = 0
|
|
128
|
+
for rec in records:
|
|
129
|
+
flags_raw = rec.get("flags", "") or ""
|
|
130
|
+
flags = set(str(flags_raw).upper().split(","))
|
|
131
|
+
if flags & BAD_FLAGS:
|
|
132
|
+
n_flagged += 1
|
|
133
|
+
continue
|
|
134
|
+
filtered.append(rec)
|
|
135
|
+
if n_flagged > 0:
|
|
136
|
+
logger.warning("%d registros removidos por flags OBIS de qualidade.", n_flagged)
|
|
137
|
+
return filtered
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def standardise_records(records: list[dict], species_name: str) -> "pd.DataFrame":
|
|
141
|
+
"""Convert OBIS record dicts to the standard occurrence schema."""
|
|
142
|
+
rows = []
|
|
143
|
+
for rec in records:
|
|
144
|
+
rows.append({
|
|
145
|
+
"species": species_name,
|
|
146
|
+
"decimalLatitude": rec.get("decimalLatitude"),
|
|
147
|
+
"decimalLongitude": rec.get("decimalLongitude"),
|
|
148
|
+
"eventDate": rec.get("eventDate") or rec.get("date_start", ""),
|
|
149
|
+
"countryCode": rec.get("countryCode", ""),
|
|
150
|
+
"basisOfRecord": rec.get("basisOfRecord", "OCCURRENCE"),
|
|
151
|
+
"coordinateUncertaintyInMeters": rec.get("coordinateUncertaintyInMeters"),
|
|
152
|
+
"datasetName": rec.get("datasetName", ""),
|
|
153
|
+
"occurrenceID": str(rec.get("occurrenceID", rec.get("id", ""))),
|
|
154
|
+
"source": "OBIS",
|
|
155
|
+
"download_doi": None,
|
|
156
|
+
"depth": rec.get("depth"),
|
|
157
|
+
"marine": True,
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
df = pd.DataFrame(rows)
|
|
161
|
+
df["decimalLatitude"] = pd.to_numeric(df["decimalLatitude"], errors="coerce")
|
|
162
|
+
df["decimalLongitude"] = pd.to_numeric(df["decimalLongitude"], errors="coerce")
|
|
163
|
+
df = df.dropna(subset=["decimalLatitude", "decimalLongitude"])
|
|
164
|
+
return df.reset_index(drop=True)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def save_metadata(output_dir: Path, species_name: str, n_records: int,
|
|
168
|
+
year_from: int, year_to: int, wkt_geometry: str | None) -> None:
|
|
169
|
+
safe_name = species_name.replace(" ", "_")
|
|
170
|
+
today = date.today().isoformat()
|
|
171
|
+
year = date.today().year
|
|
172
|
+
lines = [
|
|
173
|
+
f"Species: {species_name}",
|
|
174
|
+
f"Source: Ocean Biodiversity Information System (OBIS) — https://obis.org",
|
|
175
|
+
f"API endpoint: {OBIS_API_BASE}/occurrence",
|
|
176
|
+
f"Absence records excluded: True",
|
|
177
|
+
f"OBIS quality flags applied: True (NO_COORD, ZERO_COORD, ON_LAND, DEPTH_EXCEEDS_BATH removed)",
|
|
178
|
+
f"Year range: {year_from} - {year_to}",
|
|
179
|
+
f"WKT geometry: {wkt_geometry or 'none (global)'}",
|
|
180
|
+
f"n_records: {n_records}",
|
|
181
|
+
f"Download date: {today}",
|
|
182
|
+
(f"Citation: OBIS ({year}) Ocean Biodiversity Information System. "
|
|
183
|
+
"Intergovernmental Oceanographic Commission of UNESCO. "
|
|
184
|
+
f"www.obis.org. Accessed on {today}."),
|
|
185
|
+
"License: CC0 1.0 (https://creativecommons.org/publicdomain/zero/1.0/)",
|
|
186
|
+
]
|
|
187
|
+
meta_path = output_dir / f"download_metadata_OBIS_{safe_name}.txt"
|
|
188
|
+
try:
|
|
189
|
+
meta_path.write_text("\n".join(lines), encoding="utf-8")
|
|
190
|
+
logger.info("Metadados gravados: %s", meta_path)
|
|
191
|
+
except OSError as e:
|
|
192
|
+
logger.error(
|
|
193
|
+
"Falha ao gravar metadados: %s\n Skill anterior: ecological-data-foundation", e,
|
|
194
|
+
)
|
|
195
|
+
raise
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
# ── Main download logic ────────────────────────────────────────────────────────
|
|
199
|
+
|
|
200
|
+
def download_species(species_name: str, output_dir: Path,
|
|
201
|
+
year_from: int, year_to: int,
|
|
202
|
+
wkt_geometry: str | None) -> None:
|
|
203
|
+
logger.info("--- Iniciando download OBIS: %s ---", species_name)
|
|
204
|
+
today_str = date.today().strftime("%Y%m%d")
|
|
205
|
+
safe_name = species_name.replace(" ", "_")
|
|
206
|
+
|
|
207
|
+
log_step(1, f"Buscar registros OBIS para '{species_name}'")
|
|
208
|
+
records = fetch_obis(species_name, year_from, year_to, wkt_geometry)
|
|
209
|
+
logger.info("Registros brutos recuperados: %d", len(records))
|
|
210
|
+
|
|
211
|
+
if not records:
|
|
212
|
+
logger.warning("Nenhum registro OBIS encontrado para '%s'.", species_name)
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
log_step(2, "Aplicar filtros de qualidade OBIS")
|
|
216
|
+
records = apply_quality_flags(records)
|
|
217
|
+
logger.info("Registros apos filtros OBIS: %d", len(records))
|
|
218
|
+
|
|
219
|
+
log_step(3, "Padronizar registros para schema de saida")
|
|
220
|
+
df = standardise_records(records, species_name)
|
|
221
|
+
n_final = len(df)
|
|
222
|
+
logger.info("Registros com coordenadas validas: %d", n_final)
|
|
223
|
+
|
|
224
|
+
if n_final < 30:
|
|
225
|
+
logger.warning(
|
|
226
|
+
"Registros insuficientes para analise confiavel (n = %d). Considere relaxar filtros.",
|
|
227
|
+
n_final,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
log_step(4, "Gravar CSV de ocorrencias")
|
|
231
|
+
csv_path = output_dir / f"occurrences_raw_OBIS_{safe_name}_{today_str}.csv"
|
|
232
|
+
try:
|
|
233
|
+
df.to_csv(csv_path, index=False)
|
|
234
|
+
logger.info("Gravado: %s (%d registros)", csv_path, n_final)
|
|
235
|
+
except OSError as e:
|
|
236
|
+
logger.error(
|
|
237
|
+
"Falha ao gravar CSV: %s\n Skill anterior: ecological-data-foundation", e,
|
|
238
|
+
)
|
|
239
|
+
raise
|
|
240
|
+
|
|
241
|
+
log_step(5, "Gravar metadados do download")
|
|
242
|
+
save_metadata(output_dir, species_name, n_final, year_from, year_to, wkt_geometry)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
# ── Entry point ────────────────────────────────────────────────────────────────
|
|
246
|
+
|
|
247
|
+
def main():
|
|
248
|
+
logger.info("Script: download_from_obis.py | Skill: %s", SKILL_NAME)
|
|
249
|
+
|
|
250
|
+
argv = sys.argv[1:]
|
|
251
|
+
|
|
252
|
+
if len(argv) < 2:
|
|
253
|
+
species_input = "Chelonia mydas"
|
|
254
|
+
output_dir = Path("output/obis")
|
|
255
|
+
year_from = 1950
|
|
256
|
+
year_to = date.today().year
|
|
257
|
+
wkt_geometry = None
|
|
258
|
+
logger.warning("Menos de 2 argumentos fornecidos. Usando valores padrao para teste.")
|
|
259
|
+
else:
|
|
260
|
+
species_input = argv[0]
|
|
261
|
+
output_dir = Path(argv[1])
|
|
262
|
+
year_from = int(argv[2]) if len(argv) >= 3 else 1950
|
|
263
|
+
year_to = int(argv[3]) if len(argv) >= 4 else date.today().year
|
|
264
|
+
wkt_geometry = argv[4] if len(argv) >= 5 and argv[4] else None
|
|
265
|
+
|
|
266
|
+
logger.info("Species input : %s", species_input)
|
|
267
|
+
logger.info("Output dir : %s", output_dir)
|
|
268
|
+
logger.info("Year range : %d - %d", year_from, year_to)
|
|
269
|
+
logger.info("WKT geometry : %s", wkt_geometry or "nenhum (global)")
|
|
270
|
+
|
|
271
|
+
log_decision("absence", "exclude",
|
|
272
|
+
"apenas registros de presenca confirmada")
|
|
273
|
+
log_decision("quality_flags", str(BAD_FLAGS),
|
|
274
|
+
"registros com flags de qualidade OBIS sao removidos")
|
|
275
|
+
|
|
276
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
277
|
+
|
|
278
|
+
# Build species list
|
|
279
|
+
log_step(0, "Construir lista de especies")
|
|
280
|
+
if species_input.endswith(".csv") and Path(species_input).exists():
|
|
281
|
+
try:
|
|
282
|
+
df_sp = pd.read_csv(species_input)
|
|
283
|
+
if "scientificName" not in df_sp.columns:
|
|
284
|
+
logger.error(
|
|
285
|
+
"Coluna 'scientificName' nao encontrada em: %s\n Skill anterior: ecological-data-foundation",
|
|
286
|
+
species_input,
|
|
287
|
+
)
|
|
288
|
+
sys.exit(1)
|
|
289
|
+
species_list = df_sp["scientificName"].dropna().unique().tolist()
|
|
290
|
+
logger.info("Modo batch: %d especies carregadas", len(species_list))
|
|
291
|
+
except Exception as e:
|
|
292
|
+
logger.error(
|
|
293
|
+
"Falha ao ler lista de especies: %s\n Skill anterior: ecological-data-foundation", e,
|
|
294
|
+
)
|
|
295
|
+
sys.exit(1)
|
|
296
|
+
else:
|
|
297
|
+
species_list = [species_input.strip()]
|
|
298
|
+
logger.info("Modo especie unica: %s", species_list[0])
|
|
299
|
+
|
|
300
|
+
for sp in species_list:
|
|
301
|
+
try:
|
|
302
|
+
download_species(sp, output_dir, year_from, year_to, wkt_geometry)
|
|
303
|
+
except FileNotFoundError as e:
|
|
304
|
+
logger.error(
|
|
305
|
+
"Arquivo de entrada nao encontrado ao processar '%s': %s\n Skill anterior: ecological-data-foundation",
|
|
306
|
+
sp, e,
|
|
307
|
+
)
|
|
308
|
+
except Exception as e:
|
|
309
|
+
logger.error(
|
|
310
|
+
"Falha ao baixar '%s' do OBIS: %s\n Causa provavel: problema de rede ou especie nao encontrada.\n Skill anterior: ecological-data-foundation",
|
|
311
|
+
sp, e,
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
logger.info("Todos os downloads OBIS concluidos. Verifique: %s", output_dir)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
if __name__ == "__main__":
|
|
318
|
+
main()
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ecological-impact-assessment
|
|
3
|
+
description: "Quantifies ecological impacts using BACI designs, landscape fragmentation metrics, and pressure indices. Use this skill when the user mentions BACI analysis, before-after-control-impact, impact assessment, disturbance effects, land use change impacts, fragmentation metrics, landscape metrics, pressure or threat indices, intervention effectiveness, management outcome evaluation, or control-impact comparisons."
|
|
4
|
+
skill_version: 1.0.0
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: ecological-impact-assessment
|
|
8
|
+
|
|
9
|
+
**Domain:** BACI · Before/After · Fragmentation · Pressure · Impact synthesis
|
|
10
|
+
**Phase:** 2 — Modeling
|
|
11
|
+
**Used by:** assess-ecological-impact, build-fire-risk-map, analyze-environmental-change
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Purpose
|
|
16
|
+
|
|
17
|
+
Guides the agent through the design and analysis of ecological impact assessments: BACI frameworks, before/after comparisons, landscape fragmentation analysis, anthropogenic pressure quantification, and synthesis of impact magnitude.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## When to Invoke
|
|
22
|
+
|
|
23
|
+
- Quantifying the effect of a disturbance, land-use change, or management intervention
|
|
24
|
+
- Designing a BACI study or interpreting BACI data
|
|
25
|
+
- Computing landscape fragmentation or connectivity metrics
|
|
26
|
+
- Building a composite pressure or threat index
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Inputs
|
|
31
|
+
|
|
32
|
+
| Input | Format | Required |
|
|
33
|
+
|-------|--------|----------|
|
|
34
|
+
| Ecological indicator time series (control and impact sites) | CSV | Yes |
|
|
35
|
+
| Disturbance event date(s) | Text / date | Yes |
|
|
36
|
+
| Land cover maps (pre/post) | GeoTIFF | Conditional |
|
|
37
|
+
| Pressure / threat layers | GeoTIFF, SHP | Optional |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Outputs
|
|
42
|
+
|
|
43
|
+
| Output | Description |
|
|
44
|
+
|--------|-------------|
|
|
45
|
+
| `baci_results.csv` | BACI interaction term with CI and p-value |
|
|
46
|
+
| `fragmentation_metrics.csv` | Patch area, perimeter, connectivity indices |
|
|
47
|
+
| `pressure_index.tif` | Composite pressure map |
|
|
48
|
+
| `impact_synthesis.md` | Narrative summary of impact magnitude |
|
|
49
|
+
| `impact_plots/` | Before/after and control/impact comparison plots |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Steps
|
|
54
|
+
|
|
55
|
+
### 1. Define the Impact Hypothesis
|
|
56
|
+
- Identify the disturbance and its expected effect (direction, magnitude, timing)
|
|
57
|
+
- Define the ecological indicator(s) to measure
|
|
58
|
+
- Define control sites (unaffected by disturbance, similar baseline conditions)
|
|
59
|
+
|
|
60
|
+
### 2. BACI Design Assessment
|
|
61
|
+
- Verify: measurements Before AND After disturbance, at Control AND Impact sites
|
|
62
|
+
- Compute the BACI interaction: (After−Before)_Impact − (After−Before)_Control
|
|
63
|
+
- If multiple control or impact sites: use mixed model with site as random effect
|
|
64
|
+
- Test for parallel trends in the pre-disturbance period (assumption check)
|
|
65
|
+
|
|
66
|
+
### 3. Statistical Analysis
|
|
67
|
+
- Linear model: `indicator ~ period * treatment + (1|site)`
|
|
68
|
+
- The interaction term (`period:treatment`) is the BACI effect
|
|
69
|
+
- Report coefficient, 95% CI, and p-value for the interaction
|
|
70
|
+
- Effect size: Cohen's d or partial η²
|
|
71
|
+
|
|
72
|
+
### 4. Landscape Fragmentation (if applicable)
|
|
73
|
+
- Compute patch-level metrics: area, perimeter/area ratio, shape index
|
|
74
|
+
- Compute landscape-level metrics: largest patch index, edge density, COHESION, MESH
|
|
75
|
+
- Compare pre/post land cover using transition matrix
|
|
76
|
+
- Report habitat loss and fragmentation separately
|
|
77
|
+
|
|
78
|
+
### 5. Pressure Index (if applicable)
|
|
79
|
+
- Identify pressure layers: road density, agricultural intensity, human footprint, fire frequency
|
|
80
|
+
- Normalise each layer (0–1)
|
|
81
|
+
- Compute weighted sum (document weights and rationale)
|
|
82
|
+
- Classify pressure zones (low / moderate / high / critical)
|
|
83
|
+
|
|
84
|
+
### 6. Synthesis
|
|
85
|
+
- Combine BACI results, fragmentation metrics, and pressure index
|
|
86
|
+
- Classify overall impact as: none / minor / moderate / major / critical
|
|
87
|
+
- Describe spatial distribution of impact
|
|
88
|
+
- Identify most impacted areas and most important drivers
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Key Decisions to Document
|
|
93
|
+
|
|
94
|
+
- Control site selection criteria
|
|
95
|
+
- BACI model specification (fixed vs. random effects)
|
|
96
|
+
- Fragmentation metrics chosen and software version
|
|
97
|
+
- Pressure layer sources and weighting scheme
|
|
98
|
+
- Impact classification thresholds
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Tools and Libraries
|
|
103
|
+
|
|
104
|
+
**R:** `lme4`, `emmeans`, `landscapemetrics`, `lsm`, `ggplot2`
|
|
105
|
+
**Python:** `pylandstats`, `statsmodels`, `rasterio`
|
|
106
|
+
**CLI:** FRAGSTATS (landscape metrics)
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Resources
|
|
111
|
+
|
|
112
|
+
- `resources/baci-design-guide.md` — BACI design requirements and common pitfalls
|
|
113
|
+
- `resources/fragmentation-metrics-reference.md` — FRAGSTATS metric definitions
|
|
114
|
+
- `resources/pressure-index-template.md` — standard pressure layer sources
|
|
115
|
+
- `examples/impact/` — worked BACI and fragmentation example
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Notes
|
|
120
|
+
|
|
121
|
+
- BACI requires pre-disturbance data; post-hoc designs without pre-disturbance are not BACI
|
|
122
|
+
- Multiple control sites are strongly preferred; single control site severely limits inference
|
|
123
|
+
- Landscape metrics are scale-dependent; always report the spatial resolution used
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Example Invocation Prompts — ecological-impact-assessment
|
|
2
|
+
|
|
3
|
+
## BACI Analysis
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
Load skill: ecological-impact-assessment
|
|
7
|
+
Task: BACI analysis of deforestation impact on stream macroinvertebrate richness.
|
|
8
|
+
|
|
9
|
+
Data: data/baci_invertebrates.csv
|
|
10
|
+
Columns: site_id, treatment (control/impact), period (before/after), richness (count), date
|
|
11
|
+
|
|
12
|
+
Model: richness ~ period * treatment + (1|site), family = negative binomial
|
|
13
|
+
Check parallel trends using pre-disturbance data only.
|
|
14
|
+
Report: BACI interaction estimate, 95% CI, p-value, % change on original scale.
|
|
15
|
+
Generate: before/after comparison plots for control and impact sites.
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Fragmentation Analysis
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Load skill: ecological-impact-assessment
|
|
22
|
+
Task: Compute landscape fragmentation metrics for Atlantic Forest before (2000) and after (2022) period.
|
|
23
|
+
|
|
24
|
+
Input layers:
|
|
25
|
+
- data/landcover_2000.tif (MapBiomas class 3 = Atlantic Forest)
|
|
26
|
+
- data/landcover_2022.tif (same classification)
|
|
27
|
+
- data/spatial/study_area.shp
|
|
28
|
+
|
|
29
|
+
Metrics: total area (ha), number of patches, mean patch size, largest patch index,
|
|
30
|
+
edge density, COHESION, MESH (effective mesh size).
|
|
31
|
+
Report change between years. Output: fragmentation_metrics.csv, fragmentation_plots.png
|
|
32
|
+
```
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# BACI Design Requirements and Common Pitfalls
|
|
2
|
+
|
|
3
|
+
## Core Requirements
|
|
4
|
+
|
|
5
|
+
BACI (Before-After-Control-Impact) is the gold standard for ecological impact assessment.
|
|
6
|
+
|
|
7
|
+
### Minimum design:
|
|
8
|
+
- **Before:** ≥ 1 sampling period before the disturbance
|
|
9
|
+
- **After:** ≥ 1 sampling period after the disturbance
|
|
10
|
+
- **Control:** ≥ 1 site not affected by the disturbance, similar to impact site
|
|
11
|
+
- **Impact:** ≥ 1 site affected by the disturbance
|
|
12
|
+
|
|
13
|
+
### Recommended design (for inference):
|
|
14
|
+
- Multiple control sites (≥ 3 preferred)
|
|
15
|
+
- Multiple impact sites (≥ 3 preferred)
|
|
16
|
+
- ≥ 3 time points before and after
|
|
17
|
+
- Sites monitored for the same duration before and after
|
|
18
|
+
|
|
19
|
+
## Statistical Model
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
indicator ~ period * treatment + (1 | site)
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- `period`: factor with levels `before` / `after`
|
|
26
|
+
- `treatment`: factor with levels `control` / `impact`
|
|
27
|
+
- `period:treatment`: **the BACI interaction — the effect of interest**
|
|
28
|
+
- `(1 | site)`: random intercept for repeated measures at sites
|
|
29
|
+
|
|
30
|
+
The BACI effect = difference in before-after change between impact and control sites.
|
|
31
|
+
|
|
32
|
+
## Assumptions
|
|
33
|
+
|
|
34
|
+
1. **Parallel trends:** Control and impact sites had similar trajectories *before* the disturbance. **Test this** by checking the significance of `time × treatment` interaction in the pre-disturbance period only.
|
|
35
|
+
2. **No spillover:** The disturbance does not affect control sites (e.g., no upstream pollution affecting downstream controls).
|
|
36
|
+
3. **Independence among sites** (partially relaxed by random effects).
|
|
37
|
+
|
|
38
|
+
## Common Pitfalls
|
|
39
|
+
|
|
40
|
+
| Pitfall | Consequence | Fix |
|
|
41
|
+
|---------|------------|-----|
|
|
42
|
+
| Only one control site | Cannot distinguish site effect from treatment effect | Use ≥ 3 control sites |
|
|
43
|
+
| No pre-disturbance data | Cannot implement BACI; only Before-After | Document limitation explicitly |
|
|
44
|
+
| Control site affected by disturbance | BACI estimate biased toward zero | Verify spatial separation |
|
|
45
|
+
| Very short pre-disturbance baseline | Parallel trend assumption unverifiable | Report as limitation |
|
|
46
|
+
| Pseudoreplication (subsamples as sites) | Inflated degrees of freedom, false significance | Use random effects or aggregate |
|
|
47
|
+
| No random effects for repeated sites | Underestimated standard errors | Always include `(1|site)` |
|
|
48
|
+
|
|
49
|
+
## Effect Size Reporting
|
|
50
|
+
|
|
51
|
+
For the BACI interaction (β_BACI):
|
|
52
|
+
|
|
53
|
+
- **Linear scale (Gaussian):** Report β, 95% CI, and Cohen's d
|
|
54
|
+
- **Log scale (Poisson/lognormal):** Back-transform: exp(β) = multiplicative factor
|
|
55
|
+
- **Example:** β_BACI = -0.48 on log scale → impact sites had exp(−0.48) = 0.62× the abundance of control sites after disturbance, i.e. a 38% reduction
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Landscape Fragmentation Metrics Reference
|
|
2
|
+
|
|
3
|
+
Computed with `landscapemetrics` (R) or FRAGSTATS. Always report the spatial resolution used.
|
|
4
|
+
|
|
5
|
+
## Patch-Level Metrics
|
|
6
|
+
|
|
7
|
+
| Metric | Abbreviation | Unit | Description |
|
|
8
|
+
|--------|-------------|------|-------------|
|
|
9
|
+
| Patch area | AREA | ha | Area of each patch |
|
|
10
|
+
| Perimeter | PERIM | m | Total perimeter of patch |
|
|
11
|
+
| Perimeter-area ratio | PARA | m/ha | Shape complexity; higher = more irregular |
|
|
12
|
+
| Shape index | SHAPE | — | PERIM / (4√AREA); 1 = square; higher = complex |
|
|
13
|
+
| Fractal dimension index | FRAC | — | 1–2; higher = more complex shape |
|
|
14
|
+
| Core area | CORE | ha | Area > edge distance from patch edge |
|
|
15
|
+
| Core area index | CAI | % | % of patch that is core area |
|
|
16
|
+
| Proximity index | PROX | — | Area/distance to nearest same-class patch |
|
|
17
|
+
|
|
18
|
+
## Class-Level Metrics
|
|
19
|
+
|
|
20
|
+
| Metric | Abbreviation | Unit | Description |
|
|
21
|
+
|--------|-------------|------|-------------|
|
|
22
|
+
| Total class area | CA | ha | Sum of all patch areas |
|
|
23
|
+
| Number of patches | NP | count | Count of patches |
|
|
24
|
+
| Patch density | PD | n/100 ha | NP per 100 ha landscape |
|
|
25
|
+
| Largest patch index | LPI | % | % of landscape in largest patch |
|
|
26
|
+
| Mean patch area | AREA_MN | ha | Average patch size |
|
|
27
|
+
| Mean patch shape index | SHAPE_MN | — | Average patch shape complexity |
|
|
28
|
+
| Edge density | ED | m/ha | Total edge length per ha |
|
|
29
|
+
| Mean nearest-neighbour distance | ENN_MN | m | Isolation between patches |
|
|
30
|
+
| Aggregation index | AI | % | 0–100; 100 = fully aggregated |
|
|
31
|
+
| Splitting index | SPLIT | — | Fragmentation; 1 = unfragmented |
|
|
32
|
+
| Effective mesh size | MESH | ha | Probability two random points in same patch × landscape area |
|
|
33
|
+
| Landscape shape index | LSI | — | Total edge / min edge for same total area |
|
|
34
|
+
|
|
35
|
+
## Connectivity Metrics
|
|
36
|
+
|
|
37
|
+
| Metric | Description | R package |
|
|
38
|
+
|--------|-------------|----------|
|
|
39
|
+
| COHESION | Patch cohesion index; physical connectedness | landscapemetrics |
|
|
40
|
+
| CONNECT | Functional connectivity based on distance threshold | landscapemetrics |
|
|
41
|
+
| IIC | Integral Index of Connectivity (graph-based) | conefor / igraph |
|
|
42
|
+
| PC | Probability of Connectivity | conefor |
|
|
43
|
+
| dPC, dIIC | Importance of each patch to overall connectivity | conefor |
|
|
44
|
+
|
|
45
|
+
## Key Metrics for Impact Assessment
|
|
46
|
+
|
|
47
|
+
Recommended minimum set for before/after comparison:
|
|
48
|
+
|
|
49
|
+
1. **CA** — total habitat area (has it declined?)
|
|
50
|
+
2. **NP** — number of patches (has fragmentation increased?)
|
|
51
|
+
3. **LPI** — largest patch index (is the main mass intact?)
|
|
52
|
+
4. **ED** — edge density (are edge effects increasing?)
|
|
53
|
+
5. **MESH** — effective mesh size (functional fragmentation — most comprehensive single metric)
|
|
54
|
+
6. **COHESION** — physical connectivity of patches
|
|
55
|
+
|
|
56
|
+
## R Code Template
|
|
57
|
+
|
|
58
|
+
```r
|
|
59
|
+
library(terra)
|
|
60
|
+
library(landscapemetrics)
|
|
61
|
+
|
|
62
|
+
lc <- rast("landcover.tif")
|
|
63
|
+
habitat_class <- 3 # class code for the habitat of interest
|
|
64
|
+
|
|
65
|
+
# Patch-level
|
|
66
|
+
patch_metrics <- calculate_lsm(lc, what = c("lsm_p_area", "lsm_p_shape", "lsm_p_core"),
|
|
67
|
+
class = habitat_class)
|
|
68
|
+
|
|
69
|
+
# Class-level
|
|
70
|
+
class_metrics <- calculate_lsm(lc, what = c("lsm_c_ca", "lsm_c_np", "lsm_c_lpi",
|
|
71
|
+
"lsm_c_ed", "lsm_c_mesh", "lsm_c_cohesion"),
|
|
72
|
+
class = habitat_class)
|
|
73
|
+
print(class_metrics)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Interpreting MESH (Effective Mesh Size)
|
|
77
|
+
|
|
78
|
+
MESH (m_eff) = Σ(Ai²) / A_total
|
|
79
|
+
|
|
80
|
+
where Ai = area of patch i, A_total = total landscape area.
|
|
81
|
+
|
|
82
|
+
- **High MESH** = landscape dominated by large, connected patches → low functional fragmentation
|
|
83
|
+
- **Low MESH** = landscape cut into many small patches → high fragmentation
|
|
84
|
+
- MESH is the **most recommended single metric** for summarising fragmentation because it is sensitive to both patch size and patch number and is interpretable in area units (ha).
|
|
85
|
+
|
|
86
|
+
Example: MESH drops from 5,000 ha to 800 ha → a person starting at a random point is now 6× more likely to be confined to a smaller patch.
|