How to quickly assess urban water quality using Sentinel 2 satellite imagery in Google Earth Engine

In this article, we will together create a short, simple but smart workflow in Google Earth Engine on how to identify urban water bodies and find the most polluted one. Sentinel 2 satellite imagery of the European Space Agency will be used for estimation of chlorophyll-a level or the eutrophication state of water.

Minh Nguyen
AREA Lab
2 min readJun 9, 2020

--

If you want to go straight to GEE code, go to this link

A map showing all water surface area in the city of Hanoi. The most polluted one is in the circle named “Sword Lake”
The most polluted lake named “Sword Lake” is in the circle

Overview

Hanoi, Vietnam, the city where I live, is a city surrounded by lakes and rivers. Inside the metropolitan area only, there are about 120 natural lakes and ponds, covering 20% of the total city area. But as the city’s population growing (it reached 30.000 people per kilometer square in 2019), these lakes are being polluted by untreated sewage and domestic waste. The author believes that urban water bodies across Asia, are being faced with a similar situation.

In this article, we will together create a short, simple but smart workflow in Google Earth Engine to demonstrate how to quickly and accurately identify water bodies then find the most polluted one. We don’t have to go to the field but see things at 10 m resolution imagery thanks to Sentinel 2 satellite of the European Space Agency. Following a paper published by (Mistra 2012), chlorophyll-a level or the eutrophication state of water will be remotely sensed via the novel Normalized Difference Chlorophyll Index or NDCI.

Workflow

  • Create a polygon of your region of interest (roi)
var roi = ee.Geometry.Polygon(
[[[105.72, 21.12],
[105.72, 20.93],
[105.93, 20.93],
[105.93, 21.12]]]);
  • Select the best satellite image acquired by Sentinel 2 over your roi.
var IMG = ee.ImageCollection("COPERNICUS/S2_SR")
.filterBounds(roi)
.sort(‘CLOUDY_PIXEL_PERCENTAGE’,true)
.first()
.clip(roi);
  • Write a function to detect water pixel and mask out everything else
function ndwi_f(img){
var ndwi = img.normalizedDifference([‘B3’, ‘B8’]).rename(‘NDWI’);
return img.addBands(ndwi).updateMask(ndwi.gt(0))
}
  • Write a function to calculate NDCI for every water pixel
function ndci_f(img){
var ndci = img.normalizedDifference([‘B5’, ‘B4’]).rename(‘NDCI’);
return img.addBands(ndci)
}
  • Entire Script
https://code.earthengine.google.com/77e9e9e339de9f4be025a6069e2f32eb

Visualization

var viz = {min:0.1,max:0.4,palette:[‘cyan’,’orange’,’red’]}
Map.addLayer(IMG_NDCI.select(‘NDCI’),viz,”IMG_NDCI”)

I hope you enjoy reading and please give me a clap you if find it helpful, comment if you have anything to share. Let me know if you can reproduce it for the for your city/ area!

--

--

Minh Nguyen
AREA Lab

Satellite, Drone and Numerical Modeling. University of Applied Sciences, Cologne, Germany