Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sdarionicolas-boop/AgroIA-RAG/llms.txt

Use this file to discover all available pages before exploring further.

The AgroIA Score is a composite index from 0 to 100 that condenses six years of Sentinel-2 NDVI history, thermal stress hours from NASA POWER, and a statistical anomaly detector into a single, actionable number per agricultural field. Rather than forcing agronomists to interpret raw satellite bands or climate tables, the Score surfaces the signal that matters most at each crop’s critical growth month — making the difference between a field in excellent health and one at critical risk immediately legible.

Formula

Score (0–100) = Vigor (40%) + Stability (30%) + Cleanliness (20%) + Climate (10%)
Each component is independently clamped to its maximum weight, so no single dimension can mask a collapse in another.

Score components

Vigor — 40 points

Vigor measures the current photosynthetic activity of the field by normalising the NDVI reading captured at the crop’s critical month against a reference ceiling of 0.9:
vigor = np.clip(ndvi_critico / 0.9, 0.0, 1.0) * 40.0
A field with NDVI = 0.72 yields (0.72 / 0.9) × 40 = 32 pts. NDVI is sourced from COPERNICUS/S2_SR_HARMONIZED Band 8 (NIR) and Band 4 (Red), filtered for scenes with less than 20% cloud cover. If the critical month is cloud-free, a fallback window of ±2 months is applied by get_gee_ndvi_ventana().

Stability — 30 points

Stability penalises inter-annual inconsistency. It uses the coefficient of variation (CV = σ/μ) of the NDVI series, normalised against a reference CV of 0.45:
if len(ndvi_historico) >= 3:
    arr = np.array(ndvi_historico)
    cv  = np.std(arr) / np.mean(arr) if np.mean(arr) > 0 else 1.0
    estabilidad = np.clip(1.0 - cv / 0.45, 0.0, 1.0) * 30.0
else:
    estabilidad = 15.0  # neutral fallback
A minimum of 3 years of historical NDVI data is required to compute Stability. With fewer years, the component defaults to 15 pts (half weight) to avoid over-penalising new lotes.

Cleanliness — 20 points

Cleanliness detects anomalous seasons using Isolation Forest with contamination=0.2. Each year classified as an outlier (label == -1) reduces the component proportionally:
if len(ndvi_historico) >= 4:
    iso = IsolationForest(contamination=0.2, random_state=42)
    labels = iso.fit_predict(np.array(ndvi_historico).reshape(-1, 1))
    limpieza = np.clip(1.0 - (labels == -1).sum() / len(labels) / 0.40, 0.0, 1.0) * 20.0
else:
    limpieza = 10.0  # neutral fallback
A minimum of 4 years of data is required to fit the Isolation Forest. With fewer years, the component defaults to 10 pts (half weight).

Climate — 10 points

Climate measures thermal stress using heat hours accumulated during the campaign, sourced from NASA POWER’s daily temperature data via a sinusoidal approximation. Fewer heat hours above the crop-specific threshold produce a higher score:
clima = np.clip(1.0 - horas_calor / umbral_clima, 0.0, 1.0) * 10.0
When horas_calor equals or exceeds umbral_clima, the climate component drops to 0.

Full function signature

The following is the complete calcular_score() function from src/pipeline/agro_math.py:
def calcular_score(ndvi_critico, horas_calor, ndvi_historico, umbral_clima=40):
    """Calcula el Score AgroIA (0-100)."""
    vigor = np.clip(ndvi_critico / 0.9, 0.0, 1.0) * 40.0
    if len(ndvi_historico) >= 3:
        arr = np.array(ndvi_historico)
        cv  = np.std(arr) / np.mean(arr) if np.mean(arr) > 0 else 1.0
        estabilidad = np.clip(1.0 - cv / 0.45, 0.0, 1.0) * 30.0
    else: estabilidad = 15.0

    if len(ndvi_historico) >= 4:
        iso = IsolationForest(contamination=0.2, random_state=42)
        labels = iso.fit_predict(np.array(ndvi_historico).reshape(-1, 1))
        limpieza = np.clip(1.0 - (labels == -1).sum() / len(labels) / 0.40, 0.0, 1.0) * 20.0
    else: limpieza = 10.0

    clima = np.clip(1.0 - horas_calor / umbral_clima, 0.0, 1.0) * 10.0
    total = int(round(vigor + estabilidad + limpieza + clima))

    return {"total": total, "vigor": round(vigor, 1), "estabilidad": round(estabilidad, 1),
            "limpieza": round(limpieza, 1), "clima": round(clima, 1)}

Crop configuration

The CONFIG dictionary in agro_math.py defines per-crop agronomic parameters calibrated against INTA and University of Nebraska research. The following table lists the three supported crops:
CropCritical monthNDVI minNDVI maxHeat threshold (h)Source
maizJanuary0.250.9240INTA Marcos Juárez / Univ. Nebraska
sojaFebruary0.250.9035INTA Marcos Juárez
trigoOctober0.200.8830INTA Pergamino
ndvi_min and ndvi_max define the plausible range for NDVI validation via validar_ndvi(). Readings outside these bounds are flagged as suspicious before being passed to calcular_score().
The umbral_clima value for each crop matches agronomic thresholds for cumulative heat stress during the vegetative-to-reproductive transition. Maize tolerates more heat hours than wheat because its C4 photosynthetic pathway is more heat-tolerant.

Score interpretation

RangeCategoryRecommended action
90–100ExcellentNo intervention required
75–89GoodRoutine monitoring
60–74ModerateReview input applications; monitor closely
40–59LowConduct field inspection; consider agronomic adjustments
0–39CriticalImmediate field diagnosis required
A Critical score (0–39) does not necessarily indicate crop failure. It signals that one or more components — particularly Vigor or Stability — are significantly below expected thresholds and warrant direct agronomic evaluation.

Frequently asked questions

The pipeline first attempts to retrieve NDVI from the crop’s exact critical month. If no cloud-free Sentinel-2 scenes are available, get_gee_ndvi_ventana() searches within a ±2-month window. If no valid image is found within the window, that year is excluded from the historical series and flagged in the pipeline log. It does not count toward the Stability or Cleanliness calculations.
NDVI rarely reaches 1.0 in real agricultural fields due to soil background signal and mixed pixels at field edges. Using 0.9 as the normalisation ceiling ensures that peak-performing crops score near 40 pts rather than being systematically under-rewarded. This value is calibrated to the NDVI max values observed across the three supported crops.
Not directly. The calcular_score() function accepts a configurable umbral_clima parameter, so you can call it manually with custom values. However, the pipeline’s run_full_analysis() entry point resolves crop config from the CONFIG dict. Adding a new crop requires a new entry in agro_math.py:CONFIG with calibrated NDVI ranges, critical month, and heat threshold.

Pipeline guide

Run the full analysis pipeline on a shapefile or batch GeoJSON.

SAM field delineation

How AgroIA converts GPS points into precise GeoJSON polygons.

RAG engine

Query score history and components in natural language.

AgroMath API module

API reference for calcular_score() and related functions.

Build docs developers (and LLMs) love