In the previous module, we mapped water from a single satellite image. But what if we want to know how water has changed over the past 40 years? That's a much bigger question. The JRC Global Surface Water dataset, produced by the European Commission's Joint Research Centre, answers exactly that question using the entire Landsat archive. Let's explore it.
Learning objectives
- Load and use the JRC Global Surface Water occurrence layer.
- Interpret water seasonality and transition classes.
- Visualize long-term changes in surface water extent.
- Understand the structure and layers of the JRC GSW dataset.
Why it matters
Some of the most dramatic environmental changes on Earth involve water. The Aral Sea has lost over 90% of its volume. Lake Chad has shrunk by 90% since the 1960s. Meanwhile, new reservoirs and glacial lakes are appearing in places they never existed before.
This is a powerful insight: the JRC dataset lets us quantify all of these changes at 30-meter resolution, globally, using a consistent methodology. No fieldwork required.
Key vocabulary
- Water occurrence
- The percentage of time a pixel has been covered by water between 1984 and the present. A value of 100 means permanent water; 50 means water half the time.
- Water transition
- A classification of how water presence has changed between the first and last periods of observation (e.g., permanent, seasonal, new permanent, lost permanent).
- Seasonality
- The number of months per year that a pixel is typically covered by water.
- Maximum extent
- A binary layer showing every pixel that was detected as water at least once during the entire observation period.
- JRC GSW
- Joint Research Centre Global Surface Water dataset, version 1.4. Derived from over 4.7 million Landsat scenes spanning 1984 to present.
Quick Win: Watching the Aral Sea Disappear
The Aral Sea was once the world's fourth-largest lake. Today, it's a shadow of itself, one of the most dramatic environmental catastrophes in modern history. Let's visualize its water occurrence to see the full scale of what happened.
// Visualize water occurrence for the Aral Sea
var aral = ee.Geometry.Point([59.0, 44.5]);
// Load the JRC Global Surface Water dataset
var gsw = ee.Image('JRC/GSW1_4/GlobalSurfaceWater');
// Select the occurrence band
var occurrence = gsw.select('occurrence');
// Visualize with a blue gradient palette
Map.centerObject(aral, 7);
Map.addLayer(occurrence, {
min: 0,
max: 100,
palette: ['ffffff', 'ffbbbb', 'ff0000', '0000ff']
}, 'Water Occurrence (%)');
print('JRC GSW band names:', gsw.bandNames());
What you should see
A striking map of the Aral Sea. The original lake outline shows up in light red (water was only present a small percentage of the time), with a tiny remnant of deep blue in the north (areas still covered by water most of the time). The contrast between the vast historical footprint and the tiny remaining water body hits you immediately.
What's Inside the JRC Dataset?
Here's the cool part: the JRC team analyzed over 4.7 million Landsat satellite images spanning 1984 to the present. For every 30-meter pixel on Earth, they recorded when water was present and when it was absent. From that massive effort, they produced several derived layers, each capturing a different facet of water dynamics.
The dataset is stored as a single ee.Image with multiple bands. We don't need
to filter dates or create composites because the JRC team already did that heavy lifting across
the entire Landsat archive. Nice, right?
// Load the dataset and explore its bands
var gsw = ee.Image('JRC/GSW1_4/GlobalSurfaceWater');
print('Available bands:', gsw.bandNames());
// Bands: occurrence, change_abs, change_norm, seasonality,
// recurrence, transition, max_extent
Dataset layers at a glance
occurrence: Percentage of time water was detected (0 to 100).seasonality: Number of months water is present per year (0 to 12).transition: Categorical change classes (permanent, seasonal, new, lost).max_extent: Binary mask of all pixels ever detected as water.change_abs: Absolute change in water occurrence between periods.recurrence: How frequently water returns to a location.
Reading the Occurrence Layer
The occurrence band is the most intuitive layer. Think of it like a report card for each pixel: "Out of all the cloud-free Landsat scenes we looked at, what percentage of the time was this pixel covered by water?"
Permanent lakes and rivers will score close to 100%. Seasonally flooded areas will have intermediate values. Areas that were once water but have since dried up? Low values.
// Map occurrence with descriptive classes
var occurrence = gsw.select('occurrence');
// Permanent water: occurrence > 75%
var permanent = occurrence.gte(75);
// Seasonal water: occurrence between 25% and 75%
var seasonal = occurrence.gte(25).and(occurrence.lt(75));
// Ephemeral water: occurrence between 1% and 25%
var ephemeral = occurrence.gte(1).and(occurrence.lt(25));
Map.addLayer(permanent.selfMask(), {palette: ['0000CC']}, 'Permanent Water');
Map.addLayer(seasonal.selfMask(), {palette: ['00CCFF']}, 'Seasonal Water');
Map.addLayer(ephemeral.selfMask(), {palette: ['FFAAAA']}, 'Ephemeral Water');
Try it: Explore occurrence values
Use the Inspector tool to click on different parts of the Aral Sea occurrence layer. What values do you see in the former lake bed versus the remaining water? Navigate to a river near you and check its floodplain. Can you identify the main channel (high occurrence) versus the flood-prone areas (low occurrence)?
Where Has Water Come and Gone?
The transition band is like a before-and-after snapshot for every pixel. It classifies each pixel based on how its water status changed between the first and last observation periods. This is the fastest way to see where water has been permanently lost, where new water has appeared, and where seasonal patterns have shifted.
// Visualize water transitions
var transition = gsw.select('transition');
// Official JRC transition class palette
Map.addLayer(transition, {
min: 0,
max: 10,
palette: [
'ffffff', // 0: No change
'0000ff', // 1: Permanent
'22b14c', // 2: New permanent
'd1102d', // 3: Lost permanent
'99d9ea', // 4: Seasonal
'b5e61d', // 5: New seasonal
'e6a1aa', // 6: Lost seasonal
'ff7f27', // 7: Seasonal to permanent
'ffc90e', // 8: Permanent to seasonal
'7f7f7f', // 9: Ephemeral permanent
'c3c3c3' // 10: Ephemeral seasonal
]
}, 'Water Transitions');
Here are the key transition classes to watch for:
- Permanent (1): Water that has been there throughout the entire period. Steady and reliable.
- New permanent (2): Areas that became permanently covered by water (think: new reservoirs).
- Lost permanent (3): Areas that were once permanent water but have dried up. This is the Aral Sea story.
- Seasonal (4): Areas that consistently show seasonal water patterns.
- Permanent to seasonal (8): Previously permanent water that now appears only part of the year. A warning sign.
Watch out for this
- The transition values are categorical codes, not continuous data. Don't compute averages or
apply
.gt()on them. - Use
.eq()to select specific transition classes (e.g.,transition.eq(3)for lost permanent water).
The Rhythm of Water: Seasonality
The seasonality band tells us how many months per year a pixel is typically covered by water. A value of 12 means year-round water. A value of 3 means the pixel is wet for roughly one quarter of the year.
// Visualize water seasonality
var seasonality = gsw.select('seasonality');
Map.addLayer(seasonality, {
min: 1,
max: 12,
palette: ['FFE4B5', 'FFA500', 'FF4500', '8B0000', '4169E1', '00008B']
}, 'Seasonality (months/year)');
print('Seasonality range:', seasonality.reduceRegion({
reducer: ee.Reducer.minMax(),
geometry: aral.buffer(200000),
scale: 30,
maxPixels: 1e9
}));
Seasonality maps are especially useful for understanding monsoon flooding, snowmelt cycles, and irrigation patterns. In arid regions, you'll see reservoirs with high seasonality values (water year-round) surrounded by areas with low values (wet only after seasonal rains). It's like reading the heartbeat of a landscape.
Then vs. Now: Tracking Water Loss
Here's where it gets interesting. The max_extent band records every pixel that was
ever detected as water. By comparing this historical maximum with a current water mask, we can
measure exactly how much water has been lost. Let's do this for the Aral Sea.
// Compare historical maximum with current water
var maxExtent = gsw.select('max_extent');
var currentWater = occurrence.gte(75); // Pixels currently permanent water
// Areas of water loss: was water historically but not currently
var waterLoss = maxExtent.eq(1).and(currentWater.eq(0));
Map.addLayer(maxExtent.selfMask(), {palette: ['AADDFF']}, 'Historical Maximum Extent');
Map.addLayer(currentWater.selfMask(), {palette: ['0000CC']}, 'Current Permanent Water');
Map.addLayer(waterLoss.selfMask(), {palette: ['FF0000']}, 'Water Loss');
// Calculate lost area in km²
var lostArea = waterLoss.multiply(ee.Image.pixelArea()).reduceRegion({
reducer: ee.Reducer.sum(),
geometry: aral.buffer(200000),
scale: 30,
maxPixels: 1e9
});
print('Water loss area (km²):', ee.Number(lostArea.get('max_extent')).divide(1e6));
What you should see
Three overlapping layers around the Aral Sea. The pale blue historical extent shows the original lake boundary. The dark blue current water shows the small remaining lake. The red water loss layer fills the vast gap between them. The scale of the Aral Sea's retreat is immediately, viscerally clear.
Quick self-check
- How many Landsat scenes were used to create the JRC GSW dataset?
- What does an occurrence value of 50 mean?
- Which transition class code represents water that has been permanently lost?
- If a pixel has a seasonality value of 4, how many months per year does it typically have water?
- How would you identify areas where new reservoirs were built?
Going deeper
We've explored the JRC GSW dataset at a foundational level here. For advanced techniques, check out:
Next Steps
- Lab 23: Water Mapping - Apply MNDWI and JRC GSW to map and compare water extent at Lake Mead.