TCJ21 commited on
Commit
ed0c261
Β·
1 Parent(s): cc1cab1

changes silvia date ranges and aoi merging

Browse files
app/pages/1_🌍_Flood_extent_analysis.py CHANGED
@@ -5,7 +5,7 @@ import folium
5
  import streamlit as st
6
  from folium.plugins import Draw
7
  from src.config_parameters import params
8
- from src.gfm import download_gfm_geojson, get_existing_flood_geojson
9
  from src.utils import (
10
  add_about,
11
  set_tool_page_style,
@@ -28,21 +28,46 @@ st.markdown("# Flood extent analysis")
28
  # Set page style
29
  set_tool_page_style()
30
 
 
 
 
 
31
  # Create two rows: top and bottom panel
32
  row1 = st.container()
33
  # Crate two columns in the top panel: input map and paramters
34
  col1, col2 = row1.columns([2, 1])
35
  feat_group_selected_area = folium.FeatureGroup(name="selected_area")
36
  with col1:
37
- area_type = st.radio(label="", options=["Existing area", "New area"])
 
 
 
 
38
  if area_type == "Existing area":
39
  with open("./bboxes/bboxes.json", "r") as f:
40
  bboxes = json.load(f)
41
  selected_area = st.selectbox("Select saved area", options=bboxes.keys())
42
- geojson_selected_area = folium.GeoJson(bboxes[selected_area])
43
- feat_group_selected_area.add_child(geojson_selected_area)
44
- geojson_flood_area = get_existing_flood_geojson(selected_area)
45
- feat_group_selected_area.add_child(geojson_flood_area)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
  elif area_type == "New area":
48
  new_area_name = st.text_input("Area name")
@@ -51,6 +76,15 @@ with col1:
51
  # Create folium map
52
  # call to render Folium map in Streamlit
53
  folium_map = folium.Map([39, 0], zoom_start=8)
 
 
 
 
 
 
 
 
 
54
  # Add drawing tools to map
55
  if area_type == "New area":
56
  Draw(
@@ -120,23 +154,47 @@ if submitted:
120
 
121
  # Show loader because it will take a while
122
  with st.spinner("Getting GFM files... Please wait..."):
123
- # If a new area save the bounding box
124
  if area_type == "New area":
125
- # Save bounding box
 
 
 
126
  with open("./bboxes/bboxes.json", "r") as f:
127
  bboxes = json.load(f)
128
- selected_area_geojson = m["all_drawings"][-1]
129
- bboxes[new_area_name] = selected_area_geojson
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  with open("./bboxes/bboxes.json", "w") as f:
131
- json.dump(bboxes, f)
132
 
133
- # Download files, for a new area also get coordinates to save to GFM
134
  coords = selected_area_geojson["geometry"]["coordinates"][0]
135
- download_gfm_geojson(area_name, new_coordinates=coords)
 
 
 
 
 
136
 
137
  # For an existing area just get the latest update from GFM
138
  if area_type == "Existing area":
139
- download_gfm_geojson(area_name)
 
 
 
 
140
 
141
  # Display that getting the files is finished
142
  st.markdown("Getting GFM files finished")
 
5
  import streamlit as st
6
  from folium.plugins import Draw
7
  from src.config_parameters import params
8
+ from src.gfm import download_gfm_geojson, get_existing_flood_geojson, sync_cached_data
9
  from src.utils import (
10
  add_about,
11
  set_tool_page_style,
 
28
  # Set page style
29
  set_tool_page_style()
30
 
31
+ # Sync cached data
32
+ # ! WARNING: will erase your output folder
33
+ # # # sync_cached_data()
34
+
35
  # Create two rows: top and bottom panel
36
  row1 = st.container()
37
  # Crate two columns in the top panel: input map and paramters
38
  col1, col2 = row1.columns([2, 1])
39
  feat_group_selected_area = folium.FeatureGroup(name="selected_area")
40
  with col1:
41
+ area_type = st.radio(
42
+ label="Area Type",
43
+ options=["Existing area", "New area"],
44
+ label_visibility="hidden",
45
+ )
46
  if area_type == "Existing area":
47
  with open("./bboxes/bboxes.json", "r") as f:
48
  bboxes = json.load(f)
49
  selected_area = st.selectbox("Select saved area", options=bboxes.keys())
50
+
51
+ # retrieve and select available dates
52
+ if selected_area:
53
+ available_date_ranges = list(
54
+ bboxes[selected_area].keys()
55
+ ) # will be used again below
56
+ selected_date_range = st.selectbox(
57
+ "Select available date range", options=available_date_ranges
58
+ )
59
+
60
+ # display the bounding box
61
+ bounding_box = bboxes[selected_area][selected_date_range]["bounding_box"]
62
+ geojson_selected_area = folium.GeoJson(bounding_box)
63
+ feat_group_selected_area.add_child(geojson_selected_area)
64
+
65
+ # geojson_selected_area = folium.GeoJson(bboxes[selected_area])
66
+ # feat_group_selected_area.add_child(geojson_selected_area)
67
+ geojson_flood_area = get_existing_flood_geojson(
68
+ selected_area, selected_date_range
69
+ )
70
+ feat_group_selected_area.add_child(geojson_flood_area)
71
 
72
  elif area_type == "New area":
73
  new_area_name = st.text_input("Area name")
 
76
  # Create folium map
77
  # call to render Folium map in Streamlit
78
  folium_map = folium.Map([39, 0], zoom_start=8)
79
+ # check if the FeatureGroup has any children (i.e., layers added)
80
+ if len(feat_group_selected_area._children) > 0:
81
+ # if there is data, fit the map to the bounds
82
+ folium_map.fit_bounds(feat_group_selected_area.get_bounds())
83
+ else:
84
+ # if there is no data, set a default view
85
+ # this is necessary to start up the page
86
+ folium_map = folium.Map(location=[39, 0], zoom_start=8)
87
+
88
  # Add drawing tools to map
89
  if area_type == "New area":
90
  Draw(
 
154
 
155
  # Show loader because it will take a while
156
  with st.spinner("Getting GFM files... Please wait..."):
 
157
  if area_type == "New area":
158
+ # Convert date input into a string format for JSON storage
159
+ date_range_str = f"{start_date}_to_{end_date}"
160
+
161
+ # Load existing bboxes
162
  with open("./bboxes/bboxes.json", "r") as f:
163
  bboxes = json.load(f)
164
+
165
+ # Get the drawn area
166
+ selected_area_geojson = m["all_drawings"][-1]
167
+
168
+ # If the area doesn't exist, create it
169
+ if new_area_name not in bboxes:
170
+ bboxes[new_area_name] = {}
171
+
172
+ # Save the new bounding box under the date range key
173
+ bboxes[new_area_name][date_range_str] = {
174
+ "bounding_box": selected_area_geojson,
175
+ "flood_files": [], # Will be populated when files are downloaded
176
+ }
177
+
178
+ # Write the updated data back to file
179
  with open("./bboxes/bboxes.json", "w") as f:
180
+ json.dump(bboxes, f, indent=4)
181
 
182
+ # Download files, also getting coordinates for GFM
183
  coords = selected_area_geojson["geometry"]["coordinates"][0]
184
+ # download_gfm_geojson(area_name, new_coordinates=coords)
185
+ download_gfm_geojson(
186
+ area_name,
187
+ bbox=bboxes[new_area_name][date_range_str]["bounding_box"],
188
+ new_coordinates=coords,
189
+ )
190
 
191
  # For an existing area just get the latest update from GFM
192
  if area_type == "Existing area":
193
+ # download_gfm_geojson(area_name)
194
+ download_gfm_geojson(
195
+ selected_area,
196
+ bbox=bboxes[selected_area][selected_date_range]["bounding_box"],
197
+ )
198
 
199
  # Display that getting the files is finished
200
  st.markdown("Getting GFM files finished")
app/src/gfm.py CHANGED
@@ -1,17 +1,60 @@
1
  import io
2
  import json
3
  import os
 
4
  import zipfile
5
  from pathlib import Path
6
 
7
  import folium
8
  import requests
9
  from dotenv import load_dotenv
 
 
10
 
11
  load_dotenv()
12
 
13
 
14
- def download_gfm_geojson(area_name, new_coordinates=None, output_file_path=None):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  """
16
  Should provide an existing area name or a new area name with new_coordinates
17
  """
@@ -54,6 +97,35 @@ def download_gfm_geojson(area_name, new_coordinates=None, output_file_path=None)
54
  if aoi["aoi_name"] == area_name:
55
  aoi_id = aoi["aoi_id"]
56
  break
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
  # Get product id
59
  prod_url = f"{base_url}/aoi/{aoi_id}/products"
@@ -76,22 +148,41 @@ def download_gfm_geojson(area_name, new_coordinates=None, output_file_path=None)
76
  # Download and unzip file
77
  for f in Path(output_file_path).glob("*"):
78
  f.unlink()
 
79
  r = requests.get(download_link)
 
80
  with zipfile.ZipFile(io.BytesIO(r.content)) as z:
81
  z.extractall(str(Path(output_file_path)))
 
82
 
83
 
84
- def get_existing_flood_geojson(area_name, output_file_path=None):
85
  """
86
- Getting a saved GFM flood geojson in an output folder of GFM files
87
  """
 
88
  if not output_file_path:
89
- output_file_path = f"./output/{area_name}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
 
91
- for f in Path(output_file_path).glob("*"):
92
- if "FLOOD" in str(f) and "geojson" in str(f):
93
- geojson_path = f
94
- break
95
 
96
- flood_geojson = folium.GeoJson(json.load(open(geojson_path)))
97
- return flood_geojson
 
 
 
1
  import io
2
  import json
3
  import os
4
+ import warnings
5
  import zipfile
6
  from pathlib import Path
7
 
8
  import folium
9
  import requests
10
  from dotenv import load_dotenv
11
+ from shapely.geometry import MultiPolygon, shape
12
+ from shapely.ops import unary_union
13
 
14
  load_dotenv()
15
 
16
 
17
+ class FloodGeoJsonError(Exception):
18
+ """Custom exception for errors in fetching flood GeoJSON files."""
19
+
20
+ pass
21
+
22
+
23
+ def sync_cached_data(bboxes_path="./bboxes/bboxes.json", output_dir="./output"):
24
+ """
25
+ Ensures that all areas in bboxes.json have a corresponding folder in ./output/.
26
+ Removes any area entry from bboxes.json that does not have an output folder.
27
+ """
28
+ try:
29
+ # Load existing bounding boxes
30
+ with open(bboxes_path, "r") as f:
31
+ bboxes = json.load(f)
32
+
33
+ # Get a set of existing output folders
34
+ existing_folders = {
35
+ folder.name for folder in Path(output_dir).iterdir() if folder.is_dir()
36
+ }
37
+
38
+ # Remove entries from bboxes.json if the folder does not exist
39
+ updated_bboxes = {
40
+ area: data for area, data in bboxes.items() if area in existing_folders
41
+ }
42
+
43
+ # If changes were made, overwrite bboxes.json
44
+ if len(updated_bboxes) != len(bboxes):
45
+ with open(bboxes_path, "w") as f:
46
+ json.dump(updated_bboxes, f, indent=4)
47
+ print(f"Updated {bboxes_path}: Removed missing areas.")
48
+ else:
49
+ print("All areas have matching folders.")
50
+
51
+ except FileNotFoundError:
52
+ print(f"Error: {bboxes_path} not found.")
53
+ except json.JSONDecodeError:
54
+ print(f"Error: {bboxes_path} is not a valid JSON file.")
55
+
56
+
57
+ def download_gfm_geojson(area_name, bbox, new_coordinates=None, output_file_path=None):
58
  """
59
  Should provide an existing area name or a new area name with new_coordinates
60
  """
 
97
  if aoi["aoi_name"] == area_name:
98
  aoi_id = aoi["aoi_id"]
99
  break
100
+ # print(aoi)
101
+
102
+ # # Collect all matching AOIs (same name)
103
+ # matching_geometries = []
104
+ # for aoi in response.json()["aois"]:
105
+ # if aoi["aoi_name"] == area_name:
106
+ # geojson_geometry = aoi["geoJSON"]["geometry"]
107
+ # matching_geometries.append(shape(geojson_geometry))
108
+
109
+ # if not matching_geometries:
110
+ # raise ValueError(f"No AOIs found for area name: {area_name}")
111
+
112
+ # # Merge all matching AOI geometries into a single unified polygon
113
+ # merged_geometry = unary_union(matching_geometries)
114
+
115
+ # # Handle MultiPolygon cases (if AOIs are disjointed)
116
+ # if merged_geometry.geom_type == "MultiPolygon":
117
+ # merged_geometry = MultiPolygon([p for p in merged_geometry])
118
+
119
+ # # Convert back to GeoJSON
120
+ # merged_geojson = {
121
+ # "type": "Feature",
122
+ # "properties": {"aoi_name": area_name},
123
+ # "geometry": json.loads(json.dumps(merged_geometry.__geo_interface__)),
124
+ # }
125
+
126
+ # print(f"Merged {len(matching_geometries)} AOIs into one for '{area_name}'.")
127
+
128
+ # return merged_geojson
129
 
130
  # Get product id
131
  prod_url = f"{base_url}/aoi/{aoi_id}/products"
 
148
  # Download and unzip file
149
  for f in Path(output_file_path).glob("*"):
150
  f.unlink()
151
+ print("Donwloading...")
152
  r = requests.get(download_link)
153
+ print("Extracting zip...")
154
  with zipfile.ZipFile(io.BytesIO(r.content)) as z:
155
  z.extractall(str(Path(output_file_path)))
156
+ print("Done!")
157
 
158
 
159
+ def get_existing_flood_geojson(area_name, date_range, output_file_path=None):
160
  """
161
+ Getting a saved GFM flood geojson in an output folder of GFM files. Merge in one feature group if multiple.
162
  """
163
+
164
  if not output_file_path:
165
+ output_file_path = f"./output/{area_name}/{date_range}"
166
+
167
+ # Ensure the output directory exists
168
+ # if not Path(output_file_path).exists():
169
+ # raise FloodGeoJsonError(f"Error: Output folder '{output_file_path}' does not exist.")
170
+
171
+ # Combine multiple flood files into a FeatureGroup
172
+ flood_geojson_group = folium.FeatureGroup(name=f"{area_name} Floods {date_range}")
173
+
174
+ for flood_file in Path(output_file_path).glob("*FLOOD*.geojson"):
175
+ with open(flood_file, "r") as f:
176
+ geojson_data = json.load(f)
177
+ flood_layer = folium.GeoJson(geojson_data)
178
+ flood_geojson_group.add_child(flood_layer)
179
+
180
+ # TODO: consider merging multiple flood layers into one, to avoid overlap
181
+
182
+ return flood_geojson_group
183
 
 
 
 
 
184
 
185
+ if __name__ == "__main__":
186
+ # download_gfm_geojson('Catania')
187
+ gj = get_existing_flood_geojson("Albufera Floods")
188
+ print(type(gj))