Flood mapping with Google Earth Engine


Shammunul Islam (shais13irs@gmail.com)

Incessant rainfall in Bangladesh and heavy downpours in upstream regions caused very extreme flood events in Bangladesh in 2022 starting from mid-June (https://www.ifrc.org/emergency/bangladesh-floods). In this tutorial, we will use Google Earth Engine to analyze satellite imagery and map the floods in Bangladesh before and after the disaster.

Introduction to Google Earth Engine

Google Earth Engine (GEE) is a powerful cloud-based platform that allows users to analyze and visualize geospatial data of very large size. It provides access to a vast collection of satellite imagery and other geospatial datasets and a suite of geospatial analysis functionalities, making it an ideal tool for monitoring environmental changes and natural disasters.

Both JavaScript and Python can be used to visualize and analyze geospatial data. We will be using JavaScript here for mapping 2022 flood in Bangladesh.

Introduction to Synthetic Aperture Radar (SAR)

We will be using Synthetic Aperture Radar (SAR) as SAR can penetrate cloud-cover unlike optical imagery which can't see through cloud.

We will be using data from Sentinel-1 mission that "comprises a constellation of two polar-orbiting satellites, operating day and night performing C-band synthetic aperture radar imaging, enabling them to acquire imagery regardless of the weather." https://sentinels.copernicus.eu/web/sentinel/missions/sentinel-1

Sentinel-1 has a spatial resolution of 10m or, one pixel represents an area of 10 by 10 meters on the ground. It has a temporal resolution of 6-12 days meaning that for the satellite to come back to the same place to capture the second image, it takes around 6-12 days.

Sentinel-1 SAR captures data using various polarization modes, which are combinations of transmit and receive orientations. These modes include:

  • VV (Vertical Transmit, Vertical Receive): Utilized for assessing surface roughness and terrain mapping.
  • VH (Vertical Transmit, Horizontal Receive): Particularly useful for monitoring vegetation and changes in forests and agricultural areas.
  • HV (Horizontal Transmit, Vertical Receive): Also relevant for vegetation monitoring and agricultural applications.
  • HH (Horizontal Transmit, Horizontal Receive): Used for studying surface roughness and land cover classification.

It has 4 different operational modes:

  • Interferometric wide-swath (IW) at 250 km and 5×20 m resolution -- land and coastal areas are captured using this mode
  • Wave (WV) images of 20×20 km and 5×5 m resolution
  • Stripmap (SM) at 80 km swath and 5×5 m resolution
  • Extra wide swath (EW) of 400 km and 20×40 m resolution

Source: https://asf.alaska.edu/data-sets/sar-data-sets/sentinel-1/sentinel-1-instrument/

Step 1: Setting Up the Environment

To get started, visit the Google Earth Engine Code Editor and sign in with your Google account. The Code Editor is where we will write and execute our Earth Engine code.

The whole code for this mapping exercise can be found here

Step 2: Define the Study Area

We will be plotting flood over the whole Bangladesh and so we will need to import the shapefile of Bangladesh.

In [ ]:
from google.colab import drive
Mounted at /content/drive
In [ ]:
path = '/content/drive/MyDrive/Flood Mapping/'

We will be working with the shapefile of Bangladesh. First, click on Assets and then click on Shape files (.shp, .shx, .dbf, .prj, or .zip) to select the shapefile that we want to work with.

In [ ]:
from IPython.display import Image
%matplotlib inline
Image(filename=path+'0_final.png', width=1000)
Out[ ]:

Now, click on Select to select the shapefile that we want to upload.

In [ ]:
Image(filename=path+'0_2_final.png', width=1000)
Out[ ]:

We want to upload the shapefile named BGD_adm1.shp. For this file, select all the files starting with the name BGD_adm1 and ending with the extension names .dbf, .prj, .sbn, .shp and .shx.

In [ ]:
Image(filename=path+'0_3.png', width=700)
Out[ ]:

After we select these files, in GEE, we will see the Assets window will look like this now. Click UPLOAD.

In [ ]:
Image(filename=path+'0_4.png', width=500)
Out[ ]:

Now, we will see that this shapefile is uploaded as BGD_adm1 and is now listed under CLOUD ASSETS.

In [ ]:
Image(filename=path+'0_5.png', width=500)
Out[ ]:

Now, click on the right arrow sign found when hovered on BGD_adm1 to Import into script.

Now, we will see that this shapefile is imported into the script as a table.

In [ ]:
Image(filename=path+'0_6.png', width=500)
Out[ ]:

Rename table to bd by clicking on the name table and then just renaming it to bd.

The following two lines convert the variable bd to geometry object and adds it to the Map object which allows the shapefile to be viewed as a map.

var geometry = bd.geometry();
Map.addLayer(geometry, {color: 'grey'}, 'Bangladesh');

In this case, we specify { color: 'grey' }, which sets the color of the boundary to grey. The third argument ('Bangladesh') is a string used as the name of the layer displayed in the layer panel on the left side of the Code Editor. It will be shown as "Bangladesh" in this case.

Step 3: Selecting the Satellite Images

The beforeStart and beforeEnd variables define the time period before flood. Here we are selecting the two weeks of October to ensure that we have at least 1 image between this time period. Similarly, afterStart and afterEnd variables define the flood period, as we are interested in mapping the flooding of June, we select two week period of this month.

// Select images by predefined dates
var beforeStart = '2021-10-10';
var beforeEnd = '2021-10-23';
var afterStart = '2022-06-15';
var afterEnd = '2022-06-28';

The line ee.ImageCollection('COPERNICUS/S1_GRD') selects Sentinel-1 data. The following lines used with filter specifies further constraints on the dataset. We want the operation mode to be Interferometric wide-swath (IW) .filter(ee.Filter.eq('instrumentMode','IW')). The dataset should contain VV and VH polarisation specified by these two lines .filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VH')) .filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV')). Use data for either ASCENDING or DESCENDING orbit for both before and after situation, here we use DESCENDING orbit .filter(ee.Filter.eq('orbitProperties_pass', 'DESCENDING')). To be safe, also mention the resolution of the image to be 10 metres .filter(ee.Filter.eq('resolution_meters',10)) .filterBounds(geometry). Finally, get the data only bounded by the geometry which is defined by the shapefile you have uploaded already .filterBounds(geometry).

var collection= ee.ImageCollection('COPERNICUS/S1_GRD')
  .filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VH'))
  .filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV'))
  .filter(ee.Filter.eq('orbitProperties_pass', 'DESCENDING'))
  .select(['VV', 'VH']);

Step 4: Preparing the Image Collections

Next, we will create image collections for the "before" and "after" periods and mosaic the images to create a single composite image for each period.

// Filter the collection based on date ranges
var beforeCollection = collection.filterDate(beforeStart, beforeEnd);
var afterCollection = collection.filterDate(afterStart, afterEnd);

// Mosaic the images to create composite images for the "before" and "after" periods
var before = beforeCollection.mosaic().clip(geometry);
var after = afterCollection.mosaic().clip(geometry);

Step 5: Enhancing Flood Detection with Ratio Bands

To improve our ability to detect floods, we will compute a ratio between two polarization bands: VV (vertical-vertical) and VH (vertical-horizontal). Flooded areas typically exhibit low backscatter values in the VH band and higher values in the VV band, resulting in a discernible ratio. By analyzing this ratio, we can highlight potential flood-affected regions and distinguish them from other areas, aiding flood detection and monitoring efforts.

// Function to add a ratio band to an image
var addRatioBand = function (image) {
  var ratioBand = image.select('VV').divide(image.select('VH')).rename('VV/VH');
  return image.addBands(ratioBand);

// Apply the ratio band function to the "before" and "after" images
var beforeRgb = addRatioBand(before);
var afterRgb = addRatioBand(after);

Step 6: Visualizing the Floods

Finally, we will visualize the "before" and "after" composite images to compare the flood extent using a red-green-blue (RGB) color scheme.

// Visualization parameters
var visParams = { min: [-25, -25, 0], max: [0, 0, 2] };

// Center the map on the study area
Map.centerObject(geometry, 10);

// Add the "before" and "after" images to the map for visualization
Map.addLayer(beforeRgb, visParams, 'Before Floods');
Map.addLayer(afterRgb, visParams, 'After Floods');

Running the code by clicking on Run, we find the following map generated.

In [ ]:
Image(filename=path+'4_2.png', width=1000)
Out[ ]: