Water Class Transition

The water transition layer captures changes between three classes of water occurrence (not water, seasonal water, and permanent water) along with two additional classes for ephemeral water (ephemeral permanent and ephemeral seasonal).

This section of the tutorial will:

  1. add a map layer for visualizing water transition,
  2. create a grouped reducer for summing the area of each transition class within a specified region-of-interest, and
  3. create a chart that summarizes the area by transition class.

Basic Visualization

In the Asset List section of the script, add the following statement which creates a single band image object called transition:

Code Editor (JavaScript)

var transition = gsw.select('transition');

The GSW images contain metadata on the transition class numbers and names, and a default palette for styling the transition classes. When the transition layer is added to the map, these visualization parameters will be used automatically.

At the bottom of the Map Layers section of your script, add the following statement which adds a new map layer that displays the transition classes:

Code Editor (JavaScript)

Map.setCenter(105.26, 11.2134, 9);     // Mekong River Basin, SouthEast Asia
Map.addLayer({
  eeObject: transition,
  name: 'Transition classes (1984-2015)',
});

When you run the script, the transition layer will be displayed.

Surface Water Transition Classes
Figure 10. Screenshot of the Mekong River delta, showing a wide variation in surface water class transitions.

The map key for the transition classes is:

Value Symbol Label
0 Not water
1 Permanent
2 New permanent
3 Lost permanent
4 Seasonal
5 New seasonal
6 Lost seasonal
7 Seasonal to permanent
8 Permanent to seasonal
9 Ephemeral permanent
10 Ephemeral seasonal

Summarizing Area by Transition Class

In this section we will once again use the geometry polygon tool to define a region-of-interest. If you want to analyze a new location, you will want to first select and delete the original polygon that you drew so that you don't get results from the combined areas. See the Geometry tools section of the Code Editor docs from information on how to modify geometries.

For this example we will draw a new polygon within the Mekong River delta.

Transition Classes with ROI
Figure 11. The Mekong River delta in Vietnam, with a region-of-interest created by using the Code Editor's polygon drawing tool.

In order to calculate the area covered by parts of an image, we will add an additional band to the transition image object that identifies the size of each pixel in square meters using the ee.Image.pixelArea method.

Code Editor (JavaScript)

var area_image_with_transition_class = ee.Image.pixelArea().addBands(transition);

The resulting image object (area_image_with_transition_class) is a two band image where the first band contains the area information in units of square meters (produced by the ee.Image.pixelAreacode> method), and the second band contains the transition class information.

We then summarize the class transitions within a region of interest (roi) using the ee.Image.reduceRegion method and a grouped reducer which acts to sum up the area within each transition class:

Code Editor (JavaScript)

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);

The console tab output now displays the reduction_results. Note that you will need to expand the tree a few levels to see the area summary data.

Grouped Reduction Results
Figure 12. Console tab output, showing the results of the grouped reduction.

While the reduction_results object does contain information on the area covered by each transition class, it is not particularly easy to read. In the next section we will make it easier to view the results.

Creating a Summary Chart

In this section we will make a chart to better summarize the results. To get started, we first extract out the list of transition classes with areas as follows:

Code Editor (JavaScript)

var roi_stats = ee.List(reduction_results.get('groups'));

The result of the grouped reducer (reduction_results) is a dictionary containing a list of dictionaries. There is one dictionary in the list for each transition class. These statements use the ee.Dictionary.get method to extract the grouped reducer results from that dictionary and casts the results to an ee.List data type, so we can access the individual dictionaries.

In order to make use of the charting functions of the Code Editor, we will build a FeatureCollection that contains the necessary information. To do this we first create two lookup dictionaries and two helper functions. The code that creates the lookup dictionaries can be placed at the top of the "Calculations" section as follows:

Code Editor (JavaScript)

//////////////////////////////////////////////////////////////
// Calculations
//////////////////////////////////////////////////////////////

// Create a dictionary for looking up names of transition classes.
var lookup_names = ee.Dictionary.fromLists(
    ee.List(gsw.get('transition_class_values')).map(numToString),
    gsw.get('transition_class_names')
);
// Create a dictionary for looking up colors of transition classes.
var lookup_palette = ee.Dictionary.fromLists(
    ee.List(gsw.get('transition_class_values')).map(numToString),
    gsw.get('transition_class_palette')
);

The lookup_names dictionary associates transition class values with their names, while the lookup_palette dictionary associates the transition class values with color definitions.

The two helper functions can be placed in a new code section called "Helper functions".

Code Editor (JavaScript)

//////////////////////////////////////////////////////////////
// Helper functions
//////////////////////////////////////////////////////////////

// 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();
}

// Convert a number to a string. Used for constructing dictionary key lists
// from computed number objects.
function numToString(num) {
  return ee.Number(num).format();
}

The function createFeature takes a dictionary (containing the area and the water transition class) and returns a Feature suitable for charting. The function createPieChartSliceDictionary creates a list of colors that correspond to the transition classes, using the format expected by the pie chart.

Next, we will apply the createFeature function over each dictionary in the list (roi_stats), using ee.List.map to apply the helper function to each element of the list.

Code Editor (JavaScript)

var transition_fc = ee.FeatureCollection(roi_stats.map(createFeature));
print('transition_fc', transition_fc);

Now that we have a FeatureCollection containing the attributes we want to display on the chart, we can create a chart object and print it to the console.

Code Editor (JavaScript)

// 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']
  })
  .setChartType('PieChart')
  .setOptions({
    title: 'Summary of transition class areas',
    slices: createPieChartSliceDictionary(transition_fc),
    sliceVisibilityThreshold: 0  // Don't group small slices.
  });
print(transition_summary_chart);

The slices option colors the pie chart slices so that they use the default palette defined for the transition classes (shown earlier in the map key table). The sliceVisibilityThreshold option prevents small slices from being grouped together into an "other" category. The resulting chart should be something similar to the one shown in Figure 13.

Water transition class summary chart
Figure 13. Chart summarizing the relatives size of the water transition classes.

Final Script

The entire script for this section is:

Code Editor (JavaScript)

//////////////////////////////////////////////////////////////
// Asset List
//////////////////////////////////////////////////////////////

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]]]);
//////////////////////////////////////////////////////////////
// Constants
//////////////////////////////////////////////////////////////

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

//////////////////////////////////////////////////////////////
// Helper functions
//////////////////////////////////////////////////////////////

// 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();
}

// Convert a number to a string. Used for constructing dictionary key lists
// from computed number objects.
function numToString(num) {
  return ee.Number(num).format();
}

//////////////////////////////////////////////////////////////
// Calculations
//////////////////////////////////////////////////////////////

// Create a dictionary for looking up names of transition classes.
var lookup_names = ee.Dictionary.fromLists(
    ee.List(gsw.get('transition_class_values')).map(numToString),
    gsw.get('transition_class_names')
);
// Create a dictionary for looking up colors of transition classes.
var lookup_palette = ee.Dictionary.fromLists(
    ee.List(gsw.get('transition_class_values')).map(numToString),
    gsw.get('transition_class_palette')
);

// 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
});
histogram.setOptions({
  title: 'Histogram of surface water change intensity.'
});
print(histogram);

// 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']
  })
  .setChartType('PieChart')
  .setOptions({
    title: 'Summary of transition class areas',
    slices: createPieChartSliceDictionary(transition_fc),
    sliceVisibilityThreshold: 0  // Don't group small slices.
  });
print(transition_summary_chart);

//////////////////////////////////////////////////////////////
// Initialize Map Location
//////////////////////////////////////////////////////////////

// Uncomment one of the following statements to center the map on
// a particular location.
// Map.setCenter(-90.162, 29.8597, 10);   // New Orleans, USA
// Map.setCenter(-114.9774, 31.9254, 10); // Mouth of the Colorado River, Mexico
// Map.setCenter(-111.1871, 37.0963, 11); // Lake Powell, USA
// Map.setCenter(149.412, -35.0789, 11);  // Lake George, Australia
Map.setCenter(105.26, 11.2134, 9);     // Mekong River Basin, SouthEast Asia
// Map.setCenter(90.6743, 22.7382, 10);   // Meghna River, Bangladesh
// Map.setCenter(81.2714, 16.5079, 11);   // Godavari River Basin Irrigation Project, India
// Map.setCenter(14.7035, 52.0985, 12);   // River Oder, Germany & Poland
// Map.setCenter(-59.1696, -33.8111, 9);  // Buenos Aires, Argentina
// Map.setCenter(-74.4557, -8.4289, 11);  // Ucayali River, Peru

//////////////////////////////////////////////////////////////
// Map Layers
//////////////////////////////////////////////////////////////

Map.addLayer({
  eeObject: water_mask,
  visParams: VIS_WATER_MASK,
  name: '90% occurrence water mask',
  shown: false
});
Map.addLayer({
  eeObject: occurrence.updateMask(occurrence.divide(100)),
  name: "Water Occurrence (1984-2015)",
  visParams: VIS_OCCURRENCE,
  shown: false
});
Map.addLayer({
  eeObject: change,
  visParams: VIS_CHANGE,
  name: 'occurrence change intensity',
  shown: false
});
Map.addLayer({
  eeObject: transition,
  name: 'Transition classes (1984-2015)',
});

This concludes the tutorial on the Global Surface Water dataset. Note that this tutorial has shown how to work with just three of the data layers (occurrence, change intensity, and transition) that are available in the Global Surface Water dataset. You can read about the other data layers that are available in the Data Users Guide (v2).

Happy analyzing!