Remote-Sensing Phenology

NOVA Invasives

Detecting kudzu, phragmites, and other invasive species in Northern Virginia by exploiting their phenological signatures in Sentinel-2 time series — all processed on Google Earth Engine.

Study Area

5 Monitoring Sites

Click each marker to see the invasive species, GEE coordinate code, and detection signature. The dashed green box is the bounding box for the full study area.

Loading map…
Kudzu hotspotEnglish Ivy hotspotPhragmites wetlandNative forest (control)

Why It Works

How can a satellite see invasive plants?

The key is phenological asynchrony: invasive and native plants leaf out and senesce on different schedules, and that timing gap is clearly visible in satellite time series.

What is NDVI?

NDVI = (NIR − RED) / (NIR + RED)

Plant leaves absorb red light for photosynthesis and strongly reflect near-infrared. NDVI normalizes that ratio to [-1, +1] — higher values mean denser vegetation. Sentinel-2 uses B8 (NIR) and B4 (Red).

Water< 0
Bare soil0–0.2
Sparse vegetation0.2–0.5
Dense vegetation> 0.5

Detection Windows

Early Spring (Mar–Apr) · Late Autumn (Oct–Nov)

Invasive shrubs leaf out 2–3 weeks earlier and drop leaves 2–3 weeks later than native deciduous trees. During those windows, invasives show much higher NDVI than surrounding native vegetation — a detectable signal.

Early spring windowMar–Apr
Summer peakJun–Aug
Late autumn windowOct–Nov
Winter (evergreen anomaly)Dec–Feb
01_ndvi_concept.png
NDVI concept

Simulation

Phenology Curve Comparison

Synthetic data confirming the core hypothesis: the orange (early spring) and brown (late autumn) bands mark the windows where invasive vs. native NDVI differs most.

02_phenology_comparison.png
Phenology comparison

Kudzu

Pueraria montana

Anomalously high summer NDVI (>0.8)

Year-round

Autumn Olive / Bush Honeysuckle

Elaeagnus / Lonicera

Leafs out 2-3 weeks earlier than natives

Mar–Apr

Phragmites

Phragmites australis

Persistently high NIR reflectance

Late summer

English Ivy

Hedera helix

High winter NDVI (evergreen)

Dec–Feb

Real Data

NDVI Time Series — 5 Sites (Sentinel-2 measurements)

Real data from Google Earth Engine: 360 monthly observations across 2020–2025. Switch to 'Monthly phenology' for multi-year averages, or 'Full time series' for raw monthly values. Toggle sites via the legend.

Loading NDVI data…

Source: COPERNICUS/S2_SR_HARMONIZED · 500 m buffer · monthly median composite · cloud cover < 30%

Classification

Automated Phenological Classification

A rule-based classifier (no training data needed) evaluates four seasonal NDVI features per site. Rules encode ecological priors: early leaf-out, late senescence, winter evergreen, summer explosion — any one exceeding the native baseline flags the site as invasive.

Dyke Marsh — Phragmites

Suspected invasive (single signal)
  • Summer + +0.10 above control (relative summer surge)

Spring

0.401

Summer

0.660

Autumn

0.505

Winter

0.308

Great Falls — Kudzu

Native vegetation
  • All metrics close to the native control

Spring

0.274

Summer

0.575

Autumn

0.425

Winter

0.206

Huntley Meadows — Phragmites

Deciduous invasive shrub (Autumn Olive / Honeysuckle-type)
  • Summer + +0.29 above control (relative summer surge)
  • Early-spring + +0.18 above control (early leaf-out)
  • Late-autumn + +0.17 above control (delayed senescence)

Spring

0.553

Summer

0.845

Autumn

0.668

Winter

0.378

Native Forest (Control)

Native vegetation
  • All metrics close to the native control

Spring

0.372

Summer

0.558

Autumn

0.498

Winter

0.305

Scotts Run — Ivy

Broad mild invasive (composite signal)
  • All seasons mildly elevated (ΣΔ=+0.25, 4/4 seasons > control)

Spring

0.465

Summer

0.610

Autumn

0.535

Winter

0.374

08_failure_modes.png — v1 misses traced to 3 distinct mechanisms
Three failure modes diagnosed
05_phenology_signatures.png
Seasonal NDVI signatures
06_classification.png — v1.1 results
Classification summary (v1.1)

⚠️ Three failure modes diagnosed (v1 → v1.1)

v1 (initial rules) detected only 1 of 5 sites (Huntley Meadows). Diagnosing the 4 misses revealed three distinct failure mechanisms: (1) Scotts Run — threshold too strict: all 4 seasons +0.05–0.10 above control but no single feature breaks the wall; (2) Dyke Marsh — rule gap: summer +0.10 vs. control but the old rule only checked absolute value; (3) Great Falls — sampling error: the chosen coordinate sits on bare rock / river surface, not on the kudzu patch (shrinking the buffer to 100 m made it worse, confirming this). v1.1 added composite and summer-vs-control rules, lifting detection from 1/5 to 3/5. Great Falls needs a new coordinate — no rule change can fix a wrong sampling point.

Project Scripts

Analysis Pipeline

Four Python scripts form a complete analysis pipeline, one stage each.

01

Understand NDVI

01_explore_ndvi_concept.py

Plots the NDVI definition and simulated phenology curves for invasive vs. native plants.

Why this way: Before pulling real satellite data, make sure we know what we're measuring. No GEE account required.

Output: 01_ndvi_concept.png · 02_phenology_comparison.png · 03_study_area_map.html

02

Extract real NDVI via GEE

02_gee_ndvi_timeseries.py

Connects to Google Earth Engine and pulls monthly NDVI time series (2020–2025) for 5 sites from Sentinel-2.

Why this way: Sentinel-2 overpasses every 5 days — hundreds of scenes over 6 years. GEE parallelizes this in the cloud; nothing is downloaded locally.

Output: ndvi_timeseries_nova.csv · 04_gee_ndvi_timeseries.png

03

Download Landsat via STAC

03_download_landsat_stac.py

Pulls raw Landsat 8/9 bands via the USGS STAC API and computes seasonal NDVI maps locally.

Why this way: STAC needs no GEE account — useful fallback. Landsat 30 m is suited for regional-scale analysis.

Output: data/*.tif · outputs/ndvi_*.png

04

Phenological classification

04_phenology_analysis.py

Uses early-spring / late-autumn NDVI deltas as features in a rule-based classifier; exports JSON for the dashboard.

Why this way: This is the project goal: turn time-series signals into a yes/no invasive flag per location.

Output: phenology_features.csv · classification.json · 05/06_*.png

GEE core workflow (02_gee_ndvi_timeseries.py)
# 1. Load the Sentinel-2 dataset (lazy — nothing is downloaded yet)
collection = (
    ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
    .filterBounds(roi)              # (1) keep only scenes over NOVA
    .filterDate("2022-03-01", "2022-04-30")  # (2) early-spring window
    .filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 30))  # (3) drop cloudy scenes
)

# 2. Compute NDVI on every image (runs in parallel on GEE servers)
def add_ndvi(image):
    return image.addBands(
        image.normalizedDifference(["B8", "B4"]).rename("NDVI")
    )

# 3. Composite + extract a single value (this is where the actual call happens)
ndvi_median = collection.map(add_ndvi).select("NDVI").median()
stats = ndvi_median.reduceRegion(
    reducer=ee.Reducer.mean(),
    geometry=roi,    # 500 m buffer
    scale=10,        # Sentinel-2 native resolution
).getInfo()          # ← only this line triggers actual GEE computation

Technology

Sentinel-2 SRLandsat 8/9NDVI Time SeriesPhenology DetectionGEE Python API

Sources: Copernicus Sentinel-2 · USGS Landsat · EDDMapS · iNaturalist

Study area: Northern Virginia (38.70°N–39.00°N, 77.00°W–77.55°W)