import ee
import colorcet
from matplotlib.cm import get_cmap
import geemap
from datetime import datetime, timedelta
Forest & Buildings, Removed DEM Error
ee.Authenticate()
='remotesensing-musa6500') ee.Initialize(project
= ee.Geometry.Polygon([
bbox
[-85.9, 8.0],
[-85.9, 11.2],
[-82.5, 11.2],
[-82.5, 8.0],
[-85.9, 8.0]
[
]
])
= '2021-07-22'
start_date = '2021-07-28'
end_date = datetime.strptime(start_date, "%Y-%m-%d")
start_date = datetime.strptime(end_date, "%Y-%m-%d") end_date
# Load the datasets
# with the FABDEM layer, the output probability raster 1) is missing tiles and
# 2) shows up semi-transparent in the geemap map for no apparent reason
= ee.ImageCollection("projects/sat-io/open-datasets/GLO-30").mosaic().clip(bbox)
dem
# with the hydrosheds DEM, however, this is not the issue
# (uncomment this line to replace the FABDEM layer)
# dem = ee.Image('WWF/HydroSHEDS/03VFDEM').clip(bbox)
= ee.Terrain.slope(dem)
slope = ee.Image("ESA/WorldCover/v100/2020").select('Map').clip(bbox) landcover
= ee.ImageCollection("projects/sat-io/open-datasets/HYDROGRAPHY90/stream-outlet-distance/stream_dist_proximity")\
stream_dist_proximity_collection \
.filterBounds(bbox)
.mosaic()= stream_dist_proximity_collection.clip(bbox).rename('stream_distance')
stream_dist_proximity
= stream_dist_proximity.projection()
hydro_proj
## set time frame
= '2023-09-25'
before_start='2023-10-05'
before_end
='2023-10-05'
after_start='2023-10-15'
after_end
# SET SAR PARAMETERS (can be left default)
# Polarization (choose either "VH" or "VV")
= "VH" # or "VV"
polarization
# Pass direction (choose either "DESCENDING" or "ASCENDING")
= "DESCENDING" # or "ASCENDING"
pass_direction
# Difference threshold to be applied on the difference image (after flood - before flood)
# It has been chosen by trial and error. Adjust as needed.
= 1.25
difference_threshold
# Relative orbit (optional, if you know the relative orbit for your study area)
# relative_orbit = 79
# Rename the selected geometry feature
= bbox
aoi
# Load and filter Sentinel-1 GRD data by predefined parameters
= ee.ImageCollection('COPERNICUS/S1_GRD') \
collection filter(ee.Filter.eq('instrumentMode', 'IW')) \
.filter(ee.Filter.listContains('transmitterReceiverPolarisation', polarization)) \
.filter(ee.Filter.eq('orbitProperties_pass', pass_direction)) \
.filter(ee.Filter.eq('resolution_meters', 10)) \
.\
.filterBounds(aoi)
.select(polarization)
# Select images by predefined dates
= collection.filterDate(before_start, before_end)
before_collection = collection.filterDate(after_start, after_end)
after_collection
# Create a mosaic of selected tiles and clip to the study area
= before_collection.mosaic().clip(aoi)
before = after_collection.mosaic().clip(aoi)
after
# Apply radar speckle reduction by smoothing
= 50
smoothing_radius = before.focal_mean(smoothing_radius, 'circle', 'meters')
before_filtered = after.focal_mean(smoothing_radius, 'circle', 'meters')
after_filtered
# Calculate the difference between the before and after images
= after_filtered.divide(before_filtered)
difference
# Apply the predefined difference-threshold and create the flood extent mask
= difference_threshold
threshold = difference.gt(threshold)
difference_binary
# Refine the flood result using additional datasets
= ee.Image('JRC/GSW1_0/GlobalSurfaceWater').select('seasonality')
swater = swater.gte(10).updateMask(swater.gte(10))
swater_mask = difference_binary.where(swater_mask, 0)
flooded_mask = flooded_mask.updateMask(flooded_mask)
flooded = flooded.connectedPixelCount()
connections = flooded.updateMask(connections.gte(8)) flooded
# Mask out areas with more than 5 percent slope using a Digital Elevation Model
= ee.Algorithms.Terrain(dem)
terrain = flooded.updateMask(slope.lt(5))
flooded
# Set the default projection from the hydrography dataset
= flooded.setDefaultProjection(hydro_proj)
flooded
# Now, reduce the resolution
= flooded.reduceResolution(
flooded_mode =ee.Reducer.mode(),
reducer=10000
maxPixels
).reproject(=hydro_proj
crs )
# Create a full-area mask, initially marking everything as non-flooded (value 0)
= ee.Image.constant(0).clip(aoi)
full_area_mask
# Update the mask to mark flooded areas (value 1)
# Assuming flooded_mode is a binary image with 1 for flooded areas and 0 elsewhere
= full_area_mask.where(flooded, 1)
flood_labeled_image
# Now flood_labeled_image contains 1 for flooded areas and 0 for non-flooded areas
= (dem.rename("elevation")
combined 'Map').rename("landcover"))
.addBands(landcover.select(
.addBands(slope)"flooded_mask"))
.addBands(flood_labeled_image.rename( )
# Get all band names from the combined image
= combined.bandNames()
allBandNames
# Remove the class band name ('flooded_full_mask') to get input properties
= allBandNames.filter(ee.Filter.neq('item', 'flooded_mask'))
inputProperties
# Perform stratified sampling
= combined.stratifiedSample(
stratifiedSample =500, # Total number of points
numPoints='flooded_mask', # Band to stratify by
classBand=bbox,
region=90,
scale=0
seed
).randomColumn()
# Split into training and testing
= stratifiedSample.filter(ee.Filter.lt('random', 0.7))
training = stratifiedSample.filter(ee.Filter.gte('random', 0.7))
testing
# Set up the Random Forest classifier for flood prediction
= ee.Classifier.smileRandomForest(10).train(
classifier =training,
features='flooded_mask', # Use 'flooded_full_mask' as the class property
classProperty=inputProperties # Dynamically generated input properties
inputProperties
)
# Classify the image
= combined.select(inputProperties).classify(classifier)
classified
# Assess accuracy
= testing.classify(classifier).errorMatrix('flooded_mask', 'classification')
testAccuracy
# Calculate accuracy
= testAccuracy.accuracy().getInfo()
accuracy
# Convert the confusion matrix to an array
= testAccuracy.array().getInfo()
confusionMatrixArray
# Calculate recall for the positive class (assuming '1' represents the positive class for flooding)
= confusionMatrixArray[1][1] # True positives
true_positives = confusionMatrixArray[1][0] # False negatives
false_negatives = confusionMatrixArray[0][1] # False positives (non-flooded incorrectly identified as flooded)
false_positives = confusionMatrixArray[0][0] # True negatives (non-flooded correctly identified as non-flooded)
true_negatives = true_positives / (true_positives + false_negatives)
recall = false_positives / (false_positives + true_negatives)
false_positive_rate
print('Confusion Matrix:', confusionMatrixArray)
print('Accuracy:', accuracy)
print('Recall:', recall)
print('False Positive Rate:', false_positive_rate)
Confusion Matrix: [[84, 55], [16, 130]]
Accuracy: 0.7508771929824561
Recall: 0.8904109589041096
False Positive Rate: 0.39568345323741005
# Set up the Random Forest classifier for flood prediction with probability output
= ee.Classifier.smileRandomForest(10).setOutputMode('PROBABILITY').train(
classifier =training,
features='flooded_mask',
classProperty=inputProperties
inputProperties
)
# Classify the image to get probabilities
= combined.select(inputProperties).classify(classifier)
probabilityImage
# Visualization parameters for probability with white at the midpoint
= {
vizParamsProbability 'min': 0.,
'max': 1,
'palette': colorcet.bmw
}
= geemap.Map()
m "basemap_selector")
m.add("layer_manager")
m.add(
# Center the map on San Jose, Costa Rica
-84.0833, 9.9333, 10)
m.setCenter('ESA WorldCover 2020')
m.addLayer(landcover, {}, 'Flood Probability')
m.addLayer(probabilityImage, vizParamsProbability, 'palette': 'black'}, 'Permanent Surface Water')
m.addLayer(swater_mask, {# Display the map
m