A23a Checkpoint
Import libraries¶
In [ ]:
Copied!
import ee
import geemap
import ee
import geemap
Create an interactive map¶
In [ ]:
Copied!
Map = geemap.Map(center=[40, -100], zoom=4)
Map = geemap.Map(center=[40, -100], zoom=4)
Add Earth Engine Python script¶
In [ ]:
Copied!
# Add Earth Engine dataset
image = ee.Image("USGS/SRTMGL1_003")
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Chapter: A2.3 Surface Water Mapping
# Checkpoint: A23a
# Authors: K. Markert, G. Donchyts, A. Haag
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# *** Section 1 ***
# Define a point in Cambodia to filter by location.
point = ee.Geometry.Point(104.9632, 11.7686)
Map.centerObject(point, 11)
# Get the Sentinel-1 collection and filter by space/time.
s1Collection = ee.ImageCollection('COPERNICUS/S1_GRD') \
.filterBounds(point) \
.filterDate('2019-10-05', '2019-10-06') \
.filter(ee.Filter.eq('orbitProperties_pass', 'ASCENDING')) \
.filter(ee.Filter.eq('instrumentMode', 'IW'))
# Grab the first image in the collection.
s1Image = s1Collection.first()
# Add the Sentinel-1 image to the map.
Map.addLayer(s1Image, {
'min': -25,
'max': 0,
'bands': 'VV'
}, 'Sentinel-1 image')
# Specify band to use for Otsu thresholding.
band = 'VV'
# Define a reducer to calculate a histogram of values.
histogramReducer = ee.Reducer.histogram(255, 0.1)
# Reduce all of the image values.
globalHistogram = ee.Dictionary(
s1Image.select(band).reduceRegion({
'reducer': histogramReducer,
'geometry': s1Image.geometry(),
'scale': 90,
'maxPixels': 1e10
}).get(band)
)
# Extract out the histogram buckets and counts per bucket.
x = ee.List(globalHistogram.get('bucketMeans'))
y = ee.List(globalHistogram.get('histogram'))
# Define a list of values to plot.
dataCol = ee.Array.cat([x, y], 1).toList()
# Define the header information for data.
columnHeader = ee.List([
[
{
'label': 'Backscatter',
'role': 'domain',
'type': 'number'
},
{
'label': 'Values',
'role': 'data',
'type': 'number'
}, ]
])
# Concat the header and data for plotting.
dataTable = columnHeader.cat(dataCol)
# Create plot using the ui.Chart function with the dataTable.
# Use 'evaluate' to transfer the server-side table to the client.
# Define the chart and print it to the console.
dataTable.evaluate(function(dataTableClient) {
chart = ui.Chart(dataTableClient) \
.setChartType('AreaChart') \
.setOptions({
'title': band + ' Global Histogram',
'hAxis': {
'title': 'Backscatter [dB]',
'viewWindow': {
'min': -35,
'max': 15
}
},
'vAxis': {
'title': 'Count'
}
})
print(chart)
})
# See:
# https:#medium.com/google-earth/otsus-method-for-image-segmentation-f5c48f405e
def otsu(histogram):
# Make sure histogram is an ee.Dictionary object.
histogram = ee.Dictionary(histogram)
# Extract relevant values into arrays.
counts = ee.Array(histogram.get('histogram'))
means = ee.Array(histogram.get('bucketMeans'))
# Calculate single statistics over arrays
size = means.length().get([0])
total = counts.reduce(ee.Reducer.sum(), [0]).get([0])
sum = means.multiply(counts).reduce(ee.Reducer.sum(), [0]) \
.get([0])
mean = sum.divide(total)
# Compute between sum of squares, where each mean partitions the data.
indices = ee.List.sequence(1, size)
def func_rhf(i):
aCounts = counts.slice(0, 0, i)
aCount = aCounts.reduce(ee.Reducer.sum(), [0]) \
.get([0])
aMeans = means.slice(0, 0, i)
aMean = aMeans.multiply(aCounts) \
.reduce(ee.Reducer.sum(), [0]).get([0]) \
.divide(aCount)
bCount = total.subtract(aCount)
bMean = sum.subtract(aCount.multiply(aMean)) \
.divide(bCount)
return aCount.multiply(aMean.subtract(mean).pow(2)) \
.add(
bCount.multiply(bMean.subtract(mean).pow(2)))
bss = indices.map(func_rhf)
# Return the mean value corresponding to the maximum BSS.
return means.sort(bss).get([-1])
# Apply otsu thresholding.
globalThreshold = otsu(globalHistogram)
print('Global threshold value:', globalThreshold)
# Create list of empty strings that will be used for annotation.
thresholdCol = ee.List.repeat('', x.length())
# Find the index where the bucketMean equals the threshold.
threshIndex = x.indexOf(globalThreshold)
# Set the index to the annotation text.
thresholdCol = thresholdCol.set(threshIndex, 'Otsu Threshold')
# Redefine the column header information with annotation column.
columnHeader = ee.List([
[
{
'label': 'Backscatter',
'role': 'domain',
'type': 'number'
},
{
'label': 'Values',
'role': 'data',
'type': 'number'
},
{
'label': 'Threshold',
'role': 'annotation',
'type': 'string'
}]
])
# Loop through the data rows and add the annotation column.
i) {
i = ee.Number(i)
row = ee.List(dataCol.get(i))
return row.add(ee.String(thresholdCol.get(i)))
})
# Concat the header and data for plotting.
dataTable = columnHeader.cat(dataCol)
# Create plot using the ui.Chart function with the dataTable.
# Use 'evaluate' to transfer the server-side table to the client.
# Define the chart and print it to the console.
dataTable.evaluate(function(dataTableClient) {
# loop through the client-side table and set empty strings to None
for i in range(0, dataTableClient.length, 1):
if (dataTableClient[i][2] === '') {
dataTableClient[i][2] = None
}
chart = ui.Chart(dataTableClient) \
.setChartType('AreaChart') \
.setOptions({
'title': band + \
' Global Histogram with Threshold annotation',
'hAxis': {
'title': 'Backscatter [dB]',
'viewWindow': {
'min': -35,
'max': 15
}
},
'vAxis': {
'title': 'Count'
},
'annotations': {
'style': 'line'
}
})
print(chart)
})
# Apply the threshold on the image to extract water.
globalWater = s1Image.select(band).lt(globalThreshold)
# Add the water image to the map and mask 0 (no-water) values.
Map.addLayer(globalWater.selfMask(),
{
'palette': 'blue'
},
'Water (global threshold)')
# -----------------------------------------------------------------------
# CHECKPOINT
# -----------------------------------------------------------------------
# Add Earth Engine dataset
image = ee.Image("USGS/SRTMGL1_003")
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Chapter: A2.3 Surface Water Mapping
# Checkpoint: A23a
# Authors: K. Markert, G. Donchyts, A. Haag
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# *** Section 1 ***
# Define a point in Cambodia to filter by location.
point = ee.Geometry.Point(104.9632, 11.7686)
Map.centerObject(point, 11)
# Get the Sentinel-1 collection and filter by space/time.
s1Collection = ee.ImageCollection('COPERNICUS/S1_GRD') \
.filterBounds(point) \
.filterDate('2019-10-05', '2019-10-06') \
.filter(ee.Filter.eq('orbitProperties_pass', 'ASCENDING')) \
.filter(ee.Filter.eq('instrumentMode', 'IW'))
# Grab the first image in the collection.
s1Image = s1Collection.first()
# Add the Sentinel-1 image to the map.
Map.addLayer(s1Image, {
'min': -25,
'max': 0,
'bands': 'VV'
}, 'Sentinel-1 image')
# Specify band to use for Otsu thresholding.
band = 'VV'
# Define a reducer to calculate a histogram of values.
histogramReducer = ee.Reducer.histogram(255, 0.1)
# Reduce all of the image values.
globalHistogram = ee.Dictionary(
s1Image.select(band).reduceRegion({
'reducer': histogramReducer,
'geometry': s1Image.geometry(),
'scale': 90,
'maxPixels': 1e10
}).get(band)
)
# Extract out the histogram buckets and counts per bucket.
x = ee.List(globalHistogram.get('bucketMeans'))
y = ee.List(globalHistogram.get('histogram'))
# Define a list of values to plot.
dataCol = ee.Array.cat([x, y], 1).toList()
# Define the header information for data.
columnHeader = ee.List([
[
{
'label': 'Backscatter',
'role': 'domain',
'type': 'number'
},
{
'label': 'Values',
'role': 'data',
'type': 'number'
}, ]
])
# Concat the header and data for plotting.
dataTable = columnHeader.cat(dataCol)
# Create plot using the ui.Chart function with the dataTable.
# Use 'evaluate' to transfer the server-side table to the client.
# Define the chart and print it to the console.
dataTable.evaluate(function(dataTableClient) {
chart = ui.Chart(dataTableClient) \
.setChartType('AreaChart') \
.setOptions({
'title': band + ' Global Histogram',
'hAxis': {
'title': 'Backscatter [dB]',
'viewWindow': {
'min': -35,
'max': 15
}
},
'vAxis': {
'title': 'Count'
}
})
print(chart)
})
# See:
# https:#medium.com/google-earth/otsus-method-for-image-segmentation-f5c48f405e
def otsu(histogram):
# Make sure histogram is an ee.Dictionary object.
histogram = ee.Dictionary(histogram)
# Extract relevant values into arrays.
counts = ee.Array(histogram.get('histogram'))
means = ee.Array(histogram.get('bucketMeans'))
# Calculate single statistics over arrays
size = means.length().get([0])
total = counts.reduce(ee.Reducer.sum(), [0]).get([0])
sum = means.multiply(counts).reduce(ee.Reducer.sum(), [0]) \
.get([0])
mean = sum.divide(total)
# Compute between sum of squares, where each mean partitions the data.
indices = ee.List.sequence(1, size)
def func_rhf(i):
aCounts = counts.slice(0, 0, i)
aCount = aCounts.reduce(ee.Reducer.sum(), [0]) \
.get([0])
aMeans = means.slice(0, 0, i)
aMean = aMeans.multiply(aCounts) \
.reduce(ee.Reducer.sum(), [0]).get([0]) \
.divide(aCount)
bCount = total.subtract(aCount)
bMean = sum.subtract(aCount.multiply(aMean)) \
.divide(bCount)
return aCount.multiply(aMean.subtract(mean).pow(2)) \
.add(
bCount.multiply(bMean.subtract(mean).pow(2)))
bss = indices.map(func_rhf)
# Return the mean value corresponding to the maximum BSS.
return means.sort(bss).get([-1])
# Apply otsu thresholding.
globalThreshold = otsu(globalHistogram)
print('Global threshold value:', globalThreshold)
# Create list of empty strings that will be used for annotation.
thresholdCol = ee.List.repeat('', x.length())
# Find the index where the bucketMean equals the threshold.
threshIndex = x.indexOf(globalThreshold)
# Set the index to the annotation text.
thresholdCol = thresholdCol.set(threshIndex, 'Otsu Threshold')
# Redefine the column header information with annotation column.
columnHeader = ee.List([
[
{
'label': 'Backscatter',
'role': 'domain',
'type': 'number'
},
{
'label': 'Values',
'role': 'data',
'type': 'number'
},
{
'label': 'Threshold',
'role': 'annotation',
'type': 'string'
}]
])
# Loop through the data rows and add the annotation column.
i) {
i = ee.Number(i)
row = ee.List(dataCol.get(i))
return row.add(ee.String(thresholdCol.get(i)))
})
# Concat the header and data for plotting.
dataTable = columnHeader.cat(dataCol)
# Create plot using the ui.Chart function with the dataTable.
# Use 'evaluate' to transfer the server-side table to the client.
# Define the chart and print it to the console.
dataTable.evaluate(function(dataTableClient) {
# loop through the client-side table and set empty strings to None
for i in range(0, dataTableClient.length, 1):
if (dataTableClient[i][2] === '') {
dataTableClient[i][2] = None
}
chart = ui.Chart(dataTableClient) \
.setChartType('AreaChart') \
.setOptions({
'title': band + \
' Global Histogram with Threshold annotation',
'hAxis': {
'title': 'Backscatter [dB]',
'viewWindow': {
'min': -35,
'max': 15
}
},
'vAxis': {
'title': 'Count'
},
'annotations': {
'style': 'line'
}
})
print(chart)
})
# Apply the threshold on the image to extract water.
globalWater = s1Image.select(band).lt(globalThreshold)
# Add the water image to the map and mask 0 (no-water) values.
Map.addLayer(globalWater.selfMask(),
{
'palette': 'blue'
},
'Water (global threshold)')
# -----------------------------------------------------------------------
# CHECKPOINT
# -----------------------------------------------------------------------
Display the interactive map¶
In [ ]:
Copied!
Map
Map