The Sentinel-2 constellation is one of the better gifts the European Space Agency has given the software industry: free, regular, multispectral imagery of the whole land surface, with a repeat cycle measured in days rather than months.
It is also a good place to learn earth observation because the data is messy in ordinary, understandable ways. Clouds happen. Tiles overlap. Different processing levels mean different things. Once you can work through that, most other imagery is a variation on the theme.
What you actually get
Sentinel-2 carries a multispectral instrument with thirteen bands. The ones I use most often:
- Band 2 (blue), Band 3 (green), Band 4 (red): the visible channels, useful for true-colour composites.
- Band 8 (near infrared): the workhorse for vegetation.
- Band 11 and Band 12 (short-wave infrared): useful for moisture, burn scars and geology.
- Band 1 (coastal aerosol) and Band 9 (water vapour): atmospheric correction helpers.
Resolution varies by band. The visible and NIR bands are 10 metres; the red-edge and SWIR bands are 20 metres; the atmospheric bands are 60 metres. If you stack them naively you will end up with a grid at the coarsest resolution and a lot of resampling decisions to explain later.
Start with an index, not a model
The simplest useful transformation is the Normalised Difference Vegetation Index:
NDVI = (NIR - Red) / (NIR + Red)
Healthy vegetation reflects strongly in the near infrared and absorbs red light for photosynthesis, so the ratio separates green biomass from soil, water and roofs. Values cluster between -1 and 1; dense vegetation tends to sit above 0.6.
Other indices are variations on the same idea:
- NDWI for water, using green and NIR.
- NDMI for moisture, using NIR and SWIR.
- NDRE for red-edge, useful when a canopy is dense and NDVI saturates.
An index is not a truth claim. It is a compressed observation, sensitive to atmosphere, viewing geometry and the particular crop. Treat it as a feature, not a verdict.
A small pipeline
A deliberately small example with rasterio and numpy:
import numpy as np
import rasterio
with rasterio.open("red.tif") as red_ds, rasterio.open("nir.tif") as nir_ds:
red = red_ds.read(1).astype("float32")
nir = nir_ds.read(1).astype("float32")
profile = red_ds.profile
denominator = nir + red
ndvi = np.divide(
nir - red,
denominator,
out=np.zeros_like(nir),
where=denominator != 0,
)
profile.update(dtype="float32", count=1, nodata=np.nan)
with rasterio.open("ndvi.tif", "w", **profile) as dst:
dst.write(ndvi, 1)
In production I would add a cloud mask, a valid-data mask, and a check that the two scenes share a grid. The order matters: mask before you compute, or you will spend an afternoon wondering why the field average is being dragged down by a cumulus cloud.
Clouds are the real curriculum
A scene can be technically valid and completely useless. Sentinel-2 scenes ship with a scene classification layer that labels cloud, shadow, water, snow and so on. Use it. For time series, prefer median compositing over a short window rather than picking the single clearest date; it smooths over missed observations and reduces the chance that one bad pixel becomes a trend.
When I build a pipeline now, I try to keep three layers separate:
- Acquisition: finding scenes and their metadata.
- Preparation: masking, resampling, compositing.
- Interpretation: indices, models, thresholds.
Keeping them separate makes it possible to re-run the interpretation layer when the science changes, without re-downloading the planet.
Where the value lives
The imagery is the easy part. The useful work is usually in the join: a satellite observation, a field boundary, a planting date and a human decision. That join is where data quality, coordinate systems and product design all meet.
If you are starting out, pick one field you can visit. Download a season of imagery over it. Compute NDVI. Then go and look at the field and see whether the number matches what you see. That afternoon will teach you more than a month of tutorials — and it is a lot more fun.