YashMK89 commited on
Commit
116725d
·
verified ·
1 Parent(s): 4d1a690

update app.py

Browse files
Files changed (1) hide show
  1. app.py +196 -103
app.py CHANGED
@@ -7,8 +7,6 @@ import geopandas as gpd
7
  from datetime import datetime
8
  import leafmap.foliumap as leafmap
9
  import re
10
- from fastkml import kml
11
- from shapely.geometry import Point, Polygon
12
 
13
  # Set up the page layout
14
  st.set_page_config(layout="wide")
@@ -41,7 +39,8 @@ st.markdown(
41
  f"""
42
  <h1 style="text-align: center;">Precision Analysis for Vegetation, Water, and Air Quality</h1>
43
  """,
44
- unsafe_allow_html=True)
 
45
  st.write("<h2><div style='text-align: center;'>User Inputs</div></h2>", unsafe_allow_html=True)
46
 
47
  # Authenticate and initialize Earth Engine
@@ -88,74 +87,37 @@ elif index_choice.lower() == 'custom formula':
88
  custom_formula = st.text_input("Enter Custom Formula (e.g., '(B5 - B4) / (B5 + B4)')")
89
  st.write(f"Custom Formula: {custom_formula}") # Display the custom formula after the user inputs it
90
 
91
- # Aggregation Options
92
- aggregation_type = st.selectbox("Select Aggregation Type", ["Daily", "Weekly", "Monthly", "Yearly"])
93
 
94
- # Function to calculate NDVI and NDWI as before, with added aggregation logic
95
- def calculate_ndvi(image, geometry):
96
- ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
97
- return ndvi.reduceRegion(reducer=ee.Reducer.mean(), geometry=geometry, scale=30)
 
 
 
98
 
99
- def calculate_ndwi(image, geometry):
100
- ndwi = image.normalizedDifference(['B3', 'B8']).rename('NDWI')
101
- return ndwi.reduceRegion(reducer=ee.Reducer.mean(), geometry=geometry, scale=30)
102
-
103
- # Function to calculate averages for NDVI, NDWI, NO2 based on aggregation type
104
- def aggregate_data_daily(collection, roi):
105
- def daily_average(image):
106
- date = ee.Date(image.get('system:time_start')).format('YYYY-MM-dd')
107
- mean_ndvi = image.select('NDVI').reduceRegion(reducer=ee.Reducer.mean(), geometry=roi, scale=30)
108
- mean_ndwi = image.select('NDWI').reduceRegion(reducer=ee.Reducer.mean(), geometry=roi, scale=30)
109
- return ee.Feature(None, {'date': date, 'mean_ndvi': mean_ndvi.get('NDVI'), 'mean_ndwi': mean_ndwi.get('NDWI')})
110
-
111
- return collection.map(daily_average)
112
-
113
- # Process data based on aggregation choice
114
- def process_aggregation(collection, roi):
115
- if aggregation_type.lower() == "daily":
116
- return aggregate_data_daily(collection, roi)
117
-
118
- # Function to calculate index and return results based on aggregation choice
119
- def process_index_calculation(image, roi):
120
- result = None
121
- if index_choice == 'NDVI':
122
- result = calculate_ndvi(image, roi)
123
- elif index_choice == 'NDWI':
124
- result = calculate_ndwi(image, roi)
125
- elif index_choice == 'Average NO₂':
126
- result = calculate_avg_no2_sentinel5p(image, roi)
127
- elif index_choice.lower() == 'custom formula' and custom_formula:
128
- result = process_custom_formula(image, roi, custom_formula)
129
- return result
130
-
131
- # Function to read CSV files
132
- def read_csv(file):
133
- # Assuming the CSV file has 'latitude' and 'longitude' columns
134
- df = pd.read_csv(file)
135
  return df
136
 
137
- # Function to read GeoJSON files
138
- def read_geojson(file):
139
- geo_df = gpd.read_file(file)
140
- return geo_df
141
-
142
- # Function to read KML files
143
- def read_kml(file):
144
- kml_doc = file.read()
145
- k = kml.KML()
146
- k.from_string(kml_doc)
147
- features = list(k.features())
148
- geometries = []
149
- for feature in features:
150
- for geometry in feature.geometry():
151
- geometries.append(geometry)
152
- return geometries
153
-
154
- # User Input for Geometry Type: Point or Polygon
155
- geometry_type = st.selectbox("Select Geometry Type", ["Point", "Polygon"])
156
-
157
- # File upload based on geometry type
158
- file_upload = st.file_uploader(f"Upload your {geometry_type} data (CSV, GeoJSON, KML)", type=["csv", "geojson", "kml"])
159
 
160
  # Date Input for Start and End Dates
161
  start_date = st.date_input("Start Date", value=pd.to_datetime('2020-01-01'))
@@ -170,66 +132,197 @@ if 'results' not in st.session_state:
170
  st.session_state.results = []
171
  if 'last_params' not in st.session_state:
172
  st.session_state.last_params = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
 
174
- # Process the file based on geometry type (Point or Polygon)
175
  locations_df = None
 
 
 
176
  if file_upload:
177
  file_extension = os.path.splitext(file_upload.name)[1].lower()
178
 
179
- if geometry_type == 'Point':
180
  if file_extension == '.csv':
181
  locations_df = read_csv(file_upload)
182
  elif file_extension == '.geojson':
183
  locations_df = read_geojson(file_upload)
184
  elif file_extension == '.kml':
185
  locations_df = read_kml(file_upload)
186
-
187
- elif geometry_type == 'Polygon':
 
188
  if file_extension == '.geojson':
189
- locations_df = read_geojson(file_upload)
190
  elif file_extension == '.kml':
191
- locations_df = read_kml(file_upload)
 
 
192
 
193
  if locations_df is not None and not locations_df.empty:
194
- for idx, row in locations_df.iterrows():
195
- if geometry_type == 'Point':
 
 
 
 
 
 
 
 
196
  latitude = row['latitude']
197
  longitude = row['longitude']
 
 
 
198
  roi = ee.Geometry.Point([longitude, latitude])
199
- elif geometry_type == 'Polygon':
200
- # For Polygon, create a geometry object directly from GeoJSON or KML
201
- roi = ee.Geometry.Polygon(row['geometry']['coordinates'])
202
-
203
- collection = ee.ImageCollection(sub_options[sub_selection]) \
204
- .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
205
- .filterBounds(roi)
206
-
207
- # Apply aggregation based on user's selection
208
- aggregated_data = process_aggregation(collection, roi)
209
-
210
- # Convert the aggregated data to a list of dictionaries (to show in DataFrame)
211
- aggregated_data_list = aggregated_data.getInfo()
212
-
213
- # Further processing on the aggregated data (e.g., storing results)
214
- for data in aggregated_data_list:
215
- st.session_state.results.append({
216
- 'Location Name': row.get('name', f"Location_{idx}"),
217
- 'Date': data['date'],
218
- 'Mean NDVI': data['mean_ndvi'],
219
- 'Mean NDWI': data['mean_ndwi']
220
- })
221
-
222
- # After processing, show the results (same as your previous implementation)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
223
  if st.session_state.results:
224
  result_df = pd.DataFrame(st.session_state.results)
225
- st.write("Processed Results Table:")
226
- st.dataframe(result_df)
227
 
228
- filename = f"{main_selection}_{sub_selection}_{start_date.strftime('%Y/%m/%d')}_{end_date.strftime('%Y/%m/%d')}_{geometry_type}_daily.csv"
 
 
 
 
 
 
229
 
230
  st.download_button(
231
  label="Download results as CSV",
232
- data=result_df.to_csv(index=False).encode('utf-8'),
233
  file_name=filename,
234
  mime='text/csv'
235
  )
 
7
  from datetime import datetime
8
  import leafmap.foliumap as leafmap
9
  import re
 
 
10
 
11
  # Set up the page layout
12
  st.set_page_config(layout="wide")
 
39
  f"""
40
  <h1 style="text-align: center;">Precision Analysis for Vegetation, Water, and Air Quality</h1>
41
  """,
42
+ unsafe_allow_html=True,
43
+ )
44
  st.write("<h2><div style='text-align: center;'>User Inputs</div></h2>", unsafe_allow_html=True)
45
 
46
  # Authenticate and initialize Earth Engine
 
87
  custom_formula = st.text_input("Enter Custom Formula (e.g., '(B5 - B4) / (B5 + B4)')")
88
  st.write(f"Custom Formula: {custom_formula}") # Display the custom formula after the user inputs it
89
 
90
+ # Add Aggregation Option (Daily, Weekly, Monthly, Yearly)
91
+ aggregation_choice = st.selectbox("Select Aggregation Period", ['Daily', 'Weekly', 'Monthly', 'Yearly'])
92
 
93
+ # Function to check if the polygon geometry is valid and convert it to the correct format
94
+ def convert_to_ee_geometry(geometry):
95
+ if geometry.is_valid:
96
+ geojson = geometry.__geo_interface__
97
+ return ee.Geometry(geojson)
98
+ else:
99
+ raise ValueError("Invalid geometry: The polygon geometry is not valid.")
100
 
101
+ # Function to read points from CSV
102
+ def read_csv(file_path):
103
+ df = pd.read_csv(file_path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  return df
105
 
106
+ # Function to read points from GeoJSON
107
+ def read_geojson(file_path):
108
+ gdf = gpd.read_file(file_path)
109
+ return gdf
110
+
111
+ # Function to read points from KML
112
+ def read_kml(file_path):
113
+ gdf = gpd.read_file(file_path, driver='KML')
114
+ return gdf
115
+
116
+ # Ask user whether they want to process 'Point' or 'Polygon' data (case-insensitive)
117
+ shape_type = st.selectbox("Do you want to process 'Point' or 'Polygon' data?", ["Point", "Polygon"])
118
+
119
+ # Ask user to upload a file based on shape type (case-insensitive)
120
+ file_upload = st.file_uploader(f"Upload your {shape_type} data (CSV, GeoJSON, KML)", type=["csv", "geojson", "kml"])
 
 
 
 
 
 
 
121
 
122
  # Date Input for Start and End Dates
123
  start_date = st.date_input("Start Date", value=pd.to_datetime('2020-01-01'))
 
132
  st.session_state.results = []
133
  if 'last_params' not in st.session_state:
134
  st.session_state.last_params = {}
135
+ if 'map_data' not in st.session_state:
136
+ st.session_state.map_data = None # Initialize map_data
137
+
138
+ # Function to check if parameters have changed
139
+ def parameters_changed():
140
+ return (
141
+ st.session_state.last_params.get('main_selection') != main_selection or
142
+ st.session_state.last_params.get('sub_selection') != sub_selection or
143
+ st.session_state.last_params.get('index_choice') != index_choice or
144
+ st.session_state.last_params.get('start_date_str') != start_date_str or
145
+ st.session_state.last_params.get('end_date_str') != end_date_str or
146
+ st.session_state.last_params.get('shape_type') != shape_type or
147
+ st.session_state.last_params.get('file_upload') != file_upload or
148
+ st.session_state.last_params.get('aggregation_choice') != aggregation_choice
149
+ )
150
+
151
+ # If parameters have changed, reset the results
152
+ if parameters_changed():
153
+ st.session_state.results = [] # Clear the previous results
154
+ st.session_state.last_params = {
155
+ 'main_selection': main_selection,
156
+ 'sub_selection': sub_selection,
157
+ 'index_choice': index_choice,
158
+ 'start_date_str': start_date_str,
159
+ 'end_date_str': end_date_str,
160
+ 'shape_type': shape_type,
161
+ 'file_upload': file_upload,
162
+ 'aggregation_choice': aggregation_choice
163
+ }
164
+
165
+ # Function to perform index calculations
166
+ def calculate_ndvi(image, geometry):
167
+ ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
168
+ result = ndvi.reduceRegion(
169
+ reducer=ee.Reducer.mean(),
170
+ geometry=geometry,
171
+ scale=30
172
+ )
173
+ return result.get('NDVI')
174
+
175
+ def calculate_ndwi(image, geometry):
176
+ ndwi = image.normalizedDifference(['B3', 'B8']).rename('NDWI')
177
+ result = ndwi.reduceRegion(
178
+ reducer=ee.Reducer.mean(),
179
+ geometry=geometry,
180
+ scale=30
181
+ )
182
+ return result.get('NDWI')
183
+
184
+ def calculate_avg_no2_sentinel5p(image, geometry):
185
+ no2 = image.select('NO2').reduceRegion(
186
+ reducer=ee.Reducer.mean(),
187
+ geometry=geometry,
188
+ scale=1000
189
+ ).get('NO2')
190
+ return no2
191
+
192
+ def calculate_custom_formula(image, geometry, formula, scale=30):
193
+ band_names = image.bandNames().getInfo()
194
+ band_dict = {band: image.select(band) for band in band_names}
195
+ result_image = image.expression(formula, band_dict).rename('CustomResult')
196
+
197
+ result = result_image.reduceRegion(
198
+ reducer=ee.Reducer.mean(),
199
+ geometry=geometry,
200
+ scale=scale
201
+ )
202
+ return result
203
+
204
+ # Function to aggregate results by day, week, month, or year
205
+ def aggregate_results_by_period(results, aggregation_period):
206
+ # Convert 'date' column to datetime for proper aggregation
207
+ results['date'] = pd.to_datetime(results['date'])
208
+ if aggregation_period == 'Daily':
209
+ return results.groupby(results['date'].dt.date).mean().reset_index()
210
+ elif aggregation_period == 'Weekly':
211
+ return results.groupby(results['date'].dt.to_period('W')).mean().reset_index()
212
+ elif aggregation_period == 'Monthly':
213
+ return results.groupby(results['date'].dt.to_period('M')).mean().reset_index()
214
+ elif aggregation_period == 'Yearly':
215
+ return results.groupby(results['date'].dt.to_period('A')).mean().reset_index()
216
+
217
+ # Function to get the most recent image from the collection
218
+ def get_most_recent_image(image_collection):
219
+ image = image_collection.sort('system:time_start', False).first()
220
+ return image
221
 
 
222
  locations_df = None
223
+ polygons_df = None
224
+
225
+ # Process each point (with additional checks for file validity)
226
  if file_upload:
227
  file_extension = os.path.splitext(file_upload.name)[1].lower()
228
 
229
+ if shape_type == 'Point':
230
  if file_extension == '.csv':
231
  locations_df = read_csv(file_upload)
232
  elif file_extension == '.geojson':
233
  locations_df = read_geojson(file_upload)
234
  elif file_extension == '.kml':
235
  locations_df = read_kml(file_upload)
236
+ else:
237
+ st.error("Unsupported file type. Please upload a CSV, GeoJSON, or KML file for points.")
238
+ elif shape_type == 'Polygon':
239
  if file_extension == '.geojson':
240
+ polygons_df = read_geojson(file_upload)
241
  elif file_extension == '.kml':
242
+ polygons_df = read_kml(file_upload)
243
+ else:
244
+ st.error("Unsupported file type. Please upload a GeoJSON or KML file for polygons.")
245
 
246
  if locations_df is not None and not locations_df.empty:
247
+ # Ensure the necessary columns exist in the dataframe
248
+ if 'latitude' not in locations_df.columns or 'longitude' not in locations_df.columns:
249
+ st.error("Uploaded file is missing required 'latitude' or 'longitude' columns.")
250
+ else:
251
+ # Display a preview of the points data
252
+ st.write("Preview of the uploaded points data:")
253
+ st.dataframe(locations_df.head())
254
+
255
+ # Process each point for index calculation
256
+ for idx, row in locations_df.iterrows():
257
  latitude = row['latitude']
258
  longitude = row['longitude']
259
+ location_name = row.get('name', f"Location_{idx}")
260
+
261
+ # Define the region of interest (ROI)
262
  roi = ee.Geometry.Point([longitude, latitude])
263
+
264
+ # Load Sentinel-2 image collection
265
+ collection = ee.ImageCollection(sub_options[sub_selection]) \
266
+ .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
267
+ .filterBounds(roi)
268
+
269
+ # Aggregate results by the chosen period
270
+ if aggregation_choice == 'Daily':
271
+ collection = collection.filterDate(ee.Date(start_date_str), ee.Date(end_date_str))
272
+ elif aggregation_choice == 'Weekly':
273
+ collection = collection.filterDate(ee.Date(start_date_str), ee.Date(end_date_str))
274
+ elif aggregation_choice == 'Monthly':
275
+ collection = collection.filterDate(ee.Date(start_date_str), ee.Date(end_date_str))
276
+ elif aggregation_choice == 'Yearly':
277
+ collection = collection.filterDate(ee.Date(start_date_str), ee.Date(end_date_str))
278
+
279
+ # Get the most recent image in the collection
280
+ image = get_most_recent_image(collection)
281
+ if not image:
282
+ st.warning(f"No images found for {location_name}.")
283
+ else:
284
+ st.write(f"Found images for {location_name}.")
285
+ # Perform the calculation based on user selection
286
+ result = None
287
+ if index_choice == 'NDVI':
288
+ result = calculate_ndvi(image, roi)
289
+ elif index_choice == 'NDWI':
290
+ result = calculate_ndwi(image, roi)
291
+ elif index_choice == 'Average NO₂':
292
+ if 'NO2' in image.bandNames().getInfo():
293
+ result = calculate_avg_no2_sentinel5p(image, roi)
294
+ else:
295
+ st.warning(f"No NO2 band found for {location_name}. Please use Sentinel-5P for NO₂ data.")
296
+ elif index_choice.lower() == 'custom formula' and custom_formula:
297
+ result = calculate_custom_formula(image, roi, custom_formula)
298
+
299
+ if result is not None:
300
+ # Get the date from the image's metadata
301
+ date = image.date().format().getInfo()
302
+ # Append the result with date to the results list
303
+ st.session_state.results.append({
304
+ 'Location Name': location_name,
305
+ 'Latitude': latitude,
306
+ 'Longitude': longitude,
307
+ 'Date': date,
308
+ 'Calculated Value': result.getInfo()
309
+ })
310
+
311
+ # After processing, show the results
312
  if st.session_state.results:
313
  result_df = pd.DataFrame(st.session_state.results)
 
 
314
 
315
+ # Aggregate by the selected period
316
+ aggregated_results = aggregate_results_by_period(result_df, aggregation_choice)
317
+
318
+ st.write(f"Processed Results Table ({aggregation_choice}):")
319
+ st.dataframe(aggregated_results)
320
+
321
+ filename = f"{main_selection}_{sub_selection}_{start_date.strftime('%Y/%m/%d')}_{end_date.strftime('%Y/%m/%d')}_{shape_type}_{aggregation_choice}.csv"
322
 
323
  st.download_button(
324
  label="Download results as CSV",
325
+ data=aggregated_results.to_csv(index=False).encode('utf-8'),
326
  file_name=filename,
327
  mime='text/csv'
328
  )