11. Earth Engine Applications#
11.1. Introduction#
11.2. Technical requirements#
conda create -n gee python
conda activate gee
conda install -c conda-forge mamba
mamba install -c conda-forge pygis
jupyter lab
# %pip install pygis
import ee
import geemap
geemap.ee_initialize()
11.3. Analyzing surface water dynamics#
11.3.1. Surface water occurrence#
dataset = ee.Image('JRC/GSW1_4/GlobalSurfaceWater')
dataset.bandNames()
Map = geemap.Map()
Map.add_basemap('HYBRID')
Map
image = dataset.select(['occurrence'])
region = Map.user_roi # Draw a polygon on the map
if region is None:
region = ee.Geometry.BBox(-99.957, 46.8947, -99.278, 47.1531)
vis_params = {'min': 0.0, 'max': 100.0, 'palette': ['ffffff', 'ffbbbb', '0000ff']}
Map.addLayer(image, vis_params, 'Occurrence')
Map.addLayer(region, {}, 'ROI', True, 0.5)
Map.centerObject(region)
Map.add_colorbar(vis_params, label='Water occurrence (%)', layer_name='Occurrence')
df = geemap.image_histogram(
image,
region,
scale=30,
return_df=True,
)
df
hist = geemap.image_histogram(
image,
region,
scale=30,
x_label='Water Occurrence (%)',
y_label='Pixel Count',
title='Surface Water Occurrence',
layout_args={'title': dict(x=0.5)},
return_df=False,
)
hist
hist.update_layout(
autosize=False,
width=800,
height=400,
margin=dict(l=30, r=20, b=10, t=50, pad=4)
)
hist.write_image('water_occurrence.jpg', scale=2)
11.3.2. Surface water monthly history#
dataset = ee.ImageCollection('JRC/GSW1_4/MonthlyHistory')
dataset.size()
dataset.aggregate_array("system:index")
Map = geemap.Map()
Map
image = dataset.filterDate('2020-08-01', '2020-09-01').first()
region = Map.user_roi # Draw a polygon on the map
if region is None:
region = ee.Geometry.BBox(-99.957, 46.8947, -99.278, 47.1531)
vis_params = {'min': 0.0, 'max': 2.0, 'palette': ['ffffff', 'fffcb8', '0905ff']}
Map.addLayer(image, vis_params, 'Water')
Map.addLayer(region, {}, 'ROI', True, 0.5)
Map.centerObject(region)
geemap.jrc_hist_monthly_history(
region=region, scale=30, frequency='month', denominator=1e4, y_label='Area (ha)'
)
geemap.jrc_hist_monthly_history(
region=region,
start_month=6,
end_month=9,
scale=30,
frequency='month',
denominator=1e4,
y_label='Area (ha)',
)
geemap.jrc_hist_monthly_history(
region=region,
start_month=6,
end_month=9,
scale=30,
frequency='year',
reducer='mean',
denominator=1e4,
y_label='Area (ha)',
)
geemap.jrc_hist_monthly_history(
region=region,
start_month=6,
end_month=9,
scale=30,
frequency='year',
reducer='max',
denominator=1e4,
y_label='Area (ha)',
)
11.4. Mapping flood extents#
11.4.1. Create an interactive map#
Map = geemap.Map(center=[29.3055, 68.9062], zoom=6)
Map
11.4.2. Search datasets#
country_name = 'Pakistan'
pre_flood_start_date = '2021-08-01'
pre_flood_end_date = '2021-09-30'
flood_start_date = '2022-08-01'
flood_end_date = '2022-09-30'
11.4.3. Visualize datasets#
country = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017').filter(
ee.Filter.eq('country_na', country_name)
)
style = {'color': 'black', 'fillColor': '00000000'}
Map.addLayer(country.style(**style), {}, country_name)
Map.centerObject(country)
Map
11.4.4. Create Landsat composites#
landsat_col_2021 = (
ee.ImageCollection('LANDSAT/LC08/C02/T1')
.filterDate(pre_flood_start_date, pre_flood_end_date)
.filterBounds(country)
)
landsat_2021 = ee.Algorithms.Landsat.simpleComposite(landsat_col_2021).clipToCollection(
country
)
vis_params = {'bands': ['B6', 'B5', 'B4'], 'max': 128}
Map.addLayer(landsat_2021, vis_params, 'Landsat 2021')
landsat_col_2022 = (
ee.ImageCollection('LANDSAT/LC08/C02/T1')
.filterDate(flood_start_date, flood_end_date)
.filterBounds(country)
)
landsat_2022 = ee.Algorithms.Landsat.simpleComposite(landsat_col_2022).clipToCollection(
country
)
Map.addLayer(landsat_2022, vis_params, 'Landsat 2022')
Map.centerObject(country)
Map
11.4.5. Compare Landsat composites side by side#
Map = geemap.Map()
Map.setCenter(68.4338, 26.4213, 7)
left_layer = geemap.ee_tile_layer(
landsat_2021, vis_params, 'Landsat 2021'
)
right_layer = geemap.ee_tile_layer(
landsat_2022, vis_params, 'Landsat 2022'
)
Map.split_map(
left_layer, right_layer, left_label='Landsat 2021', right_label='Landsat 2022'
)
Map.addLayer(country.style(**style), {}, country_name)
Map
11.4.6. Compute Normalized Difference Water Index (NDWI)#
ndwi_2021 = landsat_2021.normalizedDifference(['B3', 'B5']).rename('NDWI')
ndwi_2022 = landsat_2022.normalizedDifference(['B3', 'B5']).rename('NDWI')
Map = geemap.Map()
Map.setCenter(68.4338, 26.4213, 7)
ndwi_vis = {'min': -1, 'max': 1, 'palette': 'ndwi'}
left_layer = geemap.ee_tile_layer(ndwi_2021, ndwi_vis, 'NDWI 2021')
right_layer = geemap.ee_tile_layer(ndwi_2022, ndwi_vis, 'NDWI 2022')
Map.split_map(left_layer, right_layer, left_label='NDWI 2021', right_label='NDWI 2022')
Map.addLayer(country.style(**style), {}, country_name)
Map
11.4.7. Extract Landsat water extent#
threshold = 0.1
water_2021 = ndwi_2021.gt(threshold).selfMask()
water_2022 = ndwi_2022.gt(threshold).selfMask()
Map = geemap.Map()
Map.setCenter(68.4338, 26.4213, 7)
Map.addLayer(landsat_2021, vis_params, 'Landsat 2021', False)
Map.addLayer(landsat_2022, vis_params, 'Landsat 2022', False)
left_layer = geemap.ee_tile_layer(
water_2021, {'palette': 'blue'}, 'Water 2021'
)
right_layer = geemap.ee_tile_layer(
water_2022, {'palette': 'red'}, 'Water 2022'
)
Map.split_map(
left_layer, right_layer, left_label='Water 2021', right_label='Water 2022'
)
Map.addLayer(country.style(**style), {}, country_name)
Map
11.4.8. Extract Landsat flood extent#
flood_extent = water_2022.unmask().subtract(water_2021.unmask()).gt(0).selfMask()
Map = geemap.Map()
Map.setCenter(68.4338, 26.4213, 7)
Map.addLayer(landsat_2021, vis_params, 'Landsat 2021', False)
Map.addLayer(landsat_2022, vis_params, 'Landsat 2022', False)
left_layer = geemap.ee_tile_layer(
water_2021, {'palette': 'blue'}, 'Water 2021'
)
right_layer = geemap.ee_tile_layer(
water_2022, {'palette': 'red'}, 'Water 2022'
)
Map.split_map(
left_layer, right_layer, left_label='Water 2021', right_label='Water 2022'
)
Map.addLayer(flood_extent, {'palette': 'cyan'}, 'Flood Extent')
Map.addLayer(country.style(**style), {}, country_name)
Map
11.4.9. Calculate Landsat flood area#
area_2021 = geemap.zonal_stats(
water_2021, country, scale=1000, statistics_type='SUM', return_fc=True
)
geemap.ee_to_df(area_2021)
area_2022 = geemap.zonal_stats(
water_2022, country, scale=1000, statistics_type='SUM', return_fc=True
)
geemap.ee_to_df(area_2022)
flood_area = geemap.zonal_stats(
flood_extent, country, scale=1000, statistics_type='SUM', return_fc=True
)
geemap.ee_to_df(flood_area)
11.4.10. Create Sentinel-1 SAR composites#
s1_col_2021 = (
ee.ImageCollection('COPERNICUS/S1_GRD')
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV'))
.filter(ee.Filter.eq('instrumentMode', 'IW'))
.filter(ee.Filter.eq('orbitProperties_pass', 'ASCENDING'))
.filterDate(pre_flood_start_date, pre_flood_end_date)
.filterBounds(country)
.select('VV')
)
s1_col_2021
s1_col_2022 = (
ee.ImageCollection('COPERNICUS/S1_GRD')
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV'))
.filter(ee.Filter.eq('instrumentMode', 'IW'))
.filter(ee.Filter.eq('orbitProperties_pass', 'ASCENDING'))
.filterDate(flood_start_date, flood_end_date)
.filterBounds(country)
.select('VV')
)
s1_col_2022
Map = geemap.Map()
Map.add_basemap('HYBRID')
sar_2021 = s1_col_2021.reduce(ee.Reducer.percentile([20])).clipToCollection(country)
sar_2022 = s1_col_2022.reduce(ee.Reducer.percentile([20])).clipToCollection(country)
Map.addLayer(sar_2021, {'min': -25, 'max': -5}, 'SAR 2021')
Map.addLayer(sar_2022, {'min': -25, 'max': -5}, 'SAR 2022')
Map.centerObject(country)
Map
11.4.11. Apply speckle filtering#
col_2021 = s1_col_2021.map(lambda img: img.focal_median(100, 'circle', 'meters'))
col_2022 = s1_col_2022.map(lambda img: img.focal_median(100, 'circle', 'meters'))
Map = geemap.Map()
Map.add_basemap('HYBRID')
sar_2021 = col_2021.reduce(ee.Reducer.percentile([20])).clipToCollection(country)
sar_2022 = col_2022.reduce(ee.Reducer.percentile([20])).clipToCollection(country)
Map.addLayer(sar_2021, {'min': -25, 'max': -5}, 'SAR 2021')
Map.addLayer(sar_2022, {'min': -25, 'max': -5}, 'SAR 2022')
Map.centerObject(country)
Map
11.4.12. Compare Sentinel-1 SAR composites side by side#
Map = geemap.Map()
Map.setCenter(68.4338, 26.4213, 7)
left_layer = geemap.ee_tile_layer(sar_2021, {'min': -25, 'max': -5}, 'SAR 2021')
right_layer = geemap.ee_tile_layer(sar_2022, {'min': -25, 'max': -5}, 'SAR 2022')
Map.split_map(
left_layer, right_layer, left_label='Sentinel-1 2021', right_label='Sentinel-1 2022'
)
Map.addLayer(country.style(**style), {}, country_name)
Map
11.4.13. Extract SAR water extent#
threshold = -18
water_2021 = sar_2021.lt(threshold)
water_2022 = sar_2022.lt(threshold)
Map = geemap.Map()
Map.setCenter(68.4338, 26.4213, 7)
Map.addLayer(sar_2021, {'min': -25, 'max': -5}, 'SAR 2021')
Map.addLayer(sar_2022, {'min': -25, 'max': -5}, 'SAR 2022')
left_layer = geemap.ee_tile_layer(
water_2021.selfMask(), {'palette': 'blue'}, 'Water 2021'
)
right_layer = geemap.ee_tile_layer(
water_2022.selfMask(), {'palette': 'red'}, 'Water 2022'
)
Map.split_map(
left_layer, right_layer, left_label='Water 2021', right_label='Water 2022'
)
Map.addLayer(country.style(**style), {}, country_name)
Map
11.4.14. Extract SAR flood extent#
flood_extent = water_2022.unmask().subtract(water_2021.unmask()).gt(0).selfMask()
Map = geemap.Map()
Map.setCenter(68.4338, 26.4213, 7)
Map.addLayer(sar_2021, {'min': -25, 'max': -5}, 'SAR 2021')
Map.addLayer(sar_2022, {'min': -25, 'max': -5}, 'SAR 2022')
left_layer = geemap.ee_tile_layer(
water_2021.selfMask(), {'palette': 'blue'}, 'Water 2021'
)
right_layer = geemap.ee_tile_layer(
water_2022.selfMask(), {'palette': 'red'}, 'Water 2022'
)
Map.split_map(
left_layer, right_layer, left_label='Water 2021', right_label='Water 2022'
)
Map.addLayer(flood_extent, {'palette': 'cyan'}, 'Flood Extent')
Map.addLayer(country.style(**style), {}, country_name)
Map
11.4.15. Calculate SAR flood area#
area_2021 = geemap.zonal_stats(
water_2021, country, scale=1000, statistics_type='SUM', return_fc=True
)
geemap.ee_to_df(area_2021)
area_2022 = geemap.zonal_stats(
water_2022, country, scale=1000, statistics_type='SUM', return_fc=True
)
geemap.ee_to_df(area_2022)
flood_area = geemap.zonal_stats(
flood_extent, country, scale=1000, statistics_type='SUM', return_fc=True
)
geemap.ee_to_df(flood_area)
11.5. Forest cover change analysis#
11.5.1. Forest cover mapping#
dataset = ee.Image('UMD/hansen/global_forest_change_2021_v1_9')
dataset.bandNames()
Map = geemap.Map()
first_bands = ['first_b50', 'first_b40', 'first_b30']
first_image = dataset.select(first_bands)
Map.addLayer(first_image, {'bands': first_bands, 'gamma': 1.5}, 'Year 2000 Bands 5/4/3')
last_bands = ['last_b50', 'last_b40', 'last_b30']
last_image = dataset.select(last_bands)
Map.addLayer(last_image, {'bands': last_bands, 'gamma': 1.5}, 'Year 2021 Bands 5/4/3')
treecover = dataset.select(['treecover2000'])
treeCoverVisParam = {'min': 0, 'max': 100, 'palette': ['black', 'green']}
name = 'Tree cover (%)'
Map.addLayer(treecover, treeCoverVisParam, name)
Map.add_colorbar(treeCoverVisParam, label=name, layer_name=name)
Map
threshold = 10
treecover_bin = treecover.gte(threshold).selfMask()
treeVisParam = {'palette': ['green']}
Map.addLayer(treecover_bin, treeVisParam, 'Tree cover bin')
11.5.2. Forest loss and gain mapping#
Map = geemap.Map()
treeloss_year = dataset.select(['lossyear'])
treeLossVisParam = {'min': 0, 'max': 21, 'palette': ['yellow', 'red']}
layer_name = 'Tree loss year'
Map.addLayer(treeloss_year, treeLossVisParam, layer_name)
Map.add_colorbar(treeLossVisParam, label=layer_name, layer_name=layer_name)
Map
treeloss = dataset.select(['loss']).selfMask()
treegain = dataset.select(['gain']).selfMask()
Map.addLayer(treeloss, {'palette': 'red'}, 'Tree loss')
Map.addLayer(treegain, {'palette': 'yellow'}, 'Tree gain')
Map
11.5.3. Zonal statistics by country#
Map = geemap.Map()
countries = ee.FeatureCollection(geemap.examples.get_ee_path('countries'))
style = {'color': '#000000ff', 'fillColor': '#00000000'}
Map.addLayer(countries.style(**style), {}, 'Countries')
Map
geemap.zonal_stats_by_group(
treecover_bin,
countries,
'forest_cover.csv',
statistics_type='SUM',
denominator=1e6,
scale=1000,
)
geemap.pie_chart(
'forest_cover.csv', names='NAME', values='Class_sum', max_rows=20, height=400
)
geemap.bar_chart(
'forest_cover.csv',
x='NAME',
y='Class_sum',
max_rows=20,
x_label='Country',
y_label='Forest area (km2)',
)
geemap.zonal_stats_by_group(
treeloss,
countries,
'treeloss.csv',
statistics_type='SUM',
denominator=1e6,
scale=1000,
)
geemap.pie_chart(
'treeloss.csv', names='NAME', values='Class_sum', max_rows=20, height=600
)
geemap.bar_chart(
'treeloss.csv',
x='NAME',
y='Class_sum',
max_rows=20,
x_label='Country',
y_label='Forest loss area (km2)',
)
11.6. Global land cover mapping#
11.6.1. Dynamic World#
Map = geemap.Map()
region = ee.Geometry.BBox(-89.7088, 42.9006, -89.0647, 43.2167)
start_date = '2021-01-01'
end_date = '2022-01-01'
image = geemap.dynamic_world_s2(region, start_date, end_date, clip=True)
vis_params = {'bands': ['B4', 'B3', 'B2'], 'min': 0, 'max': 3000}
Map.addLayer(image, vis_params, 'Sentinel-2 image')
landcover = geemap.dynamic_world(
region, start_date, end_date, return_type='hillshade', clip=True
)
Map.addLayer(landcover, {}, 'Land Cover')
Map.add_legend(
title="Dynamic World Land Cover",
builtin_legend='Dynamic_World',
layer_name='Land Cover',
)
Map.centerObject(region, 13)
Map
classes = geemap.dynamic_world(
region, start_date, end_date, return_type='class', clip=True
)
vis_params = {
"min": 0,
"max": 8,
"palette": [
"#419BDF",
"#397D49",
"#88B053",
"#7A87C6",
"#E49635",
"#DFC35A",
"#C4281B",
"#A59B8F",
"#B39FE1",
],
}
Map.addLayer(classes, vis_params, 'Class')
probability = geemap.dynamic_world(
region, start_date, end_date, return_type='visualize', clip=True
)
Map.addLayer(probability, {}, 'Visualize')
probability = geemap.dynamic_world(
region, start_date, end_date, return_type='probability', clip=True
)
Map.addLayer(probability, {}, 'Probability')
df = geemap.image_area_by_group(classes, region=region, scale=10, denominator=1e6)
df
Map = geemap.Map()
region = ee.Geometry.BBox(-89.7088, 42.9006, -89.0647, 43.2167)
start_date = '2017-01-01'
end_date = '2022-12-31'
images = geemap.dynamic_world_timeseries(
region, start_date, end_date, frequency='year', return_type="hillshade"
)
Map.ts_inspector(images, date_format='YYYY')
Map.add_legend(title="Dynamic World Land Cover", builtin_legend='Dynamic_World')
Map.centerObject(region)
Map
11.6.2. ESA WorldCover#
Map = geemap.Map()
esa_2020 = ee.ImageCollection("ESA/WorldCover/v100").first()
esa_2021 = ee.ImageCollection("ESA/WorldCover/v200").first()
Map.addLayer(esa_2020, {'bands': ['Map']}, 'ESA 2020')
Map.addLayer(esa_2021, {'bands': ['Map']}, 'ESA 2021')
Map.add_legend(builtin_legend='ESA_WorldCover', title='ESA Land Cover')
Map
df = geemap.image_area_by_group(
esa_2021, scale=1000, denominator=1e6, decimal_places=4, verbose=True
)
df
df.to_csv('esa_2021.csv')
Map = geemap.Map(center=[43.0006, -88.9088], zoom=12)
# Create Dynamic World Land Cover 2021
region = ee.Geometry.BBox(-179, -89, 179, 89)
start_date = '2021-01-01'
end_date = '2022-01-01'
dw_2021 = geemap.dynamic_world(region, start_date, end_date, return_type='hillshade')
# Create ESA WorldCover 2021
esa_2021 = ee.ImageCollection("ESA/WorldCover/v200").first()
# Create a split map
left_layer = geemap.ee_tile_layer(esa_2021, {'bands': ['Map']}, "ESA 2021")
right_layer = geemap.ee_tile_layer(dw_2021, {}, "Dynamic World 2021")
Map.split_map(left_layer, right_layer)
# Add legends
Map.add_legend(
title="ESA WorldCover", builtin_legend='ESA_WorldCover', position='bottomleft'
)
Map.add_legend(
title="Dynamic World Land Cover",
builtin_legend='Dynamic_World',
position='bottomright',
)
Map
11.6.3. Esri global land cover#
def esri_annual_land_cover(year):
collection = ee.ImageCollection('projects/sat-io/open-datasets/landcover/ESRI_Global-LULC_10m_TS')
start_date = ee.Date.fromYMD(year, 1, 1)
end_date = start_date.advance(1, 'year')
image = collection.filterDate(start_date, end_date).mosaic()
return image.set('system:time_start', start_date.millis())
start_year = 2017
end_year = 2021
years = ee.List.sequence(start_year, end_year)
images = ee.ImageCollection(years.map(esri_annual_land_cover))
images
palette = [
"#1A5BAB",
"#358221",
"#000000",
"#87D19E",
"#FFDB5C",
"#000000",
"#ED022A",
"#EDE9E4",
"#F2FAFF",
"#C8C8C8",
"#C6AD8D",
]
vis_params = {"min": 1, "max": 11, "palette": palette}
Map = geemap.Map()
Map.ts_inspector(images, left_vis=vis_params, date_format='YYYY')
Map.add_legend(title="Esri Land Cover", builtin_legend='ESRI_LandCover')
Map
Map = geemap.Map(center=[43.0006, -88.9088], zoom=12)
# Create Dynamic World Land Cover 2021
dw_2021 = geemap.dynamic_world(region, start_date, end_date, return_type='hillshade')
# Create Esri Global Land Cover 2021
esri_2021 = esri_annual_land_cover(2021)
# Create a split map
left_layer = geemap.ee_tile_layer(esri_2021, vis_params, "Esri 2021")
right_layer = geemap.ee_tile_layer(dw_2021, {}, "Dynamic World 2021")
Map.split_map(left_layer, right_layer)
# Add legends
Map.add_legend(
title="Esri Land Cover", builtin_legend='ESRI_LandCover', position='bottomleft'
)
Map.add_legend(
title="Dynamic World Land Cover",
builtin_legend='Dynamic_World',
position='bottomright',
)
Map