Install
openclaw skills install @ruiduobao/phenology-metricsExtract phenological metrics from NDVI/EVI time series data. Computes SOS, EOS, LOS, peak value/date, amplitude, and integral using threshold, derivative, or double logistic fitting methods.
openclaw skills install @ruiduobao/phenology-metricsExtract vegetation phenology metrics from NDVI/EVI time series. Supports threshold, derivative, and double logistic fitting methods.
| Metric | Description |
|---|---|
| SOS | Start of Season (green-up) |
| EOS | End of Season (senescence) |
| LOS | Length of Season (EOS - SOS) |
| Peak | Maximum NDVI/EVI value |
| Peak Date | Date of peak value |
| Amplitude | Peak - baseline |
| Integral | Area under curve (season productivity) |
python scripts\phenology-metrics.py extract \
--input ndvi_series.csv \
--date-col date --value-col ndvi \
--method threshold --threshold-ratio 0.5 \
--output phenology.json
python scripts\phenology-metrics.py fit \
--input ndvi_series.csv \
--date-col date --value-col ndvi \
--output fitted.json --plot-data plot.csv
python scripts\phenology-metrics.py plot-data \
--input ndvi_series.csv --date-col date --value-col ndvi \
--output curve.csv
pip install numpy>=1.21.0 scipy>=1.7.0 pandas>=1.3.0
# Or: pip install -r scripts/requirements.txt
--input: Input CSV file (date, value columns)--date-col: Date column name (default: date)--value-col: Value column name (default: ndvi)--method: Extraction method (threshold, derivative, logistic)--threshold-ratio: Threshold ratio for SOS/EOS (default: 0.5)--output: Output file path (CSV or JSON)--plot-data: Output fitted curve data for plotting--json: Output as JSONnumpy>=1.21.0
scipy>=1.7.0
pandas>=1.3.0
Use a multi-band GeoTIFF stack (one band per time step) as input:
python scripts\phenology-metrics.py extract \
--input ndvi_stack.tif \
--input-format geotiff \
--date-file dates.txt \
--method logistic \
--output phenology.json
--input-format geotiff: Specifies GeoTIFF stack input--date-file: Text file with one date per line (YYYY-MM-DD), one per band--band-epoch: Reference date for band numbering (default: band 1 = first date)Supported date formats:
| Format | Example | Notes |
|---|---|---|
| ISO date | 2023-06-15 | Recommended (YYYY-MM-DD) |
| US date | 06/15/2023 | MM/DD/YYYY |
| DOY | 166 | Day of year (1-366) |
| Year-DOY | 2023-166 | Combined format |
Specify with --date-format parameter.
| Method | Best For | Pros / Cons |
|---|---|---|
threshold | Quick estimation, sparse data | Simple but less precise |
derivative | Precise timing, smooth data | Sensitive to noise |
logistic | Smooth curves, research-grade | Best fit but needs clean data |
Recommendation: Use threshold for exploratory analysis; logistic for publication-quality results.
Time series gaps are common (clouds, sensor issues). The tool handles them via:
--max-gap)python scripts\phenology-metrics.py extract \
--input ndvi_series.csv --method logistic \
--max-gap 30 --sg-window 7 \
--output phenology.json
For multi-year time series:
--year-range 2020-2023 to loop over each year--year 2022 for single-year analysispython scripts\phenology-metrics.py extract \
--input ndvi_series.csv --method logistic \
--year-range 2020-2023 \
--output phenology_multiyear.csv
Process multiple pixels from a GeoTIFF stack:
python scripts\phenology-metrics.py batch \
--input ndvi_stack.tif \
--input-format geotiff \
--method logistic \
--output-dir ./phenology_results/ \
--max-workers 4
Outputs one JSON per pixel (row, col) in the output directory.
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('fitted.csv')
plt.plot(df['date'], df['original'], 'o', label='Original')
plt.plot(df['date'], df['fitted'], '-', label='Fitted')
plt.axvline(x=sos_date, color='green', label='SOS')
plt.axvline(x=eos_date, color='red', label='EOS')
plt.legend()
plt.show()
@software{phenology_metrics,
author = {ruiduobao},
title = {Phenology Metrics Extraction Tool},
url = {https://github.com/ruiduobao/phenology-metrics},
version = {0.1.0},
year = {2024},
}
| Error | Cause | Solution |
|---|---|---|
ConnectionError | Network issue | Check internet, retry |
HTTP 429 | Rate limit | Wait 60s, retry |
ValueError | Invalid input | Check parameter format |
| Empty output | No valid data | Check input data quality |
ModuleNotFoundError | Missing dep | Run pip install |
| Poor fit (low R²) | Noisy data | Try logistic or increase --sg-window |
| Missing SOS/EOS | Weak seasonality | Lower threshold ratio or check data range |
Local processing of NDVI/EVI time series data.
# Process a multi-band GeoTIFF stack (one pixel = one time series)
python scripts\phenology-metrics.py extract --input ndvi_stack.csv --method logistic --output phenology_results.csv
# .github/workflows/phenology-update.yml
name: Phenology Update
on:
schedule:
- cron: '0 0 1 3 *' # Every March (growing season start)
jobs:
extract:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install numpy scipy pandas
- run: |
python scripts\phenology-metrics.py extract \
--input data/ndvi_2024.csv \
--method logistic --output data/phenology_2024.csv
python scripts\phenology-metrics.py extract --input ndvi_stack.csv --method logistic --output phenology.csv
psql -d gis_db -c "\COPY phenology(pixel_id, sos, eos, los, peak_date, amplitude) FROM 'phenology.csv' CSV HEADER"
--method threshold for fastest results (least accurate)--method logistic is recommended for most vegetation types--sg-window 7 controls Savitzky-Golay smoothing window (larger = smoother)--max-gap 30 fills gaps up to 30 days; increase for cloudy regions从 NDVI/EVI 时间序列数据中提取植被物候指标。支持阈值法、导数法和双 Logistic 拟合。
| 指标 | 说明 |
|---|---|
| SOS | 生长季开始(返青) |
| EOS | 生长季结束(衰老) |
| LOS | 生长季长度(EOS - SOS) |
| Peak | 最大 NDVI/EVI 值 |
| Peak Date | 峰值日期 |
| Amplitude | 峰值 - 基线 |
| Integral | 积分 |
python scripts\phenology-metrics.py extract \
--input ndvi_series.csv \
--date-col date --value-col ndvi \
--method threshold --threshold-ratio 0.5 \
--output phenology.json
python scripts\phenology-metrics.py fit \
--input ndvi_series.csv \
--date-col date --value-col ndvi \
--output fitted.json --plot-data plot.csv
python scripts\phenology-metrics.py plot-data \
--input ndvi_series.csv --date-col date --value-col ndvi \
--output curve.csv
pip install numpy>=1.21.0 scipy>=1.7.0 pandas>=1.3.0
# 或: pip install -r scripts/requirements.txt
--input: 输入 CSV 文件(日期, 值列)--date-col: 日期列名(默认: date)--value-col: 值列名(默认: ndvi)--method: 提取方法(threshold, derivative, logistic)--threshold-ratio: SOS/EOS 阈值比率(默认: 0.5)--output: 输出文件路径--plot-data: 输出拟合曲线数据--json: 以 JSON 输出numpy>=1.21.0
scipy>=1.7.0
pandas>=1.3.0
使用多波段 GeoTIFF 堆栈(每个时间步一个波段)作为输入:
python scripts\phenology-metrics.py extract \
--input ndvi_stack.tif \
--input-format geotiff \
--date-file dates.txt \
--method logistic \
--output phenology.json
--input-format geotiff:指定 GeoTIFF 堆栈输入--date-file:每行一个日期的文本文件(YYYY-MM-DD),每个波段对应一个日期--band-epoch:波段编号参考日期(默认:波段 1 = 第一个日期)支持的日期格式:
| 格式 | 示例 | 说明 |
|---|---|---|
| ISO 日期 | 2023-06-15 | 推荐(YYYY-MM-DD) |
| 美国格式 | 06/15/2023 | MM/DD/YYYY |
| DOY | 166 | 年积日(1-366) |
| 年-DOY | 2023-166 | 组合格式 |
使用 --date-format 参数指定。
| 方法 | 适用场景 | 优缺点 |
|---|---|---|
threshold | 快速估算、稀疏数据 | 简单但不够精确 |
derivative | 精确时间、平滑数据 | 对噪声敏感 |
logistic | 平滑曲线、科研级 | 拟合最佳但需要干净数据 |
建议:探索性分析用 threshold;发表级结果用 logistic。
时间序列常有缺口(云、传感器问题)。工具通过以下方式处理:
--max-gap 配置)python scripts\phenology-metrics.py extract \
--input ndvi_series.csv --method logistic \
--max-gap 30 --sg-window 7 \
--output phenology.json
对于多年时间序列:
--year-range 2020-2023 循环每年--year 2022 进行单年分析python scripts\phenology-metrics.py extract \
--input ndvi_series.csv --method logistic \
--year-range 2020-2023 \
--output phenology_multiyear.csv
处理 GeoTIFF 堆栈中的多个像元:
python scripts\phenology-metrics.py batch \
--input ndvi_stack.tif \
--input-format geotiff \
--method logistic \
--output-dir ./phenology_results/ \
--max-workers 4
输出每个像元(行, 列)一个 JSON 文件。
@software{phenology_metrics,
author = {ruiduobao},
title = {Phenology Metrics Extraction Tool},
url = {https://github.com/ruiduobao/phenology-metrics},
version = {0.1.0},
year = {2024},
}
| 错误 | 原因 | 解决方案 |
|---|---|---|
ConnectionError | 网络问题 | 检查网络,重试 |
HTTP 429 | 速率限制 | 等待 60 秒后重试 |
ValueError | 无效输入 | 检查参数格式 |
| 无输出 | 无有效数据 | 检查输入数据质量 |
ModuleNotFoundError | 缺少依赖 | 运行 pip install |
| 拟合差(低 R²) | 数据噪声大 | 尝试 logistic 或增大 --sg-window |
| 缺少 SOS/EOS | 季节性弱 | 降低阈值比率或检查数据范围 |
本地处理 NDVI/EVI 时间序列数据。