CoursesGIS Basics — A Complete Introduction14.6 Lab — NDVI in QGIS
Module 14: Remote SensingHands-on Lab

14.6 Lab — NDVI in QGIS

Compute NDVI from a Sentinel-2 scene, visualise it, and detect vegetation change.

Lesson 73 of 100·45 min read

Key takeaways

  • You can compute NDVI in QGIS without writing code — a good first remote-sensing workflow.
  • Pre- / post-event comparison reveals change quickly.
  • The same recipe extends to NDWI, NBR, NDBI with different band pairs.

Introduction

This lab produces an NDVI map for a real Sentinel-2 scene and compares two dates to detect vegetation change. Free tools, public data, ~40 minutes.

Prerequisites

Step 1 — Choose a study area

Pick an agricultural or forested area you have some familiarity with — a farming region, a forest park, or a recent wildfire zone. The visible contrast between pre- and post-change helps.

Step 2 — Download two Sentinel-2 scenes

From the Copernicus Browser:

  1. Search your area of interest.
  2. Product type: L2A (surface reflectance).
  3. Cloud cover < 20 %.
  4. Pick a date before the event (pre-event).
  5. Pick a date after (post-event).

Download B04 (Red) and B08 (NIR) GeoTIFFs from both dates. You should have 4 files.

Step 3 — Load in QGIS

Layer → Add Layer → Add Raster Layer and add all four files. Style each with a default single-band grey renderer.

Step 4 — Compute NDVI (pre)

Raster → Raster Calculator.

Expression:

Code
("B08_pre@1" - "B04_pre@1") / ("B08_pre@1" + "B04_pre@1")
  • Output layer: ndvi_pre.tif.
  • Output extent: match one of the input bands.
  • Resolution: match input.

Click OK. You'll get a single-band raster with values roughly in [-1, 1].

Repeat for post-event as ndvi_post.tif.

Step 5 — Style the NDVI raster

  • Right-click ndvi_preProperties → Symbology.
  • Render type: Singleband pseudocolor.
  • Colour ramp: RdYlGn (red = low, green = high).
  • Min: 0, Max: 0.8 (clip outside this range for visual contrast).
  • Click OK.

You should now see a striking vegetation map — greens where vegetation is healthy, reds / yellows where bare or sparse.

Step 6 — Compute dNDVI (change)

Raster Calculator:

Code
"ndvi_post@1" - "ndvi_pre@1"

Output: dndvi.tif.

Style with a diverging ramp (RdBu, red = negative / loss, blue = positive / gain):

  • Min: −0.5, Max: 0.5.
  • Centre the ramp at 0.

Step 7 — Interpret

Areas where:

  • dNDVI strongly negative → vegetation loss (deforestation, fire, harvest).
  • dNDVI near zero → no change.
  • dNDVI strongly positive → vegetation gain (regrowth, spring leaf-out).

Zoom in to a clear loss patch. Does it correspond to a known event? Roads, clearings, or burn scars?

Step 8 — Print a comparison map

Use the QGIS layout composer to produce a 2-panel figure: pre-NDVI, post-NDVI, with a shared colour ramp. Add title, legend, scale bar, source, date. Export as PDF.

Step 9 — Optional: mask clouds

Sentinel-2 L2A products include a SCL band flagging clouds, shadows, and other quality issues. Load SCL and use the Raster Calculator to set NDVI to nodata where SCL indicates cloud (values 3, 8, 9, 10). Otherwise clouds appear as artificially low NDVI.

Step 10 — Reflection

Record in notes:

  • Which patches of change surprise you?
  • What's the biggest interpretive caveat (clouds, seasonality, atmospheric variability)?
  • Would comparing scenes from different seasons work the same way? Why or why not?

Troubleshooting

  • NDVI output is all -1 or bizarre values — check band order and that you cast both bands to float. Raster Calculator outputs float when inputs are float.
  • Bands don't align — bands B04 and B08 are both 10 m native resolution, but if they came from different products (some bands at 20 m or 60 m), resample first with gdalwarp.
  • Bright stripes — could be sensor artefacts; try a different acquisition date.
  • Nodata included in math — masks needed before calculation; check for pixel value 0 which is often Sentinel-2 nodata.

Self-check exercises

1. Why use the L2A product rather than L1C for NDVI calculation?

L2A is surface reflectance — atmospheric effects removed. L1C is top-of-atmosphere reflectance, which includes haze and aerosols that can shift NDVI by 0.05–0.10 between scenes under different atmospheric conditions. For quantitative comparison across dates, L2A is much more reliable. Use L1C only if L2A isn't available or you're doing your own atmospheric correction.

2. Your dNDVI map shows change in a cloud-shadow area. Is the change real?

Probably not. Cloud shadows depress NDVI dramatically; a scene where shadows fall differently between dates produces apparent NDVI change that's really just shadow artefacts. Always mask clouds and shadows using the SCL or an ML cloud mask before computing differences. Suspicious change patches should be cross-checked against cloud shadow locations.

3. How would you adapt this lab to compute NDWI instead of NDVI?

Use bands B03 (Green) and B08 (NIR) instead: (B03 - B08) / (B03 + B08). Value ranges shift — water has high NDWI (0.3+), land has low or negative. Same styling approach works, with a blue colour ramp for emphasis. For vegetation water content (Gao NDWI), use B08 and B11 (SWIR).

Summary

  • Pre-/post-event NDVI reveals vegetation change quickly.
  • QGIS Raster Calculator handles band math without coding.
  • Always mask clouds / shadows; use L2A surface reflectance for quantitative work.
  • Same recipe extends to any spectral index by changing bands.

Further reading

  • ESA Sentinel-2 L2A Products User Guide.
  • QGIS Documentation — Raster Calculator.
  • USGS NDVI tutorials.
  • Google Earth Engine NDVI time-series tutorials.
Module test

Module 14: Remote Sensing

Answer these quick multiple-choice questions to check your understanding before moving on.

1. Passive sensors measure what?
2. NDVI is commonly used to estimate what?
3. Why does spectral resolution matter?