File size: 11,880 Bytes
213d2b5
 
 
8d6d0f6
213d2b5
4470c81
213d2b5
8d6d0f6
213d2b5
 
 
8d6d0f6
213d2b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8d6d0f6
 
213d2b5
 
8d6d0f6
 
 
213d2b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8950d78
213d2b5
 
 
8950d78
213d2b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8d6d0f6
213d2b5
 
 
 
8d6d0f6
213d2b5
 
 
 
 
8d6d0f6
 
 
 
213d2b5
 
4470c81
213d2b5
8d6d0f6
 
213d2b5
8d6d0f6
 
 
 
 
 
 
 
 
 
 
 
8909418
8d6d0f6
 
 
 
 
 
 
2a18dc5
8d6d0f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8909418
8d6d0f6
 
 
 
 
 
 
 
 
 
213d2b5
 
8d6d0f6
213d2b5
 
 
 
8d6d0f6
 
 
 
213d2b5
 
8d6d0f6
213d2b5
 
 
4470c81
8d6d0f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213d2b5
 
 
 
8d6d0f6
 
 
213d2b5
 
 
 
8909418
8d6d0f6
213d2b5
4470c81
8d6d0f6
 
 
 
 
 
213d2b5
 
8d6d0f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213d2b5
 
 
 
 
8950d78
8d6d0f6
 
 
 
213d2b5
 
 
 
 
 
 
 
 
8d6d0f6
213d2b5
 
8d6d0f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213d2b5
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
from datetime import date, timedelta

import folium
import pandas as pd
import streamlit as st
from src import hf_utils
from src.config_parameters import params
from src.gfm import get_cached_aois, get_cached_gfm_handler
from src.utils import (
    add_about,
    get_aoi_id_from_selector_preview,
    get_existing_geojson,
    set_tool_page_style,
    toggle_menu_button,
)
from streamlit_folium import st_folium

today = date.today()

default_date_yesterday = today - timedelta(days=1)

# Page configuration
st.set_page_config(layout="wide", page_title=params["browser_title"])

# If app is deployed hide menu button
toggle_menu_button()

# Create sidebar
add_about()

# Page title
st.markdown("# Flood extent analysis")

# Set page style
set_tool_page_style()

# Create two rows: top and bottom panel
row1 = st.container()
row2 = st.container()
# Create two columns in the top panel: input map and paramters
col1, col2, col3, col4 = row1.columns([1, 1, 1, 2])
col2_1, col2_2 = row2.columns([3, 2])

# Retrieve GFM Handler and AOIs to fill AOI selector
gfm = get_cached_gfm_handler()
aois = get_cached_aois()


if "all_products" not in st.session_state:
    st.session_state["all_products"] = None


# To force removing product checkboxes when AOI selector changes
def on_area_selector_change():
    print("Area selector changed, removing product checkboxes")
    st.session_state["all_products"] = None


# Contains AOI selector
with col1:
    selected_area_name_id = st.selectbox(
        "Select saved AOI",
        options=[aoi["name_id_preview"] for aoi in aois.values()],
        on_change=on_area_selector_change,
    )

    selected_area_id = get_aoi_id_from_selector_preview(aois, selected_area_name_id)

# Contain datepickers
with col2:
    today = date.today()
    two_weeks_ago = today - timedelta(days=14)
    start_date = st.date_input("Start date", value=two_weeks_ago)

with col3:
    end_date = st.date_input("End date", value=today)

# Contains available products button
with col4:
    st.text(
        "Button info",
        help="""
    This will show the timestamps of all available GFM products
    that intersect with the AOI for the selected date range.
    Getting the available products is a relatively fast operation
    it will not trigger any product downloads.
    """,
    )
    show_available_products = st.button("Show GFM products")

# If button above is triggered, get products from GFM
# Then save all products to the session state and rerun the app to display them
if show_available_products:
    products = gfm.get_area_products(selected_area_id, start_date, end_date)
    st.session_state["all_products"] = products
    st.rerun()

# Contains the product checkboxes if they exist after pushing the "Show available products" button
with col2_2:
    row_checkboxes = st.container()
    row_buttons = st.container()

with row_checkboxes:
    checkboxes = list()
    # Products are checked against the index to check whether they are already downloaded
    index_df = hf_utils.get_geojson_index_df()
    if st.session_state["all_products"]:
        # Get unique product time groups
        unique_time_groups = set()
        for product in st.session_state["all_products"]:
            unique_time_groups.add(product["product_time_group"])

        # Create dataframe for the table
        product_data = []
        for time_group in sorted(unique_time_groups):
            # Check if any product in this group is already downloaded
            products_in_group = [
                p
                for p in st.session_state["all_products"]
                if p["product_time_group"] == time_group
            ]

            dataset_link = ""
            for product in products_in_group:
                if product["product_id"] in index_df["product"].values:
                    available_status = "Available in Floodmap"
                    flood_geojson_path = index_df.loc[
                        index_df["product"] == product["product_id"],
                        "flood_geojson_path",
                    ].values[0]
                    dataset_link = f"https://huggingface.co/datasets/rodekruis/flood-mapping/resolve/main/{flood_geojson_path}?download=true"

            product_data.append(
                {
                    "Check": False,
                    "Product time": time_group,
                    "Available": dataset_link,
                }
            )

        product_groups_df = pd.DataFrame(product_data)

        # Create the data editor with checkbox column
        product_groups_st_df = st.data_editor(
            product_groups_df,
            column_config={
                "Check": st.column_config.CheckboxColumn(
                    "Select",
                    help="Select products to process",
                    default=False,
                ),
                "Product time": st.column_config.TextColumn(
                    "Product Time Group", disabled=True
                ),
                "Available": st.column_config.LinkColumn("Available in dataset"),
            },
            hide_index=True,
            disabled=["Product time", "Available"],
        )

        # Convert checkbox states to list for compatibility with existing code
        checkboxes = product_groups_st_df["Check"].tolist()

with row_buttons:
    below_checkbox_col1, below_checkbox_col2 = row_buttons.columns([1, 1])

# Contains the "Download Products" button
with below_checkbox_col1:
    st.text(
        "Button info",
        help=""
        """
    Will download the selected products from GFM to the Floodmap app
    (click "Show available products" first if there are none).
    Products that show that they have already been downloaded can be left checked,
    they will be skipped.
    """,
    )
    download_products = st.button("Download to Floodmap")

    # If the button is clicked download all checked products that have not been downloaded yet
    if download_products:
        index_df = hf_utils.get_geojson_index_df()
        # Get selected time groups from the table
        selected_time_groups = product_groups_st_df[product_groups_st_df["Check"]][
            "Product time"
        ].tolist()

        # For each selected time group
        for time_group in selected_time_groups:
            # Get all products for this time group
            products_in_group = [
                p
                for p in st.session_state["all_products"]
                if p["product_time_group"] == time_group
            ]

            # Download each product in the group that hasn't been downloaded yet
            for product_to_download in products_in_group:
                if product_to_download["product_id"] not in index_df["product"].values:
                    with st.spinner(
                        f"Getting GFM files for {product_to_download['product_time']}, this may take a couple of minutes"
                    ):
                        gfm.download_flood_product(
                            selected_area_id, product_to_download
                        )
        st.rerun()

# For all the selected products add them to the map if they are available
feature_groups = []
flood_featuregroup = None
selected_geojsons = []
if st.session_state["all_products"]:
    index_df = hf_utils.get_geojson_index_df()
    # Get unique time groups
    unique_time_groups = sorted(
        set(p["product_time_group"] for p in st.session_state["all_products"])
    )

    # For each checkbox (which corresponds to a time group)
    for i, checkbox in enumerate(checkboxes):
        if checkbox:
            time_group = unique_time_groups[i]
            # Get all products for this time group
            products_in_group = [
                p
                for p in st.session_state["all_products"]
                if p["product_time_group"] == time_group
            ]

            # Create a feature group for this time group
            flood_featuregroup = folium.FeatureGroup(name=time_group)
            footprint_featuregroup = folium.FeatureGroup(name="Sentinel footprint")
            group_has_features = False

            # Add all available products from this group to the feature group
            for product in products_in_group:
                if product["product_id"] in index_df["product"].values:
                    # Get the raw geojsons for further usage in the app
                    flood_geojson = get_existing_geojson(product["product_id"], "flood")
                    selected_geojsons.append(flood_geojson)
                    # Convert geojsons to folium features to display on the map
                    flood_folium_geojson = folium.GeoJson(
                        flood_geojson,
                        style_function=lambda x: {
                            "fillColor": "#ff0000",
                            "color": "#ff0000",
                            "fillOpacity": 0.2,
                        },
                    )
                    flood_featuregroup.add_child(flood_folium_geojson)

                    footprint_geojson = get_existing_geojson(
                        product["product_id"], "footprint"
                    )
                    footprint_folium_geojson = folium.GeoJson(
                        footprint_geojson,
                        style_function=lambda x: {
                            "fillColor": "yellow",
                            "color": "yellow",
                            "fillOpacity": 0.2,
                            "weight": 0,
                        },
                    )
                    footprint_featuregroup.add_child(footprint_folium_geojson)
                    group_has_features = True

            # Only add the feature group if it contains any features
            if group_has_features:
                feature_groups.append(flood_featuregroup)
                feature_groups.append(footprint_featuregroup)

# Contains the map
with col2_1:
    if selected_area_id:
        # display the bounding box
        bounding_box = aois[selected_area_id]["bbox"]
        geojson_selected_area = folium.GeoJson(
            bounding_box,
            style_function=lambda x: {"fillOpacity": 0.2, "weight": 1},
        )
        feat_group_selected_area = folium.FeatureGroup(name="selected_area")
        feat_group_selected_area.add_child(geojson_selected_area)
        feature_groups.append(feat_group_selected_area)

    # Create folium map
    folium_map = folium.Map([39, 0], zoom_start=8)
    folium_map.fit_bounds(feat_group_selected_area.get_bounds())

    m = st_folium(
        folium_map, width=800, height=450, feature_group_to_add=feature_groups
    )

    if flood_featuregroup:
        flood_part_of_legend = """
        <div style="display: flex; align-items: center;">
            <div style="width: 20px; height: 20px; background:  rgba(255, 0, 0, .2); border: 1px solid red;"></div>
            <div style="margin-left: 5px;">Floods</div>
        </div>
        <div style="display: flex; align-items: center;">
            <div style="width: 20px; height: 20px; background:  rgba(255, 255, 0, .2); border: 1px solid yellow;"></div>
            <div style="margin-left: 5px;">Sentinel Footprint</div>
        </div>
        """
    else:
        flood_part_of_legend = ""
    st.markdown(
        f"""
        <div style="display: flex; align-items: center; gap: 20px;">
            <div style="display: flex; align-items: center;">
                <div style="width: 20px; height: 20px; background:  rgba(51, 136, 255, .2); border: 1px solid #3388ff;"></div>
                <div style="margin-left: 5px;">AOI</div>
            </div>
            {flood_part_of_legend}
        </div>
        """,
        unsafe_allow_html=True,
    )


# Keep track of which page we're currently on for page switch events
st.session_state["prev_page"] = "flood_extent"