# Import modules and initialize
import ee
import json
import folium
import numpy as np
from pprint import pprint
from matplotlib import colormaps, colors
import requests
import xarray as xr
import matplotlib.pyplot as plt
import geopandas as gpd
14 Supervised classification
Supervised learning techniques, like the Random Forest algorithm, offer a robust framework for classifying complex landscapes, such as distinguishing between riparian and grassland areas within a watershed. This method relies on manually selected and labeled training areas to teach the model how to recognize these distinct environments. This tutorial focuses on using satellite imagery and Random Forest classification in Google Earth Engine (GEE) to accurately identify and classify riparian versus grassland regions in a watershed.
# Authenticate
#ee.Authenticate()
# Initialize the library.
ee.Initialize()
Define helper functions
# Create helper functions
# Get Color for different polygons
def get_polygon_color(polygon):
= ['#7b3294', '#008837']
color_values = color_values[polygon['properties']['label']]
color return {'fillColor':color, 'fillOpacity':0.8}
def maskS2clouds(image):
"""
Function to filter images by cloud percentage
"""
= image.select('QA60')
qa = 1 << 10 # bits 10
cloudBitMask = 1 << 11 # bits 11
cirrusBitMask
# Both flags should be set to zero (clear sky conditions)
= qa.bitwiseAnd(cloudBitMask).eq(0).And(qa.bitwiseAnd(cirrusBitMask).eq(0))
mask return image.updateMask(mask)
# Define auxiliary functions
def create_raster(ee_object, vis_params, name):
"""
Function that createst GEE map into raster for folium map
"""
= folium.raster_layers.TileLayer(ee_object.getMapId(vis_params)['tile_fetcher'].url_format,
raster =name,
name=True,
overlay=True,
control='Map Data © <a href="https://earthengine.google.com/">Google Earth Engine</a>')
attrreturn raster
Load region boundaries
# Import boundary for region of interest (roi)
with open('../datasets/kings_creek.geojson') as file:
= json.load(file)
roi_json
# Define the ee.Geometry
= ee.Geometry(roi_json['features'][0]['geometry']) roi_geom
Load labeled regions for training
These polygons were created manually by inspecting a satellite image and drawing over small portions of the watershed that were clearly with riparian and grassland vegetation. Here is an excellent tool to get you started: https://geojson.io
# Import labeled regions for riparian and grassland areas.
with open('../datasets/riparian.geojson') as file:
= json.load(file)
riparian_json
with open('../datasets/grassland.geojson') as file:
= json.load(file)
grassland_json
# Define the ee.Geometry for each polygon
= ee.Feature(riparian_json['features'][0]['geometry'])
riparian_geom = ee.Feature(grassland_json['features'][0]['geometry']) grassland_geom
# Add class values to features
= riparian_geom.set('land_cover', 0)
riparian_geom = grassland_geom.set('land_cover', 1) grassland_geom
# Merge train dataset into a FeatureCollection
= ee.FeatureCollection([riparian_geom, grassland_geom]) training_regions
Load image dataset
This image will be used to detect riparian and grassland vegetation beyond the provided training regions.
# Sentinel 2 multispectral instrument
= ee.ImageCollection('COPERNICUS/S2_SR').filterDate('2022-06-01', '2022-07-31')
S2 = S2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE',10)).map(maskS2clouds)
S2
# Mean multispectral image for entire period
= S2.mean().divide(10000).clip(roi_geom) img
# Select values from dataset for the training regions
= img.sampleRegions(collection=training_regions,
training_data =['land_cover'], scale=30) properties
# Train Random Forest algorithm
= 10
ntrees = ee.Classifier.smileRandomForest(ntrees).train(features=training_data,
trained_classifier ='land_cover',
classProperty=img.bandNames()) inputProperties
# Classify the entire King's Creek Watershed
= img.classify(trained_classifier)
classified_img = classified_img.reduceToVectors(scale=30,
classified_vector =roi_geom).getInfo() geometry
Save static maps
Download vector and raster maps.
# Save vector map to GeoJSON file
= '../outputs/classified_vector_kings_creek.tif'
filename_classified_vector with open(filename_classified_vector, 'w') as file:
# Convert dictionary to a GeoJSON string and save it
file.write(json.dumps(classified_vector))
# Create raster of classified image in geoTIFF image
= classified_img.getDownloadUrl({
classified_img_url 'region': roi_geom,
'scale':30,
'crs': 'EPSG:4326',
'format': 'GEO_TIFF'
})
# Request and save geoTIFF map
= requests.get(classified_img_url)
response = '../outputs/classified_kings_creek.tif'
filename_classified_img with open(filename_classified_img, 'wb') as f:
f.write(response.content)
# Create raster of classified image in geoTIFF image
= img.getDownloadUrl({
truecolor_img_url 'region': roi_geom,
'scale':30,
'crs': 'EPSG:4326',
'format': 'GEO_TIFF',
'bands':['B4', 'B3', 'B2']
})
# Request and save geoTIFF map
= requests.get(truecolor_img_url)
response = '../outputs/truecolor_kings_creek.tif'
filename_truecolor_img with open(filename_truecolor_img, 'wb') as f:
f.write(response.content)
Create static figure
# Read GeoTiff file using the Xarray package
= xr.open_dataarray(filename_classified_img).squeeze() # 2d image
raster_classified = xr.open_dataarray(filename_truecolor_img)
raster_truecolor
# COnvert vector layer to GeoDataframe
= gpd.GeoDataFrame.from_features(classified_vector['features'], crs=4326)
gdf 3) gdf.head(
geometry | count | label | |
---|---|---|---|
0 | POLYGON ((-96.55425 39.09019, -96.55344 39.090... | 3 | 0 |
1 | POLYGON ((-96.55398 39.08776, -96.55398 39.087... | 77 | 0 |
2 | POLYGON ((-96.55452 39.08965, -96.55452 39.089... | 5 | 0 |
= plt.subplots(nrows=1, ncols=2, figsize=(8,4))
fig,(ax1,ax2) =0.35)
fig.subplots_adjust(wspace
=ax1, cmap='Set1', add_colorbar=False);
raster_classified.plot.imshow(ax'Classified image')
ax1.set_title('equal')
ax1.axis(
=ax2);
raster_truecolor.plot.imshow(ax'True color image')
ax2.set_title('equal')
ax2.axis(
= gdf['label'] == 0 # select rows for riparian areas
idx_riparian =ax2, facecolor=(0.9,0.2,0,0.75),
gdf.loc[idx_riparian].plot(ax='k', linewidth=0.5)
edgecolor
plt.show()
Interactive map
# Create Folium Map
= folium.Map(location=[39.09, -96.592], zoom_start=13)
m
# Create vector layer of classified polygons
= folium.GeoJson(classified_vector,
cluster_layer ="Classified image with RF",
name=get_polygon_color)
style_function
# Visualization parameters of true color image
# B4=Red, B3=Green, B2=Blue
= {'min': 0.0, 'max': 0.3, 'bands': ['B4', 'B3', 'B2'], }
vis_params
# Create raster layer of true color image
= folium.raster_layers.TileLayer(img.getMapId(vis_params)['tile_fetcher'].url_format,
true_color_layer ='True color',
name=True,
overlay=True,
control='Map Data © <a href="https://earthengine.google.com/">Google Earth Engine</a>')
attr
# Add layers to interactive map
true_color_layer.add_to(m)
cluster_layer.add_to(m)
# Add map controls to be able to compare
# the true color image with the clasified layer
folium.LayerControl().add_to(m)
# Render map
m