TCJ21 commited on
Commit
5f37cd7
Β·
1 Parent(s): c3216fe

stripped original app

Browse files
.pre-commit-config.yaml DELETED
@@ -1,39 +0,0 @@
1
- default_language_version:
2
- python: python3.9
3
-
4
- repos:
5
- - repo: https://github.com/pre-commit/pre-commit-hooks
6
- rev: v4.4.0
7
- hooks:
8
- - id: trailing-whitespace
9
- - id: end-of-file-fixer
10
- - id: check-ast
11
- - id: check-toml
12
- - id: check-yaml
13
- - repo: https://github.com/psf/black
14
- rev: 23.1.0
15
- hooks:
16
- - id: black
17
- - repo: https://github.com/PyCQA/isort
18
- rev: 5.12.0
19
- hooks:
20
- - id: isort
21
- - repo: https://github.com/pycqa/flake8
22
- rev: 6.0.0
23
- hooks:
24
- - id: flake8
25
- additional_dependencies:
26
- # - flake8-bugbear==21.9.2
27
- # - flake8-comprehensions==3.6.1
28
- # - flake8-deprecated==1.3
29
- - flake8-docstrings==1.6.0
30
- # - flake8-keyword-arguments==0.1.0
31
- - repo: https://github.com/pre-commit/mirrors-mypy
32
- rev: v0.910
33
- hooks:
34
- - id: mypy
35
- additional_dependencies:
36
- - types-PyYAML==5.4.10
37
- - types-setuptools==57.4.0
38
- - types-requests==2.25.9
39
- exclude: tests
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,63 +1,3 @@
1
  # Flood Mapping Tool
2
-
3
- [![Open in Streamlit](https://static.streamlit.io/badges/streamlit_badge_black_white.svg)](https://mapaction-flood-map.streamlit.app/)
4
- [![license](https://img.shields.io/github/license/OCHA-DAP/pa-aa-toolbox.svg)](https://github.com/mapaction/flood-mapping-tool/blob/main/LICENSE)
5
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
6
- [![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
7
-
8
- This repository contains a Streamlit app that allows to estimate flood extent using Sentinel-1 synthetic-aperture radar <a href='https://sentinel.esa.int/web/sentinel/user-guidessentinel-1-sar'>SAR</a> data.
9
-
10
- The methodology is based on a <a href='https://un-spider.org/advisory-support/recommended-practices/recommended-\practice-google-earth-engine-flood-mapping'> recommended practice </a> published by the United Nations Platform for Space-based Information for Disaster Management and Emergency Response (UN-SPIDER) and it uses several satellite imagery datasets to produce the final output. The datasets are retrieved from <a href='https://earthengine.google.com/'>Google Earth Engine</a> which is a powerful web-platform for cloud-based processing of remote sensing data on large scales. More information on the methodology is given in the <i>Description</i> page in the Streamlit app.
11
-
12
- This analysis provides a comprehensive overview of a flooding event, across different areas of interest, from settlements to countries. However, as mentioned in the UN-SPIDER website, the methodology is meant for broad information provision in a global context, and contains inherent uncertainties. Therefore, it is important that the tool is not used as the only source of information for rescue response planning.
13
-
14
- ## Usage
15
-
16
- #### Requirements
17
-
18
- The Python version currently used is 3.10. Please install all packages from
19
- ``requirements.txt``:
20
-
21
- ```shell
22
- pip install -r requirements.txt
23
- ```
24
-
25
- #### Google Earth Engine authentication
26
-
27
- [Sign up](https://signup.earthengine.google.com/) for a Google Earth Engine account, if you don't already have one. Open a terminal window, type `python` and then paste the following code:
28
-
29
- ```python
30
- import ee
31
- ee.Authenticate()
32
- ```
33
-
34
- Log in to your Google account to obtain the authorization code and paste it back into the terminal. Once you press "Enter", an authorization token will be saved to your computer under the following file path (depending on your operating system):
35
-
36
- - Windows: `C:\\Users\\USERNAME\\.config\\earthengine\\credentials`
37
- - Linux: `/home/USERNAME/.config/earthengine/credentials`
38
- - MacOS: `/Users/USERNAME/.config/earthengine/credentials`
39
-
40
- The credentials will be used when initialising Google Earth Engine in the app.
41
-
42
- #### Run the app
43
-
44
- Finally, open a terminal and run
45
-
46
- ```shell
47
- streamlit run app/Home.py
48
- ```
49
-
50
- A new browser window will open and you can start using the tool.
51
-
52
- ## Contributing
53
-
54
- #### Pre-commit
55
-
56
- All code is formatted according to
57
- [black](https://github.com/psf/black) and [flake8](https://flake8.pycqa.org/en/latest) guidelines. The repo is set-up to use [pre-commit](https://github.com/pre-commit/pre-commit). Please run ``pre-commit install`` the first time you are editing. Thereafter all commits will be checked against black and flake8 guidelines.
58
-
59
- To check if your changes pass pre-commit without committing, run:
60
-
61
- ```shell
62
- pre-commit run --all-files
63
- ```
 
1
  # Flood Mapping Tool
2
+ TODO: New documentation. For now its kept on python 3.10 like original Mapaction app and packages have not yet been updated.
3
+ Just create venv and pip install requirements
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/Home.py CHANGED
@@ -1,4 +1,5 @@
1
  """Home page for Streamlit app."""
 
2
  import streamlit as st
3
  from src.config_parameters import params
4
  from src.utils import (
@@ -26,110 +27,8 @@ st.markdown("# Home")
26
 
27
  # First section
28
  st.markdown("## Introduction")
29
- st.markdown(
30
- """
31
- This tool allows to estimate flood extent using Sentinel-1
32
- synthetic-aperture radar
33
- <a href='%s'>SAR</a> data.<br><br>
34
- The methodology is based on a <a href=
35
- '%s'>recommended practice</a>
36
- published by the United Nations Platform for Space-based Information for
37
- Disaster Management and Emergency Response (UN-SPIDER) and it uses several
38
- satellite imagery datasets to produce the final output. The datasets are
39
- retrieved from <a href='%s'>Google Earth
40
- Engine</a> which is a powerful web-platform for cloud-based processing of
41
- remote sensing data on large scales. More information on the methodology is
42
- given in the Description.<br><br>
43
- This analysis provides a comprehensive overview of a flooding event, across
44
- different areas of interest, from settlements to countries. However, as
45
- mentioned in the UN-SPIDER website, the methodology is meant for broad
46
- information provision in a global context, and contains inherent
47
- uncertainties. Therefore, it is important that the tool is not used as the
48
- only source of information for rescue response planning.
49
- """
50
- % (
51
- params["url_sentinel_esa"],
52
- params["url_unspider_tutorial"],
53
- params["url_gee"],
54
- ),
55
- unsafe_allow_html=True,
56
- )
57
 
58
  # Second section
59
  st.markdown("## How to use the tool")
60
- st.markdown(
61
- """
62
- <ul>
63
- <li><p>
64
- In the sidebar, choose <i>Flood extent analysis</i> to start the
65
- analysis.
66
- </p>
67
- <li><p>
68
- In the left panel, use the drawing tool to select an area of
69
- interest on the map. You can delete your selection by clicking on
70
- the bin icon. While the flood mapping is generated regardless of
71
- the size of the selected region, you will be able to save raster
72
- and vector flooding extent only if the side of the rectangular
73
- selection does not exceed 100 km.
74
- </p>
75
- <li><p>
76
- In the right panel click on the title <i>Choose Image Dates</i>
77
- in order to expand the section. Here you need to select four dates.
78
- The first two identify a range of dates based on which the
79
- reference imagery (before the flooding event) is defined. You can
80
- select even years worth of data (the reference imagery is
81
- calculated as the median between the range of observations), but
82
- make sure you take into account wet and dry seasons if only taking
83
- a few months. The last two refer to a period of time which comes
84
- after the flooding event. By setting periods, not single dates, you
85
- allow the selection of enough tiles to cover the area of interest.
86
- Sentinel-1 imagery is acquired minimum every 12 days for each point
87
- on the globe (see Figure 2 in the documentation).
88
- </p>
89
- <li>
90
- <p>
91
- By clicking on <i>Choose parameters</i>, you will be able to
92
- set two variables:
93
- </p>
94
- <ul>
95
- <li><p>
96
- The <i>threshold</i> is the value against which the
97
- difference the two satellite images - before and after the
98
- flooding event - is tested. Lower thresholds result in a
99
- greater area considered "flooded". It is recommended to set
100
- the value to 1.25, which was selected through trial and
101
- error. You may want to adjust the value in case of high
102
- rates of false positive or negative values, especially in
103
- case other sources of information are available and it is
104
- possible to compare flood extent estimations between
105
- sources.
106
- </p>
107
- <li><p>
108
- The <i>pass direction</i> has to do with the way the
109
- satellite travels around the Earth. Depending on your area
110
- of interest and time period, you may find more imagery
111
- available for either the <i>Ascending</i> or the
112
- <i>Descending</i> pass directions (see Figure 2 in the
113
- Documentation). It is recommended to leave the parameter
114
- unchanged for a first estimation and change its value in
115
- case partial or no imagery is produced.
116
- </p>
117
- </ul>
118
- <li><p>
119
- Once the parameters are set, you can finally click on <i>Compute
120
- flood extent</i> to run the calculations. A map will appear
121
- underneath, with a layer containing the flooded area within the
122
- area of interest.
123
- </p>
124
- <li><p>
125
- If you wish to export the layer to file, you can click on <i>Export
126
- to file</i> and download the raster and/or vector data.
127
- </p>
128
- </ul>
129
- <p>
130
- In case you get errors, follow the intructions. If you have doubts,
131
- feel free to contact the Data Science team.
132
- </p>
133
- """,
134
- unsafe_allow_html=True,
135
- )
 
1
  """Home page for Streamlit app."""
2
+
3
  import streamlit as st
4
  from src.config_parameters import params
5
  from src.utils import (
 
27
 
28
  # First section
29
  st.markdown("## Introduction")
30
+ st.markdown("TODO: new introduction")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
 
32
  # Second section
33
  st.markdown("## How to use the tool")
34
+ st.markdown("TODO: new how to use the tool")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/img/workflow.png DELETED
Binary file (44.6 kB)
 
app/pages/1_🌍_Flood_extent_analysis.py CHANGED
@@ -1,7 +1,7 @@
1
  """Flood extent analysis page for Streamlit app."""
 
2
  import datetime as dt
3
 
4
- import ee
5
  import folium
6
  import geemap.foliumap as geemap
7
  import requests
@@ -15,7 +15,6 @@ from src.utils import (
15
  set_tool_page_style,
16
  toggle_menu_button,
17
  )
18
- from src.utils_ee import ee_initialize
19
  from src.utils_flood_analysis import derive_flood_extents
20
  from streamlit_folium import st_folium
21
 
@@ -35,10 +34,6 @@ st.markdown("# Flood extent analysis")
35
  # Set page style
36
  set_tool_page_style()
37
 
38
- # Initialise Google Earth Engine
39
- ee_initialize(force_use_service_account=True)
40
-
41
-
42
  # Output_created is useful to decide whether the bottom panel with the
43
  # output map should be visualised or not
44
  if "output_created" not in st.session_state:
@@ -73,8 +68,8 @@ with col1:
73
  draw_options={
74
  "circle": False,
75
  "polyline": False,
76
- "polygon": True,
77
- "circle": False,
78
  "marker": False,
79
  "circlemarker": False,
80
  },
@@ -90,62 +85,24 @@ with col2:
90
  with st.expander("Choose Image Dates"):
91
  # Callback is added, so that, every time a parameters is changed,
92
  # the bottom panel containing the output map is hidden
93
- before_start = st.date_input(
94
- "Start date for reference imagery",
95
- value=dt.date(year=2022, month=7, day=1),
96
- help="It needs to be prior to the flooding event",
97
  on_change=callback,
98
  )
99
- before_end = st.date_input(
100
- "End date for reference imagery",
101
- value=dt.date(year=2022, month=7, day=30),
102
- help=(
103
- "It needs to be prior to the flooding event, at least 15 "
104
- "days subsequent to the date selected above"
105
- ),
106
- on_change=callback,
107
- )
108
- after_start = st.date_input(
109
- "Start date for flooding imagery",
110
- value=dt.date(year=2022, month=9, day=1),
111
- help="It needs to be subsequent to the flooding event",
112
- on_change=callback,
113
- )
114
- after_end = st.date_input(
115
- "End date for flooding imagery",
116
- value=dt.date(year=2022, month=9, day=16),
117
- help=(
118
- "It needs to be subsequent to the flooding event and at "
119
- "least 10 days to the date selected above"
120
- ),
121
  on_change=callback,
122
  )
123
  # Add collapsable container for parameters
124
  with st.expander("Choose Parameters"):
125
  # Add slider for threshold
126
- add_slider = st.slider(
127
- label="Select a threshold",
128
- min_value=0.0,
129
- max_value=5.0,
130
- value=1.25,
131
- step=0.25,
132
- help="Higher values might reduce overall noise",
133
- on_change=callback,
134
- )
135
- # Add radio buttons for pass direction
136
- pass_direction = st.radio(
137
- "Set pass direction",
138
- ["Ascending", "Descending"],
139
- on_change=callback,
140
- )
141
  # Button for computation
142
- submitted = st.button("Compute flood extent")
143
  # Introduce date validation
144
- check_dates = before_start < before_end <= after_start < after_end
145
  # Introduce drawing validation (a polygon needs to exist)
146
- check_drawing = (
147
- output["all_drawings"] != [] and output["all_drawings"] is not None
148
- )
149
  # What happens when button is clicked on?
150
  if submitted:
151
  with col2:
@@ -159,144 +116,70 @@ if submitted:
159
  # Add output for computation
160
  with st.spinner("Computing... Please wait..."):
161
  # Extract coordinates from drawn polygon
162
- coords = output["all_drawings"][-1]["geometry"]["coordinates"][
163
- 0
164
- ]
165
  # Create geometry from coordinates
166
- ee_geom_region = ee.Geometry.Polygon(coords)
167
- # Crate flood raster and vector
168
- (
169
- detected_flood_vector,
170
- detected_flood_raster,
171
- _,
172
- _,
173
- ) = derive_flood_extents(
174
- aoi=ee_geom_region,
175
- before_start_date=str(before_start),
176
- before_end_date=str(before_end),
177
- after_start_date=str(after_start),
178
- after_end_date=str(after_end),
179
- difference_threshold=add_slider,
180
- polarization="VH",
181
- pass_direction=pass_direction,
182
- export=False,
183
- )
184
- # Create output map
185
- Map2 = geemap.Map(
186
- # basemap="HYBRID",
187
- plugin_Draw=False,
188
- Draw_export=False,
189
- locate_control=False,
190
- plugin_LatLngPopup=False,
191
- )
192
- try:
193
- # Add flood vector layer to map
194
- Map2.add_layer(
195
- ee_object=detected_flood_raster,
196
- name="Flood extent raster",
197
- )
198
- Map2.add_layer(
199
- ee_object=detected_flood_vector,
200
- name="Flood extent vector",
201
- )
202
- # Center map on flood raster
203
- Map2.centerObject(detected_flood_raster)
204
- except ee.EEException:
205
- # If error contains the sentence below, it means that
206
- # an image could not be properly generated
207
- st.error(
208
- """
209
- No satellite image found for the selected
210
- dates.\n\n
211
- Try changing the pass direction.\n\n
212
- If this does not work, choose different
213
- dates: it is likely that the satellite did not
214
- cover the area of interest in the range of
215
- dates specified (either before or after the
216
- flooding event).
217
- """
218
- )
219
- else:
220
- # If computation was succesfull, save outputs for
221
- # output map
222
- st.success("Computation complete")
223
- st.session_state.output_created = True
224
- st.session_state.Map2 = Map2
225
- st.session_state.detected_flood_raster = (
226
- detected_flood_raster
227
- )
228
- st.session_state.detected_flood_vector = (
229
- detected_flood_vector
230
- )
231
- st.session_state.ee_geom_region = ee_geom_region
232
  # If computation was successful, create output map in bottom panel
233
  if st.session_state.output_created:
234
  with row2:
235
  # Add collapsable container for output map
236
  with st.expander("Output map", expanded=True):
237
- # Export Map2 to streamlit
238
- st.session_state.Map2.to_streamlit()
239
- # Create button to export to file
240
- submitted2 = st.button("Export to file")
241
- # What happens if button is clicked on?
242
- if submitted2:
243
- # Add output for computation
244
- with st.spinner("Computing... Please wait..."):
245
- try:
246
- # Get download url for raster data
247
- raster = st.session_state.detected_flood_raster
248
- url_r = raster.getDownloadUrl(
249
- {
250
- "region": st.session_state.ee_geom_region,
251
- "scale": 30,
252
- "format": "GEO_TIFF",
253
- }
254
- )
255
- except Exception:
256
- st.error(
257
- """
258
- The image size is too big for the image to
259
- be exported to file. Select a smaller area
260
- of interest (side <~ 150km) and repeat the
261
- analysis.
262
- """
263
- )
264
- else:
265
- response_r = requests.get(url_r)
266
- # Get download url for raster data
267
- vector = st.session_state.detected_flood_vector
268
- url_v = vector.getDownloadUrl("GEOJSON")
269
- response_v = requests.get(url_v)
270
- filename = "flood_extent"
271
- timestamp = dt.datetime.now().strftime(
272
- "%Y-%m-%d_%H-%M"
273
- )
274
- with row2:
275
- # Create download buttons for raster and vector
276
- # data
277
- with open("flood_extent.tif", "wb"):
278
- ste.download_button(
279
- label="Download Raster Extent",
280
- data=response_r.content,
281
- file_name=(
282
- f"{filename}"
283
- "_raster_"
284
- f"{timestamp}"
285
- ".tif"
286
- ),
287
- mime="image/tif",
288
- )
289
- with open("flood_extent.geojson", "wb"):
290
- ste.download_button(
291
- label="Download Vector Extent",
292
- data=response_v.content,
293
- file_name=(
294
- f"{filename}"
295
- "_vector_"
296
- f"{timestamp}"
297
- ".geojson"
298
- ),
299
- mime="text/json",
300
- )
301
- # Output for computation complete
302
- st.success("Computation complete")
 
1
  """Flood extent analysis page for Streamlit app."""
2
+
3
  import datetime as dt
4
 
 
5
  import folium
6
  import geemap.foliumap as geemap
7
  import requests
 
15
  set_tool_page_style,
16
  toggle_menu_button,
17
  )
 
18
  from src.utils_flood_analysis import derive_flood_extents
19
  from streamlit_folium import st_folium
20
 
 
34
  # Set page style
35
  set_tool_page_style()
36
 
 
 
 
 
37
  # Output_created is useful to decide whether the bottom panel with the
38
  # output map should be visualised or not
39
  if "output_created" not in st.session_state:
 
68
  draw_options={
69
  "circle": False,
70
  "polyline": False,
71
+ "polygon": False,
72
+ "rectangle": True,
73
  "marker": False,
74
  "circlemarker": False,
75
  },
 
85
  with st.expander("Choose Image Dates"):
86
  # Callback is added, so that, every time a parameters is changed,
87
  # the bottom panel containing the output map is hidden
88
+ start_date = st.date_input(
89
+ "Start date",
 
 
90
  on_change=callback,
91
  )
92
+ end_date = st.date_input(
93
+ "End date",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  on_change=callback,
95
  )
96
  # Add collapsable container for parameters
97
  with st.expander("Choose Parameters"):
98
  # Add slider for threshold
99
+ st.text("Add relevant (API) parameters here")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  # Button for computation
101
+ submitted = st.button("Get flood extent")
102
  # Introduce date validation
103
+ check_dates = start_date <= end_date
104
  # Introduce drawing validation (a polygon needs to exist)
105
+ check_drawing = output["all_drawings"] != [] and output["all_drawings"] is not None
 
 
106
  # What happens when button is clicked on?
107
  if submitted:
108
  with col2:
 
116
  # Add output for computation
117
  with st.spinner("Computing... Please wait..."):
118
  # Extract coordinates from drawn polygon
119
+ coords = output["all_drawings"][-1]["geometry"]["coordinates"][0]
120
+ print(f"Coords: {coords}")
 
121
  # Create geometry from coordinates
122
+ st.session_state.output_created = True
123
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
  # If computation was successful, create output map in bottom panel
125
  if st.session_state.output_created:
126
  with row2:
127
  # Add collapsable container for output map
128
  with st.expander("Output map", expanded=True):
129
+ st.success("Calc complete")
130
+ # # Export Map2 to streamlit
131
+ # st.session_state.Map2.to_streamlit()
132
+ # # Create button to export to file
133
+ # submitted2 = st.button("Export to file")
134
+ # # What happens if button is clicked on?
135
+ # if submitted2:
136
+ # # Add output for computation
137
+ # with st.spinner("Computing... Please wait..."):
138
+ # try:
139
+ # # Get download url for raster data
140
+ # raster = st.session_state.detected_flood_raster
141
+ # url_r = raster.getDownloadUrl(
142
+ # {
143
+ # "region": st.session_state.ee_geom_region,
144
+ # "scale": 30,
145
+ # "format": "GEO_TIFF",
146
+ # }
147
+ # )
148
+ # except Exception:
149
+ # st.error(
150
+ # """
151
+ # The image size is too big for the image to
152
+ # be exported to file. Select a smaller area
153
+ # of interest (side <~ 150km) and repeat the
154
+ # analysis.
155
+ # """
156
+ # )
157
+ # else:
158
+ # response_r = requests.get(url_r)
159
+ # # Get download url for raster data
160
+ # vector = st.session_state.detected_flood_vector
161
+ # url_v = vector.getDownloadUrl("GEOJSON")
162
+ # response_v = requests.get(url_v)
163
+ # filename = "flood_extent"
164
+ # timestamp = dt.datetime.now().strftime("%Y-%m-%d_%H-%M")
165
+ # with row2:
166
+ # # Create download buttons for raster and vector
167
+ # # data
168
+ # with open("flood_extent.tif", "wb"):
169
+ # ste.download_button(
170
+ # label="Download Raster Extent",
171
+ # data=response_r.content,
172
+ # file_name=(f"{filename}_raster_{timestamp}.tif"),
173
+ # mime="image/tif",
174
+ # )
175
+ # with open("flood_extent.geojson", "wb"):
176
+ # ste.download_button(
177
+ # label="Download Vector Extent",
178
+ # data=response_v.content,
179
+ # file_name=(
180
+ # f"{filename}_vector_{timestamp}.geojson"
181
+ # ),
182
+ # mime="text/json",
183
+ # )
184
+ # # Output for computation complete
185
+ # st.success("Computation complete")
 
 
 
 
 
 
 
 
 
app/pages/2_πŸ“–_Documentation.py CHANGED
@@ -1,4 +1,5 @@
1
  """Documentation page for Streamlit app."""
 
2
  import streamlit as st
3
  from PIL import Image
4
  from src.config_parameters import params
@@ -28,63 +29,9 @@ st.markdown("# Documentation")
28
  # First section
29
  st.markdown("## Methodology")
30
  st.markdown(
31
- """
32
- The methodology is based on the workflow depicted in Figure 1. In
33
- addition to Sentinel-1 synthetic-aperture radar <a href='%s'>SAR</a> data,
34
- two other datasets are used through <a href='%s'>Google Earth Engine</a>:
35
- <ul>
36
- <li><p>
37
- The <i>WWF HydroSHEDS Void-Filled DEM, 3 Arc-Seconds</i>
38
- <a href='%s'>dataset</a> is based on elevation data
39
- obtained in 2000 by NASA's Shuttle Radar Topography Mission (SRTM),
40
- and it is used to mask out areas with more than 5 percent slope
41
- (see following section on limitations).
42
- </p>
43
- <li><p>
44
- The <i>JRC Global Surface Water Mapping Layers, v1.4</i>
45
- <a href='%s'>dataset</a> contains maps of the
46
- location and temporal distribution of surface water from 1984 to
47
- 2021, and it is used to mask areas with perennial water bodies,
48
- such as rivers or lakes.
49
- </p>
50
- </ul>
51
- """
52
- % (
53
- params["url_sentinel_dataset"],
54
- params["url_gee"],
55
- params["url_elevation_dataset"],
56
- params["url_surface_water_dataset"],
57
- ),
58
- unsafe_allow_html=True,
59
  )
60
 
61
- # Add image workflow
62
- img = Image.open("app/img/workflow.png")
63
- col1, mid, col2, last = st.columns([5, 3, 10, 10])
64
- with col1:
65
- st.image(img, width=350)
66
- with col2:
67
- # Trick to add caption at the bottom of the column, as Streamlit has not
68
- # developed a functionality to allign text to bottom
69
- space_before_caption = "<br>" * 27
70
- st.markdown(
71
- space_before_caption,
72
- unsafe_allow_html=True,
73
- )
74
- st.markdown(
75
- """
76
- <p style="font-size:%s;">
77
- Figure 1. Workflow of the flood mapping methodology (<a href=
78
- '%s'>source</a>).
79
- </p>
80
- """
81
- % (
82
- params["docs_caption_fontsize"],
83
- params["url_unspider_tutorial_detail"],
84
- ),
85
- unsafe_allow_html=True,
86
- )
87
-
88
 
89
  # Second section
90
  st.markdown("## Radar imagery for flood detection")
@@ -140,40 +87,3 @@ st.markdown(
140
  % (params["docs_caption_fontsize"], params["url_sentinel_img_location"]),
141
  unsafe_allow_html=True,
142
  )
143
-
144
- # Third section
145
- st.markdown("## Key limitations")
146
- st.markdown(
147
- """
148
- Radar imagery is great for detecting floods, as it is good at picking up
149
- water and it is not affected by the time of the day or clouds (at this
150
- wavelength). But it has its limits, and performs actually quite bad if
151
- having to detect water in mountainous regions, especially if with narrow
152
- valleys, and in urban areas (urban canyons). The reasons are mainly around
153
- the viewing angles, which can cause image distortions. This method may also
154
- result in false positives for other land cover changes with smooth
155
- surfaces, such as roads and sand. Rough surface texture caused by wind or
156
- rainfall may also make it challenging for the radar imagery to identify
157
- water bodies.
158
- """,
159
- unsafe_allow_html=True,
160
- )
161
-
162
-
163
- # Last section
164
- st.markdown("## Useful links")
165
- st.markdown(
166
- """
167
- <a href='%s'>UN-SPIDER recommended practice</a><br>
168
- <a href='%s'>Sentinel-1 satellite imagery user guide</a><br>
169
- Relevant scientific publications:
170
- <a href='%s'>1</a>, <a href='%s'>2</a><br>
171
- """
172
- % (
173
- params["url_unspider_tutorial"],
174
- params["url_sentinel_esa"],
175
- params["url_publication_1"],
176
- params["url_publication_2"],
177
- ),
178
- unsafe_allow_html=True,
179
- )
 
1
  """Documentation page for Streamlit app."""
2
+
3
  import streamlit as st
4
  from PIL import Image
5
  from src.config_parameters import params
 
29
  # First section
30
  st.markdown("## Methodology")
31
  st.markdown(
32
+ "TODO: new documentation, only kept in Sentinel 1 section unchanged from the Mapaction tool"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  )
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
  # Second section
37
  st.markdown("## Radar imagery for flood detection")
 
87
  % (params["docs_caption_fontsize"], params["url_sentinel_img_location"]),
88
  unsafe_allow_html=True,
89
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/src/config_parameters.py CHANGED
@@ -1,60 +1,23 @@
1
  """Configuration file."""
 
2
  params = {
3
  # Title browser tab
4
- "browser_title": "Flood mapping tool - MapAction",
5
  # Data scientists involved
6
  "data_scientists": {
7
- "Piet": "pgerrits@mapaction.org",
8
- "Daniele": "[email protected]",
9
- "Cate": "[email protected]",
10
  },
11
  # Urls
12
- "url_data_science_wiki": (
13
- "https://mapaction.atlassian.net/wiki/spaces/GAFO/overview"
14
- ),
15
- "url_gee": "https://earthengine.google.com/",
16
- "url_project_wiki": (
17
- "https://mapaction.atlassian.net/wiki/spaces/GAFO/pages/15920922751/"
18
- "Rapid+flood+mapping+from+satellite+imagery"
19
- ),
20
  "url_github_repo": "https://github.com/mapaction/flood-extent-tool",
21
- "url_sentinel_esa": (
22
- "https://sentinel.esa.int/web/sentinel/user-guides/sentinel-1-sar"
23
- ),
24
  "url_sentinel_dataset": (
25
- "https://developers.google.com/earth-engine/datasets/catalog/"
26
- "COPERNICUS_S1_GRD"
27
  ),
28
  "url_sentinel_img": (
29
  "https://sentinel.esa.int/documents/247904/4748961/Sentinel-1-Repeat-"
30
  "Coverage-Frequency-Geometry-2021.jpg"
31
  ),
32
  "url_sentinel_img_location": (
33
- "https://sentinel.esa.int/web/sentinel/missions/sentinel-1/"
34
- "observation-scenario"
35
- ),
36
- "url_unspider_tutorial": (
37
- "https://un-spider.org/advisory-support/recommended-practices/"
38
- "recommended-practice-google-earth-engine-flood-mapping"
39
- ),
40
- "url_unspider_tutorial_detail": (
41
- "https://un-spider.org/advisory-support/recommended-practices/"
42
- "recommended-practice-google-earth-engine-flood-mapping/in-detail"
43
- ),
44
- "url_elevation_dataset": (
45
- "https://developers.google.com/earth-engine/datasets/catalog/"
46
- "WWF_HydroSHEDS_03VFDEM"
47
- ),
48
- "url_surface_water_dataset": (
49
- "https://developers.google.com/earth-engine/datasets/catalog/"
50
- "JRC_GSW1_4_GlobalSurfaceWater"
51
- ),
52
- "url_publication_1": (
53
- "https://onlinelibrary.wiley.com/doi/full/10.1111/jfr3.12303"
54
- ),
55
- "url_publication_2": (
56
- "https://www.sciencedirect.com/science/article/abs/pii/"
57
- "S0924271620301702"
58
  ),
59
  # Layout and styles
60
  ## Sidebar
 
1
  """Configuration file."""
2
+
3
  params = {
4
  # Title browser tab
5
+ "browser_title": "Flood mapping tool - 510",
6
  # Data scientists involved
7
  "data_scientists": {
8
+ "Daniele": "dcastellana@redcross.nl",
 
 
9
  },
10
  # Urls
 
 
 
 
 
 
 
 
11
  "url_github_repo": "https://github.com/mapaction/flood-extent-tool",
 
 
 
12
  "url_sentinel_dataset": (
13
+ "https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S1_GRD"
 
14
  ),
15
  "url_sentinel_img": (
16
  "https://sentinel.esa.int/documents/247904/4748961/Sentinel-1-Repeat-"
17
  "Coverage-Frequency-Geometry-2021.jpg"
18
  ),
19
  "url_sentinel_img_location": (
20
+ "https://sentinel.esa.int/web/sentinel/missions/sentinel-1/observation-scenario"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  ),
22
  # Layout and styles
23
  ## Sidebar
app/src/utils.py CHANGED
@@ -1,9 +1,11 @@
1
  """Functions for the layout of the Streamlit app, including the sidebar."""
 
2
  import base64
3
  import os
4
  from datetime import date
5
 
6
  import streamlit as st
 
7
  from src.config_parameters import params
8
 
9
 
@@ -183,46 +185,16 @@ def add_about():
183
  Returns:
184
  None
185
  """
186
- today = date.today().strftime("%B %d, %Y")
187
-
188
  # About textbox
189
  st.sidebar.markdown("## About")
190
  st.sidebar.markdown(
191
- """
192
- <div class='warning' style='
193
- background-color: %s;
194
- margin: 0px;
195
- padding: 1em;'
196
- '>
197
- <p style='
198
- margin-left:1em;
199
- margin: 0px;
200
- font-size: 1rem;
201
- margin-bottom: 1em;
202
- '>
203
- Last update: %s
204
- </p>
205
- <p style='
206
- margin-left:1em;
207
- font-size: 1rem;
208
- margin: 0px
209
- '>
210
- <a href='%s'>
211
- Wiki reference page</a><br>
212
- <a href='%s'>
213
- GitHub repository</a><br>
214
- <a href='%s'>
215
- Data Science Lab</a>
216
- </p>
217
- </div>
218
- """
219
- % (
220
- params["about_box_background_color"],
221
- today,
222
- params["url_project_wiki"],
223
- params["url_github_repo"],
224
- params["url_data_science_wiki"],
225
- ),
226
  unsafe_allow_html=True,
227
  )
228
 
 
1
  """Functions for the layout of the Streamlit app, including the sidebar."""
2
+
3
  import base64
4
  import os
5
  from datetime import date
6
 
7
  import streamlit as st
8
+
9
  from src.config_parameters import params
10
 
11
 
 
185
  Returns:
186
  None
187
  """
 
 
188
  # About textbox
189
  st.sidebar.markdown("## About")
190
  st.sidebar.markdown(
191
+ f"""
192
+ <p>
193
+ Todo: general about stuff <br />
194
+ <a href='{params["url_github_repo"]}'>
195
+ Github Repo</a>
196
+ </p>
197
+ """,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  unsafe_allow_html=True,
199
  )
200
 
app/src/utils_ee.py DELETED
@@ -1,36 +0,0 @@
1
- """Module for ee-related functionalities."""
2
- import ee
3
- import streamlit as st
4
- from ee import oauth
5
- from google.oauth2 import service_account
6
- from src.utils import is_app_on_streamlit
7
-
8
-
9
- @st.experimental_memo
10
- def ee_initialize(force_use_service_account: bool = False):
11
- """Initialise Google Earth Engine.
12
-
13
- Checks whether the app is deployed on Streamlit Cloud and, based on the
14
- result, initialises Google Earth Engine in different ways: if the app is
15
- run locally, the credentials are retrieved from the user's credentials
16
- stored in the local system (personal Google account is used). If the app
17
- is deployed on Streamlit Cloud, credentials are taken from the secrets
18
- field in the cloud (a dedicated service account is used).
19
- Inputs:
20
- force_use_service_account (bool): If True, the dedicated Google
21
- service account is used, regardless of whether the app is run
22
- locally or in the cloud. To be able to use a service account
23
- locally, a file called "secrets.toml" should be added to the
24
- folder ".streamlit", in the main project folder.
25
-
26
- Returns:
27
- None
28
- """
29
- if force_use_service_account or is_app_on_streamlit():
30
- service_account_keys = st.secrets["ee_keys"]
31
- credentials = service_account.Credentials.from_service_account_info(
32
- service_account_keys, scopes=oauth.SCOPES
33
- )
34
- ee.Initialize(credentials)
35
- else:
36
- ee.Initialize()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/src/utils_flood_analysis.py DELETED
@@ -1,369 +0,0 @@
1
- """Functions to derive flood extent using Google Earth Engine."""
2
- import time
3
-
4
- import ee
5
-
6
-
7
- def _check_task_completed(task_id, verbose=False):
8
- """
9
- Return True if a task export completes successfully, else returns false.
10
-
11
- Inputs:
12
- task_id (str): Google Earth Engine task id
13
-
14
- Returns:
15
- boolean
16
-
17
- """
18
- status = ee.data.getTaskStatus(task_id)[0]
19
- if status["state"] in (
20
- ee.batch.Task.State.CANCELLED,
21
- ee.batch.Task.State.FAILED,
22
- ):
23
- if "error_message" in status:
24
- if verbose:
25
- print(status["error_message"])
26
- return True
27
- elif status["state"] == ee.batch.Task.State.COMPLETED:
28
- return True
29
- return False
30
-
31
-
32
- def wait_for_tasks(task_ids, timeout=3600, verbose=False):
33
- """
34
- Wait for tasks to complete, fail, or timeout.
35
-
36
- Wait for all active tasks if task_ids is not provided.
37
- Note: Tasks will not be canceled after timeout, and
38
- may continue to run.
39
- Inputs:
40
- task_ids (list):
41
- timeout (int):
42
-
43
- Returns:
44
- None
45
- """
46
- start = time.time()
47
- elapsed = 0
48
- while elapsed < timeout or timeout == 0:
49
- elapsed = time.time() - start
50
- finished = [_check_task_completed(task) for task in task_ids]
51
- if all(finished):
52
- if verbose:
53
- print(f"Tasks {task_ids} completed after {elapsed}s")
54
- return True
55
- time.sleep(5)
56
- if verbose:
57
- print(
58
- f"Stopped waiting for {len(task_ids)} tasks \
59
- after {timeout} seconds"
60
- )
61
- return False
62
-
63
-
64
- def export_flood_data(
65
- flooded_area_vector,
66
- flooded_area_raster,
67
- image_before_flood,
68
- image_after_flood,
69
- region,
70
- filename="flood_extents",
71
- verbose=False,
72
- ):
73
- """
74
- Export the results of derive_flood_extents function to Google Drive.
75
-
76
- Inputs:
77
- flooded_area_vector (ee.FeatureCollection): Detected flood extents as
78
- vector geometries.
79
- flooded_area_raster (ee.Image): Detected flood extents as a binary
80
- raster.
81
- image_before_flood (ee.Image): The 'before' Sentinel-1 image.
82
- image_after_flood (ee.Image): The 'after' Sentinel-1 image containing
83
- view of the flood waters.
84
- region (ee.Geometry.Polygon): Geographic extent of analysis area.
85
- filename (str): Desired filename prefix for exported files
86
-
87
- Returns:
88
- None
89
- """
90
- if verbose:
91
- print(
92
- "Exporting detected flood extents to your Google Drive. \
93
- Please wait..."
94
- )
95
- s1_before_task = ee.batch.Export.image.toDrive(
96
- image=image_before_flood,
97
- description="export_before_s1_scene",
98
- scale=30,
99
- region=region,
100
- fileNamePrefix=filename + "_s1_before",
101
- crs="EPSG:4326",
102
- fileFormat="GeoTIFF",
103
- )
104
-
105
- s1_after_task = ee.batch.Export.image.toDrive(
106
- image=image_after_flood,
107
- description="export_flooded_s1_scene",
108
- scale=30,
109
- region=region,
110
- fileNamePrefix=filename + "_s1_after",
111
- crs="EPSG:4326",
112
- fileFormat="GeoTIFF",
113
- )
114
-
115
- raster_task = ee.batch.Export.image.toDrive(
116
- image=flooded_area_raster,
117
- description="export_flood_extents_raster",
118
- scale=30,
119
- region=region,
120
- fileNamePrefix=filename + "_raster",
121
- crs="EPSG:4326",
122
- fileFormat="GeoTIFF",
123
- )
124
-
125
- vector_task = ee.batch.Export.table.toDrive(
126
- collection=flooded_area_vector,
127
- description="export_flood_extents_polygons",
128
- fileFormat="shp",
129
- fileNamePrefix=filename + "_polygons",
130
- )
131
-
132
- s1_before_task.start()
133
- s1_after_task.start()
134
- raster_task.start()
135
- vector_task.start()
136
-
137
- if verbose:
138
- print("Exporting before Sentinel-1 scene: Task id ", s1_before_task.id)
139
- print("Exporting flooded Sentinel-1 scene: Task id ", s1_after_task.id)
140
- print("Exporting flood extent geotiff: Task id ", raster_task.id)
141
- print("Exporting flood extent shapefile: Task id ", vector_task.id)
142
-
143
- wait_for_tasks(
144
- [s1_before_task.id, s1_after_task.id, raster_task.id, vector_task.id]
145
- )
146
-
147
-
148
- def retrieve_image_collection(
149
- search_region,
150
- start_date,
151
- end_date,
152
- polarization="VH",
153
- pass_direction="Ascending",
154
- ):
155
- """
156
- Retrieve Sentinel-1 immage collection from Google Earth Engine.
157
-
158
- Inputs:
159
- search_region (ee.Geometry.Polygon): Geographic extent of image search.
160
- start_date (str): Date in format yyyy-mm-dd, e.g., '2020-10-01'.
161
- end_date (str): Date in format yyyy-mm-dd, e.g., '2020-10-01'.
162
- polarization (str): Synthetic aperture radar polarization mode, e.g.,
163
- 'VH' or 'VV'. VH is mostly is the preferred polarization for
164
- flood mapping.
165
- pass_direction (str): Synthetic aperture radar pass direction, either
166
- 'Ascending' or 'Descending'.
167
-
168
- Returns:
169
- collection (ee.ImageCollection): Sentinel-1 images matching the search
170
- criteria.
171
- """
172
- collection = (
173
- ee.ImageCollection("COPERNICUS/S1_GRD")
174
- .filter(ee.Filter.eq("instrumentMode", "IW"))
175
- .filter(
176
- ee.Filter.listContains(
177
- "transmitterReceiverPolarisation", polarization
178
- )
179
- )
180
- .filter(ee.Filter.eq("orbitProperties_pass", pass_direction.upper()))
181
- .filter(ee.Filter.eq("resolution_meters", 10))
182
- .filterDate(start_date, end_date)
183
- .filterBounds(search_region)
184
- .select(polarization)
185
- )
186
-
187
- return collection
188
-
189
-
190
- def smooth(image, smoothing_radius=50):
191
- """
192
- Reduce the radar speckle by smoothing.
193
-
194
- Inputs:
195
- image (ee.Image): Input image.
196
- smoothing_radius (int): The radius of the kernel to use for focal mean
197
- smoothing.
198
-
199
- Returns:
200
- smoothed_image (ee.Image): The resulting image after smoothing is
201
- applied.
202
- """
203
- smoothed_image = image.focal_mean(
204
- radius=smoothing_radius, kernelType="circle", units="meters"
205
- )
206
-
207
- return smoothed_image
208
-
209
-
210
- def mask_permanent_water(image):
211
- """
212
- Query the JRC Global Surface Water Mapping Layers, v1.3.
213
-
214
- The goal is to determine where perennial water bodies (water > 10
215
- months/yr), and mask these areas.
216
- Inputs:
217
- image (ee.Image): Input image.
218
-
219
- Returns:
220
- masked_image (ee.Image): The resulting image after surface water
221
- masking is applied.
222
- """
223
- surface_water = ee.Image("JRC/GSW1_4/GlobalSurfaceWater").select(
224
- "seasonality"
225
- )
226
- surface_water_mask = surface_water.gte(10).updateMask(
227
- surface_water.gte(10)
228
- )
229
-
230
- # Flooded layer where perennial water bodies(water > 10 mo / yr) is
231
- # assigned a 0 value
232
- where_surface_water = image.where(surface_water_mask, 0)
233
-
234
- masked_image = image.updateMask(where_surface_water)
235
-
236
- return masked_image
237
-
238
-
239
- def reduce_noise(image):
240
- """
241
- Reduce noise in the image.
242
-
243
- Compute connectivity of pixels to eliminate those connected to 8 or fewer
244
- neighbours.
245
- Inputs:
246
- image (ee.Image): A binary image.
247
-
248
- Returns:
249
- reduced_noise_image (ee.Image): The resulting image after noise
250
- reduction is applied.
251
- """
252
- connections = image.connectedPixelCount()
253
- reduced_noise_image = image.updateMask(connections.gte(8))
254
-
255
- return reduced_noise_image
256
-
257
-
258
- def mask_slopes(image):
259
- """
260
- Mask out areas with more than 5 % slope with a Digital Elevation Model.
261
-
262
- Inputs:
263
- image (ee.Image): Input image.
264
- Returns:
265
- slopes_masked (ee.Image): The resulting image after slope masking is
266
- applied.
267
- """
268
- dem = ee.Image("WWF/HydroSHEDS/03VFDEM")
269
- terrain = ee.Algorithms.Terrain(dem)
270
- slope = terrain.select("slope")
271
- slopes_masked = image.updateMask(slope.lt(5))
272
-
273
- return slopes_masked
274
-
275
-
276
- def derive_flood_extents(
277
- aoi,
278
- before_start_date,
279
- before_end_date,
280
- after_start_date,
281
- after_end_date,
282
- difference_threshold=1.25,
283
- polarization="VH",
284
- pass_direction="Ascending",
285
- export=False,
286
- export_filename="flood_extents",
287
- ):
288
- """
289
- Set start and end dates of a period BEFORE and AFTER a flood.
290
-
291
- These periods need to be long enough for Sentinel-1 to acquire an image.
292
-
293
- Inputs:
294
- aoi (ee.Geometry.Polygon): Geographic extent of analysis area.
295
- before_start_date (str): Date in format yyyy-mm-dd, e.g., '2020-10-01'.
296
- before_end_date (str): Date in format yyyy-mm-dd, e.g., '2020-10-01'.
297
- after_start_date (str): Date in format yyyy-mm-dd, e.g., '2020-10-01'.
298
- after_end_date (str): Date in format yyyy-mm-dd, e.g., '2020-10-01'.
299
- difference_threshold (float): Threshold to be applied on the
300
- differenced image (after flood - before flood). It has been chosen
301
- by trial and error. In case your flood extent result shows many
302
- false-positive or negative signals, consider changing it.
303
- export (bool): Flag to export derived flood extents to Google Drive
304
- export_filename (str): Desired filename prefix for exported files. Only
305
- used if export=True.
306
-
307
- Returns:
308
- flood_vectors (ee.FeatureCollection): Detected flood extents as vector
309
- geometries.
310
- flood_rasters (ee.Image): Detected flood extents as a binary raster.
311
- before_filtered (ee.Image): The 'before' Sentinel-1 image.
312
- after_filtered (ee.Image): The 'after' Sentinel-1 image containing view
313
- of the flood waters.
314
- """
315
- before_flood_img_col = retrieve_image_collection(
316
- search_region=aoi,
317
- start_date=before_start_date,
318
- end_date=before_end_date,
319
- polarization=polarization,
320
- pass_direction=pass_direction,
321
- )
322
- after_flood_img_col = retrieve_image_collection(
323
- search_region=aoi,
324
- start_date=after_start_date,
325
- end_date=after_end_date,
326
- polarization=polarization,
327
- pass_direction=pass_direction,
328
- )
329
-
330
- # Create a mosaic of selected tiles and clip to study area
331
- before_mosaic = before_flood_img_col.mosaic().clip(aoi)
332
- after_mosaic = after_flood_img_col.mosaic().clip(aoi)
333
-
334
- before_filtered = smooth(before_mosaic)
335
- after_filtered = smooth(after_mosaic)
336
-
337
- # Calculate the difference between the before and after images
338
- difference = after_filtered.divide(before_filtered)
339
-
340
- # Apply the predefined difference - threshold and create the flood extent
341
- # mask
342
- difference_binary = difference.gt(difference_threshold)
343
- difference_binary_masked = mask_permanent_water(difference_binary)
344
- difference_binary_masked_reduced_noise = reduce_noise(
345
- difference_binary_masked
346
- )
347
- flood_rasters = mask_slopes(difference_binary_masked_reduced_noise)
348
-
349
- # Export the extent of detected flood in vector format
350
- flood_vectors = flood_rasters.reduceToVectors(
351
- scale=10,
352
- geometryType="polygon",
353
- geometry=aoi,
354
- eightConnected=False,
355
- bestEffort=True,
356
- tileScale=2,
357
- )
358
-
359
- if export:
360
- export_flood_data(
361
- flooded_area_vector=flood_vectors,
362
- flooded_area_raster=flood_rasters,
363
- image_before_flood=before_filtered,
364
- image_after_flood=after_filtered,
365
- region=aoi,
366
- filename=export_filename,
367
- )
368
-
369
- return flood_vectors, flood_rasters, before_filtered, after_filtered
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt CHANGED
@@ -5,3 +5,4 @@ streamlit==1.14.1
5
  streamlit_ext==0.1.4
6
  streamlit-folium==0.7.0
7
  pre-commit==2.18.1
 
 
5
  streamlit_ext==0.1.4
6
  streamlit-folium==0.7.0
7
  pre-commit==2.18.1
8
+ altair<5