Planetary-scale answers, unlocked.
A Hands-On Guide for Working with Large-Scale Spatial Data. Learn more.
Authors
NDVI, the normalized difference vegetation index, measures how much healthy green vegetation a pixel contains, calculated from the red and near-infrared light it reflects. Values run from −1 to +1. Dense, healthy vegetation scores high, bare ground scores near zero, and water scores below zero. NDVI is computed from Sentinel-2, Landsat, Moderate Resolution Imaging Spectroradiometer (MODIS), drone, and aircraft imagery. This article tests one claim. A single NDVI date cannot identify a crop. NDVI supports a field decision only once it is summarized per field across a season.
The Havasu catalog holds a Sentinel-2 scene index of L2A (Level-2A, corrected for the atmosphere) imagery, seasonal mosaics at 10 m, and Fields of the World field boundaries.
The NDVI formula divides the difference between near-infrared (NIR) and red reflectance by their sum:
NDVI = (NIR − Red) / (NIR + Red)
Chlorophyll absorbs red light for photosynthesis. The cell structure of leaves reflects near-infrared light strongly. NASA's Earth Observatory explains NDVI through this contrast. Dividing by the sum keeps the index between −1 and +1 at any scene brightness.
Each sensor records red and near-infrared in different numbered bands:
The U.S. Geological Survey (USGS) publishes the Landsat band formulas. The European Space Agency (ESA) documents the Sentinel-2 bands. Sentinel-2 also records a narrow NIR band, B8A at 20 m, which some workflows use in place of B8.
NDVI values describe land cover along a gradient from water to dense forest. NASA's Earth Observatory gives these ranges, and notes that values of 0.8 to 0.9 indicate the highest possible density of green leaves:
A healthy wheat field at peak growth can score as high as a forest, and the same field reads near zero after harvest. Sensor, season, and processing level shift the numbers. NDVI values compare best against the same place at another time, or against neighbors imaged on the same date.
Each related index uses different bands or adds terms for a specific weakness:
Alfredo Huete introduced the soil-adjusted vegetation index in 1988. Liu and Huete corrected for atmosphere and canopy background together in a feedback-based modification of the NDVI (1995), the precursor of EVI. Huete and colleagues documented EVI as the MODIS companion to NDVI in 2002. The USGS publishes the Landsat versions of EVI and SAVI. The normalized difference water index (NDWI) here is McFeeters' water index (1996). A second index with the same name, Gao's NDWI from the same year, uses NIR and shortwave infrared to measure leaf water content. The USGS calls its Landsat version the normalized difference moisture index (NDMI). Hyperspectral imagery supports hundreds of narrow-band indices beyond these five.
Saturation over dense canopy. Once leaves cover the ground, red reflectance drops close to zero. Further leaf growth then barely changes the ratio. NDVI flattens near its upper range over forests and mature crops. The red and near-infrared plot above shows where saturation starts in the Haskell County scene. Pixels with NDVI above 0.8 cluster at a median red reflectance of 0.030 against a near-infrared of 0.421. EVI and NDRE stay sensitive there. Gitelson, Kaufman, and Merzlyak showed that a green band in place of red stays sensitive to chlorophyll (1996). Gitelson's Wide Dynamic Range Vegetation Index (2004) weights NIR by 0.1 to 0.2. It is at least three times more sensitive than NDVI at leaf area indices from 2 to 6 (square meters of leaf per square meter of ground). Mutanga and Skidmore showed that narrow-band indices overcome the saturation problem in biomass estimation (2004).
Soil background. In sparse vegetation, bright, wet, and dark soils shift NDVI by different amounts at the same plant cover. SAVI's soil factor corrects for this.
Atmosphere. Haze, smoke, and thin cloud scatter red light more than NIR and lower NDVI. Surface reflectance correction reduces the effect. EVI's blue term corrects for haze particles that remain.
To calculate NDVI from satellite imagery:
Sentinel-2 revisits each location every five days at the equator with two satellites (Drusch and colleagues, 2012). Landsat extends the record back to 1972. Both are open Earth observation data.
Stacking every cloud-free observation of a pixel through a year traces a curve. NDVI rises at green-up, peaks at full canopy, and declines as leaves age and dry (senescence) or the crop is harvested. Analysts read the start, peak, and end of season from that curve. The difference from the same date in prior years is an NDVI anomaly. They fill cloud gaps with composites such as NASA's 16-day, 250 m MODIS vegetation index product (MOD13Q1). Where clouds persist for weeks, synthetic aperture radar images through them. NDVI differencing between two dates is one of the simplest forms of change detection.
Zonal statistics return the mean, standard deviation, or count of the NDVI pixels inside each polygon, such as a field boundary. A field whose mean NDVI falls below its neighbors, or below its own prior-year curve, is a candidate for crop stress. A high standard deviation within a field points to uneven growth from drainage, soil, or irrigation problems.
Parcel data ties NDVI to a legal unit of land and its owner. Field boundary datasets follow the planted area, which a parcel can split or merge. The choice of polygon changes the result. A polygon drawn by a model can hold two management units, parts of a field that are farmed differently. Mixed 10 m pixels along a road or a neighbor's crop also shift the mean. Wherobots used this pattern for mean AlphaEarth embeddings over about 3.5 million Iowa fields across 2023 to 2025, and NDVI works the same way.
The example below uses one cloud-free Sentinel-2 scene of Haskell County, Kansas, from 28 July 2024 (S2A_14SLG_20240728, 0.55% cloud). It joins that scene to the 2024 Fields of the World boundaries for a 12 km by 11 km block of center-pivot and dryland farms west of Sublette.
wherobots_open_data.sentinel2.l2a_source_items
wherobots_open_data.rasterflow_output_samples.fields_of_the_world_vector_global
WherobotsDB reads the raster files and the polygon table in one SQL query: raster and vector in one engine; most stacks pick one. RS_MapAlgebra applies the formula and the cloud mask in one pass. RS_ZonalStats returns the mean, standard deviation, and count of clear NDVI pixels inside each polygon. The scene index and the STAC reader find scenes by area, date, and cloud cover. Each band is an out-db raster: the table stores a link, and WherobotsDB reads the pixels from the COG. RasterFlow ran the Fields of the World model that produced the boundaries. The model is based on the Fields of The World benchmark by Kerner and colleagues (AAAI 2025).
-- Mean Sentinel-2 NDVI per Fields of the World polygon, Haskell County, Kansas, 28 July 2024 WITH scene AS ( SELECT assets.red.href AS red_href, assets.nir.href AS nir_href, assets.scl.href AS scl_href FROM wherobots_open_data.sentinel2.l2a_source_items WHERE year = 2024 AND month = 7 AND mgrs_tile = '14SLG' AND id = 'S2A_14SLG_20240728_0_L2A' ), block AS ( -- The 12 km by 11 km block, in the scene's UTM zone 14N SELECT ST_Transform(ST_SetSRID(ST_PolygonFromEnvelope(-100.98, 37.48, -100.86, 37.58), 4326), 'EPSG:4326', 'EPSG:32614') AS box ), ndvi AS ( -- One NDVI raster for the whole block. Stack red (B04), NIR (B08) and the 20 m scene -- classification layer resampled to 10 m; keep SCL 4 (vegetation) and 5 (not vegetated) SELECT RS_MapAlgebra( RS_AddBand(RS_AddBand(red, nir, 1, 2), RS_ReprojectMatch(scl, red, 'NearestNeighbor'), 1, 3), 'D', 'out = (rast[0] > 0 && rast[1] > 0 && (rast[2] == 4 || rast[2] == 5)) ? (rast[1] - rast[0]) / (rast[1] + rast[0]) : -9999.0;', -9999.0) AS rast FROM ( -- RS_Clip(raster, band, geometry, allTouched, noDataValue, crop): reads only the COG tiles in the block SELECT RS_Clip(RS_FromPath(s.red_href), 1, k.box, false, 0, true) AS red, RS_Clip(RS_FromPath(s.nir_href), 1, k.box, false, 0, true) AS nir, RS_Clip(RS_FromPath(s.scl_href), 1, k.box, false, 0, true) AS scl FROM scene s CROSS JOIN block k ) ), fields AS ( SELECT ST_AreaSpheroid(geometry) AS area_m2, ST_Transform(ST_SetSRID(geometry, 4326), 'EPSG:4326', 'EPSG:32614') AS geom FROM wherobots_open_data.rasterflow_output_samples.fields_of_the_world_vector_global WHERE bbox.xmin > -100.98 AND bbox.xmax < -100.86 AND bbox.ymin > 37.48 AND bbox.ymax < 37.58 AND time < TIMESTAMP '2024-06-01 00:00:00' -- the 2024 boundary vintage AND ST_AreaSpheroid(geometry) >= 20000 -- fields of 2 ha or more ) -- Send the single NDVI raster to every field partition, then summarize each field SELECT /*+ BROADCAST(n) */ ROUND(f.area_m2) AS area_m2, RS_ZonalStats(n.rast, f.geom, 1, 'mean') AS mean_ndvi, RS_ZonalStats(n.rast, f.geom, 1, 'stddev') AS sd_ndvi, RS_ZonalStats(n.rast, f.geom, 1, 'count') AS clear_pixels FROM fields f CROSS JOIN ndvi n;
The map algebra runs once over about 1.3 million pixels. The broadcast hint copies that raster to every worker that holds part of the field table. Raster processing at scale: the out-of-database architecture behind WherobotsDB explains why zonal statistics read only the pixels inside each zone.
The query ran in about three minutes. On 28 July, mean NDVI per field ranged from 0.13 to 0.89, with a median of 0.44. 24 fields averaged above 0.7, most of them summer crops at full canopy. 30 averaged below 0.3, including harvested wheat and fallow ground. The median within-field standard deviation was 0.07. Four fields had a standard deviation above 0.3, a spread that points to green canopy and bare ground inside one polygon.
The season query runs on all 15 cloud-free Sentinel-2 L2A scenes of tile 14SLG in 2024, with the same SCL mask and formula. For each field it takes the highest April or May mean (the spring peak) and the highest July or August mean (the summer peak). A field is spring peak when spring exceeds 0.6 and summer stays under 0.45. It is summer peak when summer exceeds 0.7 and spring stays under 0.45. It is low until November when both stay under 0.35. Other fields are mixed. The thresholds are set for this block. It returns the median NDVI per pattern and date and ran in under four minutes.
-- Mean NDVI per field on 15 clear Sentinel-2 dates in 2024, grouped by seasonal pattern WITH scenes AS ( -- REPARTITION spreads the 15 scenes over 15 tasks, so each date's reads, map algebra and -- zonal statistics run in parallel SELECT /*+ REPARTITION(15) */ id, to_date(datetime) AS scene_date, assets.red.href AS red_href, assets.nir.href AS nir_href, assets.scl.href AS scl_href FROM wherobots_open_data.sentinel2.l2a_source_items WHERE year = 2024 AND mgrs_tile = '14SLG' AND id IN ('S2A_14SLG_20240130_0_L2A','S2B_14SLG_20240224_0_L2A','S2B_14SLG_20240322_0_L2A', 'S2B_14SLG_20240414_0_L2A','S2B_14SLG_20240514_0_L2A','S2A_14SLG_20240605_0_L2A', 'S2A_14SLG_20240628_0_L2A','S2B_14SLG_20240713_0_L2A','S2A_14SLG_20240728_0_L2A', 'S2A_14SLG_20240817_0_L2A','S2A_14SLG_20240903_0_L2A','S2A_14SLG_20240926_0_L2A', 'S2A_14SLG_20241016_0_L2A','S2B_14SLG_20241110_0_L2A','S2B_14SLG_20241207_0_L2A') ), block AS ( SELECT ST_Transform(ST_SetSRID(ST_PolygonFromEnvelope(-100.98, 37.48, -100.86, 37.58), 4326), 'EPSG:4326', 'EPSG:32614') AS box ), ndvi AS ( -- One NDVI raster per date for the block SELECT s.scene_date, RS_MapAlgebra( RS_AddBand(RS_AddBand(r, n, 1, 2), RS_ReprojectMatch(l, r, 'NearestNeighbor'), 1, 3), 'D', 'out = (rast[0] > 0 && rast[1] > 0 && (rast[2] == 4 || rast[2] == 5)) ? (rast[1] - rast[0]) / (rast[1] + rast[0]) : -9999.0;', -9999.0) AS rast FROM (SELECT /*+ BROADCAST(k) */ s.scene_date, RS_Clip(RS_FromPath(s.red_href), 1, k.box, false, 0, true) AS r, RS_Clip(RS_FromPath(s.nir_href), 1, k.box, false, 0, true) AS n, RS_Clip(RS_FromPath(s.scl_href), 1, k.box, false, 0, true) AS l FROM scenes s CROSS JOIN block k) s ), fields AS ( -- The table has no ID column, so a 10-character geohash of each field's centroid serves as one SELECT ST_GeoHash(ST_Centroid(geometry), 10) AS field_id, ST_Transform(ST_SetSRID(geometry, 4326), 'EPSG:4326', 'EPSG:32614') AS geom FROM wherobots_open_data.rasterflow_output_samples.fields_of_the_world_vector_global WHERE bbox.xmin > -100.98 AND bbox.xmax < -100.86 AND bbox.ymin > 37.48 AND bbox.ymax < 37.58 AND time < TIMESTAMP '2024-06-01 00:00:00' AND ST_AreaSpheroid(geometry) >= 20000 ), per_field AS ( -- One row per field and date. The 102 field polygons are small, so broadcast them SELECT /*+ BROADCAST(f) */ f.field_id, n.scene_date, RS_ZonalStats(n.rast, f.geom, 1, 'mean') AS mean_ndvi, RS_ZonalStats(n.rast, f.geom, 1, 'count') AS clear_pixels FROM ndvi n CROSS JOIN fields f ), peaks AS ( -- Each field's highest mean NDVI in spring (April, May) and summer (July, August) SELECT *, MAX(CASE WHEN month(scene_date) IN (4, 5) THEN mean_ndvi END) OVER (PARTITION BY field_id) AS spring_peak, MAX(CASE WHEN month(scene_date) IN (7, 8) THEN mean_ndvi END) OVER (PARTITION BY field_id) AS summer_peak FROM per_field ), patterns AS ( SELECT *, CASE WHEN spring_peak > 0.6 AND summer_peak < 0.45 THEN 'spring peak' WHEN summer_peak > 0.7 AND spring_peak < 0.45 THEN 'summer peak' WHEN spring_peak < 0.35 AND summer_peak < 0.35 THEN 'low until November' ELSE 'mixed' END AS pattern FROM peaks ) -- One row per date and pattern, plus an all-fields row per date (pattern is NULL). -- SELECT * FROM per_field instead returns each field's own curve. SELECT scene_date, pattern, COUNT(*) AS fields, ROUND(AVG(mean_ndvi), 3) AS avg_ndvi, ROUND(percentile(mean_ndvi, 0.5), 3) AS median_ndvi, SUM(clear_pixels) AS clear_px FROM patterns GROUP BY GROUPING SETS ((scene_date, pattern), (scene_date)) ORDER BY scene_date, pattern;
All 102 fields have a value on every date. The average field mean was 0.255 on 30 January, rose to 0.384 on 14 May, and peaked at 0.479 on 13 July before falling to 0.266 by 16 October. The 28 July row reproduces the single-date result, a median field mean of 0.44.
The pattern rows split the averages into spring-peak, summer-peak, and low-until-November fields:
The other 55 fields show double peaks, partial-field patterns, or values that fall between the thresholds. Mixed curves like these can mean a field polygon holds more than one management unit.
To cover a county or a state, the query repeats per Sentinel-2 tile. It builds one NDVI raster per tile and date and joins it to the fields it intersects. An agent connected through the Wherobots Model Context Protocol (MCP) Server can write it from a plain-language request such as "mean July NDVI for every field near Sublette". Pairing models trained on text, documents, databases, and the internet with imagery, field boundaries, and SQL is geospatial AI applied to agriculture.
The idea that leaves change the ratio of red to near-infrared light predates satellites. Carl Jordan measured light on the forest floor. He showed that the ratio of 800 nm to 675 nm light tracks leaf area, in Derivation of leaf-area index from quality of light on the forest floor (Ecology, 1969).
John Rouse, Robert Haas, John Schell, and Donald Deering at Texas A&M turned it into a normalized difference for ERTS-1, the Earth Resources Technology Satellite later renamed Landsat 1. They reported a band ratio correlated with green biomass in Monitoring vegetation systems in the Great Plains with ERTS (NASA SP-351, 1974). Compton Tucker at NASA Goddard compared combinations of red and photographic infrared bands in Red and photographic infrared linear combinations for monitoring vegetation (Remote Sensing of Environment, 1979). The normalized difference was among them. Piers Sellers then gave the index a physical basis: his radiative transfer model in Canopy reflectance, photosynthesis and transpiration (1985) linked visible and near-infrared canopy reflectance to photosynthesis and transpiration.
The NOAA AVHRR weather satellites made the index global. Pinzon and Tucker's non-stationary 1981 to 2012 AVHRR NDVI3g time series (2014) extends that calibrated record across three decades. Pettorelli and colleagues reviewed how ecologists use it in Using the satellite-derived NDVI to assess ecological responses to environmental change (2005).
The research that put NDVI into routine use treats each pixel or field as a time series. Zhang, Friedl, and colleagues fit S-shaped (logistic) curves to MODIS vegetation index series to monitor vegetation phenology (2003). They read green-up, maturity, senescence, and dormancy from each curve. Jönsson and Eklundh's TIMESAT (2004) became a standard tool for extracting season dates from noisy series with cloud gaps.
Wardlow, Egbert, and Kastens showed that time series of MODIS 250 m vegetation indices separate crop types in the U.S. Central Great Plains (2007). The Haskell County season tests the same principle at 10 m. On 28 July, 24 fields have a mean NDVI above 0.7, and 18 of them follow the summer-peak curve.
Adding one spring date separates the groups. Crop mapping from the timing of green-up and harvest (phenology) uses this principle.
A long field record mixes sensors. A calibration difference between sensors can look like real change in a 40-year record. Roy and colleagues characterized the Landsat 7 to Landsat 8 NDVI difference (2016). Claverie and colleagues built the Harmonized Landsat and Sentinel-2 surface reflectance data set (2018) so the two missions can feed one time series.
A season of NDVI for every field in a state is billions of pixels joined to millions of polygons. Gorelick and colleagues showed planetary-scale raster analysis in Google Earth Engine (2017). Jia Yu, Jinxuan Wu, and Mohamed Sarwat introduced distributed spatial processing on Apache Spark with GeoSpark (ACM SIGSPATIAL 2015). Yu, Zhang, and Sarwat described its partitioning, indexing, and spatial join design in Spatial data management in Apache Spark: the GeoSpark perspective and beyond (GeoInformatica, 2019). GeoSpark became Apache Sedona. Its raster functions include the map algebra and zonal statistics used here, and they run the per-field summaries in parallel. Kanchan Chowdhury and Sarwat extended the stack to deep learning on raster and spatiotemporal data with GeoTorch (SIGSPATIAL 2022).
Compute NDVI from Sentinel-2 and summarize it per field in SQL with a Wherobots free trial at cloud.wherobots.com.
NDVI stands for normalized difference vegetation index. It is the difference between near-infrared and red reflectance divided by their sum. That ratio measures how much green vegetation a pixel contains. Researchers introduced it in the early 1970s to monitor vegetation in the Great Plains with Landsat 1.
NDVI measures the amount and vigor of green vegetation in a pixel. Chlorophyll absorbs red light, and the inner structure of leaves reflects near-infrared light. Dense, healthy vegetation therefore shows a large gap between the two and a high NDVI. Bare soil, rock, and water produce a small gap or a negative value.
A high NDVI value, from about 0.6 to 0.9, means dense, healthy green vegetation such as a full crop canopy or a temperate or tropical forest. Values around 0.2 to 0.3 indicate shrubs and grassland. Values of 0.1 and below indicate bare rock, sand, or snow. Negative values indicate water.
NDVI is used to monitor crop health and estimate yield. It tracks drought through NDVI anomalies, the gap between a date’s NDVI and its usual value for that date. It maps when plants green up and when they age and dry (phenology), and it is used to assess wildfire fuel and measure urban green space. Agronomists, insurers, land managers, and climate scientists all use NDVI time series from satellites such as Sentinel-2, Landsat, and MODIS.
EVI performs better than NDVI over dense canopy, where NDVI stops rising (saturates). EVI also corrects for soil background and, with the blue band, for haze and other atmospheric effects. NDVI is simpler, needs only red and near-infrared, and has a longer, more consistent record. Many analyses compute both.
NDVI can be calculated from free Sentinel-2 and Landsat surface reflectance imagery, available from the Copernicus Data Space Ecosystem, USGS EarthExplorer, AWS Open Data, and Microsoft Planetary Computer. Ready-made NDVI products include USGS Landsat spectral indices, NASA MODIS 16-day composites, and Copernicus Land Monitoring Service NDVI.
NDVI is calculated as (NIR − Red) / (NIR + Red) on surface reflectance values. For Sentinel-2 that is (B8 − B4) / (B8 + B4), and for Landsat 8 and 9 it is (B5 − B4) / (B5 + B4). Mask clouds, shadows, and water first. Then apply the formula pixel by pixel.
In agriculture, NDVI is summarized per field and tracked through the season. Agronomists compare each field’s NDVI curve with its neighbors and its own prior years. They flag low-NDVI zones inside a field for scouting, irrigation checks, or variable-rate fertilizer. Mid-season NDVI is also an input to yield models. The curve shape separates crops planted on different calendars. Near Sublette, Kansas, spring-peak fields reached a median NDVI of 0.82 in April and summer-peak fields 0.77 in late July.
A well-known example is the AVHRR NDVI record that showed increased plant growth between 45°N and 70°N from 1981 to 1991 (Myneni and colleagues, Nature, 1997). A farm-scale example: on 28 July 2024, 24 of 102 fields near Sublette, Kansas, averaged NDVI above 0.7 while 30 averaged below 0.3.
NDVI saturates because red reflectance falls close to zero once a canopy closes. Further leaf growth raises near-infrared reflectance a little and leaves the ratio almost unchanged. NDVI flattens around 0.8 to 0.9 over dense forest and mature crops. EVI, NDRE, and the Wide Dynamic Range Vegetation Index keep responding at high leaf area.
What is a Geofence? How Geofencing Works, With Examples
A geofence is a virtual boundary that triggers an action when a device enters or leaves it. How geofencing works, its uses and limits, and a test on ship data.
What is Map Matching? GPS Traces, Methods and Examples
Map matching places each GPS record on the road a vehicle drove. How it works, nearest-road snapping vs hidden Markov models, and a test on Ann Arbor drives.
Geospatial Big Data: Why Analytics Is Hard to Scale
Teams struggle to scale geospatial analytics for five reasons: data volume, mixed formats, many coordinate systems, spatial joins and pipeline upkeep.
share this article
Awesome that you’d like to share our articles. Where would you like to share it to: