Skip to content
← Back to blog
Remote SensingOctober 26, 2026

A Year of Sentinel-2 NDVI: What a Time Series Shows About a Punjab Farm Town

Twelve monthly NDVI images of a 10 km square around Jagraon, Punjab: how to build the series, how to read the curve of a field and a town, and what the curves cannot tell you.

remote-sensingremote sensing

Introduction

One satellite image is a photograph of one day. Fields, towns and orchards can look alike on that day and behave very differently over the year. A series of images of the same place, lined up in date order, shows the behaviour.

This post follows a 10 km square around Jagraon, a town in Punjab, India, through 2023. Punjab is a good first place to try this, because its fields change colour twice a year and the pattern is easy to see. At the end I link two step-by-step tutorials that build the same series in QGIS and in Python.

What is a raster time series?

A raster is a grid of cells, and each cell holds one number. A raster time series is a stack of rasters of the same place, in date order, on the same grid. Position in the stack is time.

The number in each cell here is NDVI, the normalised difference vegetation index, (NIR - red) / (NIR + red), where NIR is near infrared light. Green leaves reflect a lot of near infrared and absorb red, so healthy crops give values near 0.8 or 0.9. Bare soil gives about 0.1 to 0.2, and roofs and roads stay low. I explain how to calculate it from a Sentinel-2 scene in NDVI from Sentinel-2: a step-by-step satellite imagery workflow.

Why it matters

Take two places with the same NDVI on one date. One might be a wheat field that is about to be harvested, and the other an orchard that is green all year. A single image cannot tell them apart. Their curves through the year can.

The same idea sits under crop monitoring, drought watching and the mapping of how land changes. All of them compare a place with its own earlier self, and that needs the time axis.

How the series was built

The images come from Sentinel-2, a set of European satellites that pass over a place about every five days at 10 m resolution. I read Level-2A scenes, which are corrected to surface reflectance, from the open Earth Search catalogue. For each scene I did the following:

  1. Took the red band (B04) and the near infrared band (B08).
  2. Removed cells that the scene classification layer marks as no data, saturated, cloud shadow, medium or high probability cloud, or thin cirrus.
  3. Calculated NDVI.

Then I took the median of the clear values of each cell within each month. A median is the middle value of a sorted list, so one bad observation barely moves it. Each month needs a composite because many single passes are cloudy, especially in the monsoon, and a month of passes gives every cell a better chance of at least one clear view. The price is that a monthly file hides what happens inside the month.

The result is twelve rasters of 1000 by 1000 cells in EPSG:32643 (WGS 84 / UTM zone 43N). Every month has 4 to 6 usable scenes, so every cell has a value in every month.

What the year looks like

The figure below puts all twelve months on one colour scale. Click a month to enlarge it, or switch to the play view to watch the year run. The scale matters. If each map were stretched to its own range, a pale May and a pale November could use the same colours for different values.

Fields turn green twice a year. The town in the middle never does.
Monthly NDVI, 2023, a 10 km square around Jagraon, Punjab. Click a month to open it full screen.
0 bare soil, roofs0.30.60.9 dense green crop
Same colour scale on every map. 10 m Sentinel-2, monthly median. Contains modified Copernicus Sentinel data 2023.

Read it like a film. January to March are dark green, April turns pale, May to July are mostly pale, and August to October are green again. November is the palest month. In December green patches come back. The town stays pale in every month, because roofs and roads do not grow.

Reading three places

The maps show the whole square. A curve shows one place. I picked three.

Field A turns green twice a year. The town never does.
Monthly NDVI in 2023 at three places near Jagraon. Hover a month for all three. Click a name to hide it.
Winter windowMonsoon window0.000.250.500.751.00JanFebMarAprMayJunJulAugSepOctNovDecABC
The shaded bands are the two windows used in the seasons map: February and March, August and September.
Show the numbers as a table
MonthABC
Jan0.670.110.56
Feb0.860.110.52
Mar0.850.090.44
Apr0.350.090.56
May0.120.060.44
Jun0.060.070.42
Jul0.100.070.49
Aug0.740.110.57
Sep0.790.160.60
Oct0.720.100.64
Nov0.130.070.46
Dec0.430.080.65

Site A is a field next to the town. Its curve has two peaks: 0.86 in February and 0.85 in March, then 0.74, 0.79 and 0.72 in August, September and October. Between them it falls to 0.06 in June. This is the shape of a field with two crops in a year. The usual crop calendar of this region is wheat, sown from late October into December and harvested in April, then rice, transplanted in June and July and harvested from late September into October. A fits that calendar well. I did not visit the field, so this is a reading of the curve and not an observation of the crop.

Site B is in the middle of the town. Its NDVI stays between 0.06 and 0.16 in all twelve months.

Site C is a field south of the town that never falls to bare ground. Its NDVI stays between 0.42 and 0.65. Something green grows there for most of the year, but the curve cannot say what. An orchard, a fodder crop and a long-season crop can all look like this.

Mapping the whole square

A curve at three points says little about a million cells, but NumPy can do the same sums on all of them. Two summaries are useful.

The seasonal swing is the highest NDVI of a cell in the year minus its lowest. At A it is 0.81, at B it is 0.10, and at C it is 0.23. Across the square the mean swing is 0.64.

The town hardly changes, the fields swing by more than half the scale
Seasonal swing: the highest NDVI of the year minus the lowest.
Map of the seasonal swing of NDVI in the square around Jagraon
0 no change0.9 large change
A Rice and wheat field0.81
B Jagraon town0.10
C Field that stays green0.23

Mean swing over the whole square: 0.64.

The numbers are the same as in the QGIS tutorial (Cell statistics and the Raster calculator) and in the Python tutorial. These shares describe NDVI curves and are not a crop map. Contains modified Copernicus Sentinel data 2023.

The second summary asks in which seasons a cell is green. I call a cell green when its NDVI is above 0.6 at some point in a season, and I use February and March as the winter season and August and September as the monsoon season.

Six in ten cells are green in both seasons
A cell counts as green when its NDVI is above 0.6. Winter means February or March, monsoon means August or September.
Map of the square around Jagraon coloured by the season in which each cell is green
Both seasons58.7%
586,625 cells
Monsoon only (Aug or Sep)19.3%
192,711 cells
Winter only (Feb or Mar)2.5%
25,435 cells
Never above 0.619.5%
195,229 cells
The numbers are the same as in the QGIS tutorial (Cell statistics and the Raster calculator) and in the Python tutorial. These shares describe NDVI curves and are not a crop map. Contains modified Copernicus Sentinel data 2023.

About 58.7 percent of the cells are green in both seasons, which is the pattern of site A spread over the square. Another 19.3 percent are green in the monsoon only, 2.5 percent in winter only, and 19.5 percent in neither. The last group holds the town, roads, canals and fields that never reach 0.6 in those months. These shares describe NDVI curves. They are not a crop map.

Workflow

If you want to build this yourself, the steps are:

  1. Search a catalogue such as Earth Search for Sentinel-2 L2A scenes over a small area for one year.
  2. Mask clouds with the scene classification layer, and calculate NDVI for each scene.
  3. Take the median per cell for each month, and write twelve GeoTIFF files on the same grid.
  4. Stack the twelve files, for example with Build Virtual Raster in QGIS or np.stack in Python.
  5. Read the curve of a few places, then summarise every cell with the swing and the peak season.

I wrote the QGIS and Python versions as step-by-step tutorials that follow the same steps with the same numbers, using ready-made monthly files so you can skip the download:

The study note Remote sensing time series covers composites, gaps, phenology and trends in more depth.

Common mistakes

  • Stretching every map to its own minimum and maximum, so the colours mean different values in each picture.
  • Stacking the files in the wrong order and reading March where January belongs.
  • Filling a gap with a straight line and then treating the filled value as an observation. I tested this on the 2023 series by hiding August at site A and filling it from July and September: the fill gave 0.45 and the real value was 0.74, because rice grows fast between July and August and a straight line cannot know that.
  • Smoothing a series of only twelve points. A three-month rolling mean lowers the peaks and raises the troughs, which are the features you wanted to see.
  • Naming a crop from a curve that no one checked in the field.

Key takeaways

  • A raster time series is a stack of rasters of one place, in date order, on one grid.
  • Use one colour scale for every map in the series.
  • The curve of a place through the year says more than any single date.
  • In this square, one field has two green seasons, the town stays flat and low, and a third place stays green nearly all year.
  • Summaries such as the swing turn a stack into one map.
  • NDVI says how green a place was. It cannot say what grew there.
  • Monthly medians hide what happens inside the month, and one year is not a trend.
T&S
Time & Spatial
Engineers who've shipped real systems
Share