Holy Ganga River carves her own way: Determining the long-term changes in the Ganga River Basin, India using Google Earth Engine and high-resolution mapping of global surface water.

Quick Overview:

This project uses the dataset described in the Nature letter High-resolution mapping of global surface water and its long-term changes in the Ganga River Basin, India. This tutorial provides examples of how to use Google Earth Engine to visualize data layers available in the GSW dataset, and presents some typical visualizations and analyses.

About the Data-set

Water Occurrence (1984-2015)

The GSW dataset contains many data layers that present the surface water data in different ways. This dataset contains a summary of where and how often surface water occurred over the entire time-period between March 1984 and October 2015.
Typically, more cloud-free observations (and thus valid observations) are available during dry seasons than wet. Without monthly weighting, the overall water occurrence (that is, computed over the full period) would be biased by temporal distribution of the valid observations (that is, giving more weight to the dry season than to the wet season).

In this tutorial, we will:

  1. add a styled map layer for visualizing water occurrence change intensity, and
  2. create a grouped reducer for summing the area of each transition class within a specified region-of-interest, and
  3. summarize the change intensity in a specified region-of-interest using a histogram.

Here is the code: Note that this code is modified from the code in Google Earth Engine.

Import images from GSW dataset and store it in variable named “gsw”

var gsw = ee.Image('JRC/GSW1_0/GlobalSurfaceWater'); 
 var occurrence = gsw.select('occurrence');
 var change = gsw.select("change_abs");
 var transition = gsw.select('transition');
 var roi = ee.Geometry.Polygon(
         [[[105.531921, 10.412183],
           [105.652770, 10.285193],
           [105.949401, 10.520218],
           [105.809326, 10.666006]]]);

Adding Visualization Parameters

The visualization parameters stored in JSON structure VIS_OCCURRENCE indicate that red should be used for a minimum value of 0% and blue for a maximum value of 100%.

     min: 0,
     max: 100,
     palette: ['red', 'blue']
 var VIS_CHANGE = {
     min: -50,
     max: 50,
     palette: ['red', 'black', 'limegreen']

Creating a Threshold Layer

The water occurrence image contains information on how often water is expected using a range of values from 0 to 100%. However, it is often useful to define a binary water layer (i.e. “water” vs. “non-water”) based on a certain percentage of occurrence (i.e. a threshold value). We will use this simple binary layer as a clean background layer over which other GSW layers can be placed. Creating this threshold layer can be done using the following statements, which uses a threshold value of 90% to separate water and non-water.

   palette: ['white', 'black']

Create a feature for a transition class that includes the area covered.

function createFeature(transition_class_stats) {
   transition_class_stats = ee.Dictionary(transition_class_stats);
   var class_number = transition_class_stats.get('transition_class_value');
   var result = {
       transition_class_number: class_number,
       transition_class_name: lookup_names.get(class_number),
       transition_class_palette: lookup_palette.get(class_number),
       area_m2: transition_class_stats.get('sum')
   return ee.Feature(null, result);   // Creates a feature without a geometry.

Create a JSON dictionary that defines piechart colors based on the transition class palette.

// https://developers.google.com/chart/interactive/docs/gallery/piechart
 function createPieChartSliceDictionary(fc) {
   return ee.List(fc.aggregate_array("transition_class_palette"))
     .map(function(p) { return {'color': p}; }).getInfo();
// Create a dictionary for looking up names of transition classes.
var lookup_names = ee.Dictionary.fromLists(
// Create a dictionary for looking up colors of transition classes.
var lookup_palette = ee.Dictionary.fromLists(
// Create a water mask layer, and set the image mask so that non-water areas are transparent.
var water_mask = occurrence.gt(90).mask(1);
// Generate a histogram object and print it to the console tab.
var histogram = ui.Chart.image.histogram({
  image: change,
  region: roi,
  scale: 30,
  minBucketWidth: 10
  title: 'Histogram of surface water change intensity.'
// Summarize transition classes in a region of interest.
var area_image_with_transition_class = ee.Image.pixelArea().addBands(transition);
var reduction_results = area_image_with_transition_class.reduceRegion({
  reducer: ee.Reducer.sum().group({
    groupField: 1,
    groupName: 'transition_class_value',
  geometry: roi,
  scale: 30,
  bestEffort: true,
print('reduction_results', reduction_results);
var roi_stats = ee.List(reduction_results.get('groups'));
var transition_fc = ee.FeatureCollection(roi_stats.map(createFeature));
print('transition_fc', transition_fc);
// Add a summary chart.
var transition_summary_chart = ui.Chart.feature.byFeature({
    features: transition_fc,
    xProperty: 'transition_class_name',
    yProperties: ['area_m2', 'transition_class_number']
    title: 'Summary of transition class areas',
    slices: createPieChartSliceDictionary(transition_fc),
    sliceVisibilityThreshold: 0  // Don't group small slices.

Initialize Map Location

//Map.setCenter(84.7866,25.7722,12); //Ganga at Patna
Map.setCenter(86.881,25.4472,10); //Ganga at Bhagalpur
//Map.setCenter(90.325,23.4207,10); //Bhramaputra River, Bangladesh
// Map Layers
  eeObject: water_mask,
  visParams: VIS_WATER_MASK,
  name: '90% occurrence water mask',
  shown: false
  eeObject: occurrence.updateMask(occurrence.divide(100)),
  name: "Water Occurrence (1984-2015)",
  visParams: VIS_OCCURRENCE,
  shown: false
  eeObject: change,
  visParams: VIS_CHANGE,
  name: 'occurrence change intensity',
  shown: false
  eeObject: transition,
  name: 'Transition classes (1984-2015)',
  shown: false
// Create a circle by drawing a 20000 meter buffer around a point.
var roi = ee.Geometry.Point([86.9306, 26.5296]).buffer(10000);
  eeObject: occurrence.updateMask(occurrence.divide(100))
  name: "Clipped",
  visParams: VIS_OCCURRENCE,
  //shown: false


Some interesting phenomenon across the globe.

12 thoughts on “Holy Ganga River carves her own way: Determining the long-term changes in the Ganga River Basin, India using Google Earth Engine and high-resolution mapping of global surface water.”

  1. Pingback: Develop a Harmonic Model: Original vs Fitted NDVI values (Study Area: Thailand) – DINESH SHRESTHA

  2. Pingback: Using Hansen Global Forest Change Data to determine Forest Cover, Forest Gain, and Forest Loss (Study Area: Malaysia) – DINESH SHRESTHA

  3. Pingback: Make your map look Artistic – Add Grid Lines in your Map (Study Area: Canada) – DINESH SHRESTHA

  4. Pingback: Night Light Maps (Study Area: South Asia) – DINESH SHRESTHA

  5. Pingback: How to generate Histogram in Google Earth Engine? – DINESH SHRESTHA

  6. Pingback: Using 2001 MODIS Land Cover Type Yearly Global 500m Data to Determine the Land-Cover Analysis (Study Area: Mexico) – DINESH SHRESTHA

  7. Pingback: Learn How to Add two charts next to the map to interactively display a time-series of NDVI and reflectance for each click on the map? – DINESH SHRESTHA

  8. Pingback: Create a Chart to show Landsat 8 TOA Spectra at three points near Mumbai City – DINESH SHRESTHA

  9. Pingback: Learn how to calculate and export 90th Percentile Annual NDVI for years 1985-2019 using Landsat 8, 7, and 5 scenes (Study Area: Turkey) – DINESH SHRESTHA

  10. Pingback: Use 2018 Landsat 8 Scenes for Land Cover Classification in Google Earth Engine (Study Area: Dubai) – DINESH SHRESTHA

  11. Pingback: Land Cover Change Analysis between 1999 and 2018 in GEE (Study Area: Mumbai) – DINESH SHRESTHA

  12. Pingback: Time-lapse: Animate 30m Landsat images generated 90th percentile Annual NDVI from 1999 to 2018 (Study Area: South America) – DINESH SHRESTHA

Leave a Reply

%d bloggers like this: