Spaces:
Running
Running
Update web app
Browse files- pages/03_landsat.py +156 -23
pages/03_landsat.py
CHANGED
@@ -4,6 +4,7 @@ import geemap
|
|
4 |
import ipywidgets as widgets
|
5 |
from IPython.display import display
|
6 |
import solara
|
|
|
7 |
|
8 |
|
9 |
class Map(geemap.Map):
|
@@ -12,6 +13,23 @@ class Map(geemap.Map):
|
|
12 |
self.add_basemap("Esri.WorldImagery")
|
13 |
self.add_gui_widget(add_header=True)
|
14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
def add_gui_widget(self, position="topright", **kwargs):
|
16 |
|
17 |
widget = widgets.VBox(layout=widgets.Layout(padding="0px 5px 0px 5px"))
|
@@ -22,11 +40,13 @@ class Map(geemap.Map):
|
|
22 |
padding = "0px 5px 0px 5px"
|
23 |
pre_start_date = widgets.DatePicker(
|
24 |
description="Start",
|
|
|
25 |
style=style,
|
26 |
layout=widgets.Layout(padding=padding, width="160px"),
|
27 |
)
|
28 |
pre_end_date = widgets.DatePicker(
|
29 |
description="End",
|
|
|
30 |
style=style,
|
31 |
layout=widgets.Layout(padding=padding, width="160px"),
|
32 |
)
|
@@ -50,11 +70,13 @@ class Map(geemap.Map):
|
|
50 |
]
|
51 |
post_start_date = widgets.DatePicker(
|
52 |
description="Start",
|
|
|
53 |
style=style,
|
54 |
layout=widgets.Layout(padding=padding, width="160px"),
|
55 |
)
|
56 |
post_end_date = widgets.DatePicker(
|
57 |
description="End",
|
|
|
58 |
style=style,
|
59 |
layout=widgets.Layout(padding=padding, width="160px"),
|
60 |
)
|
@@ -78,17 +100,53 @@ class Map(geemap.Map):
|
|
78 |
]
|
79 |
|
80 |
apply_btn = widgets.Button(description="Apply", layout=layout)
|
81 |
-
split_btn = widgets.Button(description="Split", layout=layout)
|
82 |
reset_btn = widgets.Button(description="Reset", layout=layout)
|
83 |
-
|
84 |
-
buttons = widgets.HBox([apply_btn, split_btn, reset_btn, close_btn])
|
85 |
output = widgets.Output()
|
86 |
|
87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
self.add_widget(widget, position=position, **kwargs)
|
89 |
|
90 |
def apply_btn_click(b):
|
91 |
|
|
|
|
|
|
|
|
|
|
|
92 |
if self.user_roi is None:
|
93 |
output.clear_output()
|
94 |
output.append_stdout("Please draw a ROI first.")
|
@@ -105,34 +163,109 @@ class Map(geemap.Map):
|
|
105 |
output.clear_output()
|
106 |
output.append_stdout("Computing... Please wait.")
|
107 |
roi = ee.FeatureCollection(self.user_roi)
|
108 |
-
|
109 |
-
|
110 |
-
.
|
111 |
-
|
112 |
-
pre_start_date.value.
|
113 |
-
pre_end_date.value.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
)
|
115 |
-
|
116 |
-
)
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
123 |
)
|
124 |
-
.filter(ee.Filter.lt("CLOUD_COVERAGE", post_cloud_cover.value))
|
125 |
-
)
|
126 |
|
127 |
pre_img = pre_col.median().clip(roi)
|
128 |
post_img = post_col.median().clip(roi)
|
129 |
-
|
130 |
-
|
131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
output.clear_output()
|
133 |
|
134 |
apply_btn.on_click(apply_btn_click)
|
135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
|
137 |
@solara.component
|
138 |
def Page():
|
|
|
4 |
import ipywidgets as widgets
|
5 |
from IPython.display import display
|
6 |
import solara
|
7 |
+
from datetime import date
|
8 |
|
9 |
|
10 |
class Map(geemap.Map):
|
|
|
13 |
self.add_basemap("Esri.WorldImagery")
|
14 |
self.add_gui_widget(add_header=True)
|
15 |
|
16 |
+
def clean_up(self):
|
17 |
+
|
18 |
+
layers = [
|
19 |
+
"Pre-event Image",
|
20 |
+
"Post-event Image",
|
21 |
+
"Pre-event NDWI",
|
22 |
+
"Post-event NDWI",
|
23 |
+
"Pre-event Water",
|
24 |
+
"Post-event Water",
|
25 |
+
"Disappeared Water",
|
26 |
+
"New Water",
|
27 |
+
]
|
28 |
+
for layer_name in layers:
|
29 |
+
layer = self.find_layer(layer_name)
|
30 |
+
if layer is not None:
|
31 |
+
self.remove(layer)
|
32 |
+
|
33 |
def add_gui_widget(self, position="topright", **kwargs):
|
34 |
|
35 |
widget = widgets.VBox(layout=widgets.Layout(padding="0px 5px 0px 5px"))
|
|
|
40 |
padding = "0px 5px 0px 5px"
|
41 |
pre_start_date = widgets.DatePicker(
|
42 |
description="Start",
|
43 |
+
value=date(2014, 1, 1),
|
44 |
style=style,
|
45 |
layout=widgets.Layout(padding=padding, width="160px"),
|
46 |
)
|
47 |
pre_end_date = widgets.DatePicker(
|
48 |
description="End",
|
49 |
+
value=date(2014, 12, 31),
|
50 |
style=style,
|
51 |
layout=widgets.Layout(padding=padding, width="160px"),
|
52 |
)
|
|
|
70 |
]
|
71 |
post_start_date = widgets.DatePicker(
|
72 |
description="Start",
|
73 |
+
value=date(2024, 1, 1),
|
74 |
style=style,
|
75 |
layout=widgets.Layout(padding=padding, width="160px"),
|
76 |
)
|
77 |
post_end_date = widgets.DatePicker(
|
78 |
description="End",
|
79 |
+
value=date(2024, 12, 31),
|
80 |
style=style,
|
81 |
layout=widgets.Layout(padding=padding, width="160px"),
|
82 |
)
|
|
|
100 |
]
|
101 |
|
102 |
apply_btn = widgets.Button(description="Apply", layout=layout)
|
|
|
103 |
reset_btn = widgets.Button(description="Reset", layout=layout)
|
104 |
+
buttons = widgets.HBox([apply_btn, reset_btn])
|
|
|
105 |
output = widgets.Output()
|
106 |
|
107 |
+
use_split = widgets.Checkbox(
|
108 |
+
value=False,
|
109 |
+
description="Split map",
|
110 |
+
style=style,
|
111 |
+
layout=widgets.Layout(padding=padding, width="100px"),
|
112 |
+
)
|
113 |
+
|
114 |
+
use_ndwi = widgets.Checkbox(
|
115 |
+
value=False,
|
116 |
+
description="Compute NDWI",
|
117 |
+
style=style,
|
118 |
+
layout=widgets.Layout(padding=padding, width="160px"),
|
119 |
+
)
|
120 |
+
|
121 |
+
ndwi_threhold = widgets.FloatSlider(
|
122 |
+
description="Threshold",
|
123 |
+
min=-1,
|
124 |
+
max=1,
|
125 |
+
value=0,
|
126 |
+
step=0.05,
|
127 |
+
readout=True,
|
128 |
+
style=style,
|
129 |
+
layout=widgets.Layout(padding=padding, width="230px"),
|
130 |
+
)
|
131 |
+
|
132 |
+
options = widgets.HBox(
|
133 |
+
[
|
134 |
+
use_split,
|
135 |
+
use_ndwi,
|
136 |
+
ndwi_threhold,
|
137 |
+
]
|
138 |
+
)
|
139 |
+
|
140 |
+
widget.children = [pre_widget, post_widget, options, buttons, output]
|
141 |
self.add_widget(widget, position=position, **kwargs)
|
142 |
|
143 |
def apply_btn_click(b):
|
144 |
|
145 |
+
marker_layer = self.find_layer("Search location")
|
146 |
+
if marker_layer is not None:
|
147 |
+
self.remove(marker_layer)
|
148 |
+
self.clean_up()
|
149 |
+
|
150 |
if self.user_roi is None:
|
151 |
output.clear_output()
|
152 |
output.append_stdout("Please draw a ROI first.")
|
|
|
163 |
output.clear_output()
|
164 |
output.append_stdout("Computing... Please wait.")
|
165 |
roi = ee.FeatureCollection(self.user_roi)
|
166 |
+
vis_params = {"bands": ["B6", "B5", "B4"], "min": 0, "max": 0.4}
|
167 |
+
if pre_start_date.value.strftime("%Y-%m-%d") < "2013-04-11":
|
168 |
+
pre_col = geemap.landsat_timeseries(
|
169 |
+
roi,
|
170 |
+
start_year=pre_start_date.value.year,
|
171 |
+
end_year=pre_end_date.value.year,
|
172 |
+
).select(["SWIR1", "NIR", "Red", "Green"], ["B6", "B5", "B4", "B3"])
|
173 |
+
else:
|
174 |
+
pre_col = (
|
175 |
+
ee.ImageCollection("NASA/HLS/HLSL30/v002")
|
176 |
+
.filterBounds(roi)
|
177 |
+
.filterDate(
|
178 |
+
pre_start_date.value.strftime("%Y-%m-%d"),
|
179 |
+
pre_end_date.value.strftime("%Y-%m-%d"),
|
180 |
+
)
|
181 |
+
.filter(ee.Filter.lt("CLOUD_COVERAGE", pre_cloud_cover.value))
|
182 |
)
|
183 |
+
|
184 |
+
if post_start_date.value.strftime("%Y-%m-%d") < "2013-04-11":
|
185 |
+
post_col = geemap.landsat_timeseries(
|
186 |
+
roi,
|
187 |
+
start_year=post_start_date.value.year,
|
188 |
+
end_year=post_end_date.value.year,
|
189 |
+
).select(["SWIR1", "NIR", "Red", "Green"], ["B6", "B5", "B4", "B3"])
|
190 |
+
else:
|
191 |
+
post_col = (
|
192 |
+
ee.ImageCollection("NASA/HLS/HLSL30/v002")
|
193 |
+
.filterBounds(roi)
|
194 |
+
.filterDate(
|
195 |
+
post_start_date.value.strftime("%Y-%m-%d"),
|
196 |
+
post_end_date.value.strftime("%Y-%m-%d"),
|
197 |
+
)
|
198 |
+
.filter(ee.Filter.lt("CLOUD_COVERAGE", post_cloud_cover.value))
|
199 |
)
|
|
|
|
|
200 |
|
201 |
pre_img = pre_col.median().clip(roi)
|
202 |
post_img = post_col.median().clip(roi)
|
203 |
+
|
204 |
+
if use_split.value:
|
205 |
+
left_layer = geemap.ee_tile_layer(
|
206 |
+
pre_img, vis_params, "Pre-event Image"
|
207 |
+
)
|
208 |
+
right_layer = geemap.ee_tile_layer(
|
209 |
+
post_img, vis_params, "Post-event Image"
|
210 |
+
)
|
211 |
+
self.split_map(
|
212 |
+
left_layer,
|
213 |
+
right_layer,
|
214 |
+
add_close_button=True,
|
215 |
+
left_label="Pre-event",
|
216 |
+
right_label="Post-event",
|
217 |
+
)
|
218 |
+
else:
|
219 |
+
pre_img = pre_col.median().clip(roi)
|
220 |
+
post_img = post_col.median().clip(roi)
|
221 |
+
self.add_layer(pre_img, vis_params, "Pre-event Image")
|
222 |
+
self.add_layer(post_img, vis_params, "Post-event Image")
|
223 |
+
|
224 |
+
if use_ndwi.value and (not use_split.value):
|
225 |
+
pre_ndwi = pre_img.normalizedDifference(["B3", "B6"]).rename("NDWI")
|
226 |
+
post_ndwi = post_img.normalizedDifference(["B3", "B6"]).rename(
|
227 |
+
"NDWI"
|
228 |
+
)
|
229 |
+
ndwi_vis = {"min": -1, "max": 1, "palette": "ndwi"}
|
230 |
+
self.add_layer(pre_ndwi, ndwi_vis, "Pre-event NDWI", False)
|
231 |
+
self.add_layer(post_ndwi, ndwi_vis, "Post-event NDWI", False)
|
232 |
+
|
233 |
+
pre_water = pre_ndwi.gt(ndwi_threhold.value)
|
234 |
+
post_water = post_ndwi.gt(ndwi_threhold.value)
|
235 |
+
self.add_layer(
|
236 |
+
pre_water.selfMask(), {"palette": "blue"}, "Pre-event Water"
|
237 |
+
)
|
238 |
+
self.add_layer(
|
239 |
+
post_water.selfMask(), {"palette": "red"}, "Post-event Water"
|
240 |
+
)
|
241 |
+
new_water = post_water.subtract(pre_water).gt(0)
|
242 |
+
disappear_water = pre_water.subtract(post_water).gt(0)
|
243 |
+
self.add_layer(
|
244 |
+
disappear_water.selfMask(),
|
245 |
+
{"palette": "brown"},
|
246 |
+
"Disappeared Water",
|
247 |
+
)
|
248 |
+
self.add_layer(
|
249 |
+
new_water.selfMask(), {"palette": "cyan"}, "New Water"
|
250 |
+
)
|
251 |
+
|
252 |
+
with output:
|
253 |
+
output.clear_output()
|
254 |
+
|
255 |
output.clear_output()
|
256 |
|
257 |
apply_btn.on_click(apply_btn_click)
|
258 |
|
259 |
+
def reset_btn_click(b):
|
260 |
+
self.clean_up()
|
261 |
+
self._draw_control.clear()
|
262 |
+
draw_layer = self.find_layer("Drawn Features")
|
263 |
+
if draw_layer is not None:
|
264 |
+
self.remove(draw_layer)
|
265 |
+
output.clear_output()
|
266 |
+
|
267 |
+
reset_btn.on_click(reset_btn_click)
|
268 |
+
|
269 |
|
270 |
@solara.component
|
271 |
def Page():
|