Spaces:
Running
Running
import ee | |
import geemap | |
import ipywidgets as widgets | |
import solara | |
from datetime import date | |
class Map(geemap.Map): | |
def __init__(self, **kwargs): | |
super().__init__(**kwargs) | |
self.add_basemap("Esri.WorldImagery") | |
self.add_gui_widget(add_header=True) | |
def clean_up(self): | |
layers = [ | |
"Pre-event Image", | |
"Post-event Image", | |
"Pre-event NDWI", | |
"Post-event NDWI", | |
"Pre-event Water", | |
"Post-event Water", | |
"Disappeared Water", | |
"New Water", | |
] | |
for layer_name in layers: | |
layer = self.find_layer(layer_name) | |
if layer is not None: | |
self.remove(layer) | |
def add_gui_widget(self, position="topright", **kwargs): | |
widget = widgets.VBox(layout=widgets.Layout(padding="0px 5px 0px 5px")) | |
pre_widget = widgets.HBox() | |
post_widget = widgets.HBox() | |
layout = widgets.Layout(width="auto") | |
style = {"description_width": "initial"} | |
padding = "0px 5px 0px 5px" | |
pre_start_date = widgets.DatePicker( | |
description="Start", | |
value=date(2014, 1, 1), | |
style=style, | |
layout=widgets.Layout(padding=padding, width="160px"), | |
) | |
pre_end_date = widgets.DatePicker( | |
description="End", | |
value=date(2014, 12, 31), | |
style=style, | |
layout=widgets.Layout(padding=padding, width="160px"), | |
) | |
pre_cloud_cover = widgets.IntSlider( | |
description="Cloud", | |
min=0, | |
max=100, | |
value=25, | |
step=1, | |
readout=False, | |
style=style, | |
layout=widgets.Layout(padding=padding, width="130px"), | |
) | |
pre_cloud_label = widgets.Label(value=str(pre_cloud_cover.value)) | |
geemap.jslink_slider_label(pre_cloud_cover, pre_cloud_label) | |
pre_widget.children = [ | |
pre_start_date, | |
pre_end_date, | |
pre_cloud_cover, | |
pre_cloud_label, | |
] | |
post_start_date = widgets.DatePicker( | |
description="Start", | |
value=date(2024, 1, 1), | |
style=style, | |
layout=widgets.Layout(padding=padding, width="160px"), | |
) | |
post_end_date = widgets.DatePicker( | |
description="End", | |
value=date(2024, 12, 31), | |
style=style, | |
layout=widgets.Layout(padding=padding, width="160px"), | |
) | |
post_cloud_cover = widgets.IntSlider( | |
description="Cloud", | |
min=0, | |
max=100, | |
value=30, | |
step=1, | |
readout=False, | |
style=style, | |
layout=widgets.Layout(padding=padding, width="130px"), | |
) | |
post_cloud_label = widgets.Label(value=str(post_cloud_cover.value)) | |
geemap.jslink_slider_label(post_cloud_cover, post_cloud_label) | |
post_widget.children = [ | |
post_start_date, | |
post_end_date, | |
post_cloud_cover, | |
post_cloud_label, | |
] | |
apply_btn = widgets.Button(description="Apply", layout=layout) | |
reset_btn = widgets.Button(description="Reset", layout=layout) | |
buttons = widgets.HBox([apply_btn, reset_btn]) | |
output = widgets.Output() | |
use_split = widgets.Checkbox( | |
value=False, | |
description="Split map", | |
style=style, | |
layout=widgets.Layout(padding=padding, width="100px"), | |
) | |
use_ndwi = widgets.Checkbox( | |
value=False, | |
description="Compute NDWI", | |
style=style, | |
layout=widgets.Layout(padding=padding, width="160px"), | |
) | |
ndwi_threhold = widgets.FloatSlider( | |
description="Threshold", | |
min=-1, | |
max=1, | |
value=0, | |
step=0.05, | |
readout=True, | |
style=style, | |
layout=widgets.Layout(padding=padding, width="230px"), | |
) | |
options = widgets.HBox( | |
[ | |
use_split, | |
use_ndwi, | |
ndwi_threhold, | |
] | |
) | |
widget.children = [pre_widget, post_widget, options, buttons, output] | |
self.add_widget(widget, position=position, **kwargs) | |
def apply_btn_click(b): | |
marker_layer = self.find_layer("Search location") | |
if marker_layer is not None: | |
self.remove(marker_layer) | |
self.clean_up() | |
if self.user_roi is None: | |
output.clear_output() | |
output.append_stdout("Please draw a ROI first.") | |
elif ( | |
pre_start_date.value is None | |
or pre_end_date.value is None | |
or post_start_date.value is None | |
or post_end_date.value is None | |
): | |
output.clear_output() | |
output.append_stdout("Please select start and end dates.") | |
elif self.user_roi is not None: | |
output.clear_output() | |
output.append_stdout("Computing... Please wait.") | |
roi = ee.FeatureCollection(self.user_roi) | |
vis_params = {"bands": ["B6", "B5", "B4"], "min": 0, "max": 0.4} | |
if pre_start_date.value.strftime("%Y-%m-%d") < "2013-04-11": | |
pre_col = geemap.landsat_timeseries( | |
roi, | |
start_year=pre_start_date.value.year, | |
end_year=pre_end_date.value.year, | |
).select(["SWIR1", "NIR", "Red", "Green"], ["B6", "B5", "B4", "B3"]) | |
else: | |
pre_col = ( | |
ee.ImageCollection("NASA/HLS/HLSL30/v002") | |
.filterBounds(roi) | |
.filterDate( | |
pre_start_date.value.strftime("%Y-%m-%d"), | |
pre_end_date.value.strftime("%Y-%m-%d"), | |
) | |
.filter(ee.Filter.lt("CLOUD_COVERAGE", pre_cloud_cover.value)) | |
) | |
if post_start_date.value.strftime("%Y-%m-%d") < "2013-04-11": | |
post_col = geemap.landsat_timeseries( | |
roi, | |
start_year=post_start_date.value.year, | |
end_year=post_end_date.value.year, | |
).select(["SWIR1", "NIR", "Red", "Green"], ["B6", "B5", "B4", "B3"]) | |
else: | |
post_col = ( | |
ee.ImageCollection("NASA/HLS/HLSL30/v002") | |
.filterBounds(roi) | |
.filterDate( | |
post_start_date.value.strftime("%Y-%m-%d"), | |
post_end_date.value.strftime("%Y-%m-%d"), | |
) | |
.filter(ee.Filter.lt("CLOUD_COVERAGE", post_cloud_cover.value)) | |
) | |
pre_img = pre_col.median().clip(roi) | |
post_img = post_col.median().clip(roi) | |
if use_split.value: | |
left_layer = geemap.ee_tile_layer( | |
pre_img, vis_params, "Pre-event Image" | |
) | |
right_layer = geemap.ee_tile_layer( | |
post_img, vis_params, "Post-event Image" | |
) | |
self.split_map( | |
left_layer, | |
right_layer, | |
add_close_button=True, | |
left_label="Pre-event", | |
right_label="Post-event", | |
) | |
else: | |
pre_img = pre_col.median().clip(roi) | |
post_img = post_col.median().clip(roi) | |
self.add_layer(pre_img, vis_params, "Pre-event Image") | |
self.add_layer(post_img, vis_params, "Post-event Image") | |
if use_ndwi.value and (not use_split.value): | |
pre_ndwi = pre_img.normalizedDifference(["B3", "B6"]).rename("NDWI") | |
post_ndwi = post_img.normalizedDifference(["B3", "B6"]).rename( | |
"NDWI" | |
) | |
ndwi_vis = {"min": -1, "max": 1, "palette": "ndwi"} | |
self.add_layer(pre_ndwi, ndwi_vis, "Pre-event NDWI", False) | |
self.add_layer(post_ndwi, ndwi_vis, "Post-event NDWI", False) | |
pre_water = pre_ndwi.gt(ndwi_threhold.value) | |
post_water = post_ndwi.gt(ndwi_threhold.value) | |
self.add_layer( | |
pre_water.selfMask(), {"palette": "blue"}, "Pre-event Water" | |
) | |
self.add_layer( | |
post_water.selfMask(), {"palette": "red"}, "Post-event Water" | |
) | |
new_water = post_water.subtract(pre_water).gt(0) | |
disappear_water = pre_water.subtract(post_water).gt(0) | |
self.add_layer( | |
disappear_water.selfMask(), | |
{"palette": "brown"}, | |
"Disappeared Water", | |
) | |
self.add_layer( | |
new_water.selfMask(), {"palette": "cyan"}, "New Water" | |
) | |
with output: | |
output.clear_output() | |
output.clear_output() | |
apply_btn.on_click(apply_btn_click) | |
def reset_btn_click(b): | |
self.clean_up() | |
self._draw_control.clear() | |
draw_layer = self.find_layer("Drawn Features") | |
if draw_layer is not None: | |
self.remove(draw_layer) | |
output.clear_output() | |
reset_btn.on_click(reset_btn_click) | |
def Page(): | |
with solara.Column(style={"min-width": "500px"}): | |
Map.element( | |
center=[20, -0], | |
zoom=2, | |
height="750px", | |
zoom_ctrl=False, | |
measure_ctrl=False, | |
) | |