YashMK89 commited on
Commit
f86a704
·
verified ·
1 Parent(s): e2914ee

update app.py

Browse files
Files changed (1) hide show
  1. app.py +186 -80
app.py CHANGED
@@ -12,7 +12,7 @@ import re
12
  st.set_page_config(layout="wide")
13
 
14
  # Custom button styling
15
- st.markdown(
16
  """
17
  <style>
18
  div.stButton > button:first-child {
@@ -25,7 +25,7 @@ st.markdown(
25
 
26
  # Logo
27
  st.write(
28
- """
29
  <div style="display: flex; justify-content: space-between; align-items: center;">
30
  <img src="https://huggingface.co/spaces/YashMK89/GEE_Calculator/resolve/main/ISRO_Logo.png" style="width: 20%; margin-right: auto;">
31
  <img src="https://huggingface.co/spaces/YashMK89/GEE_Calculator/resolve/main/SAC_Logo.png" style="width: 20%; margin-left: auto;">
@@ -36,7 +36,9 @@ st.write(
36
 
37
  # Title
38
  st.markdown(
39
- "<h1 style='text-align: center;'>Precision Analysis for Vegetation, Water, and Air Quality</h1>",
 
 
40
  unsafe_allow_html=True,
41
  )
42
  st.write("<h2><div style='text-align: center;'>User Inputs</div></h2>", unsafe_allow_html=True)
@@ -85,6 +87,13 @@ elif index_choice.lower() == 'custom formula':
85
  custom_formula = st.text_input("Enter Custom Formula (e.g., '(B5 - B4) / (B5 + B4)')")
86
  st.write(f"Custom Formula: {custom_formula}") # Display the custom formula after the user inputs it
87
 
 
 
 
 
 
 
 
88
  # Function to check if the polygon geometry is valid and convert it to the correct format
89
  def convert_to_ee_geometry(geometry):
90
  if geometry.is_valid:
@@ -155,65 +164,137 @@ if parameters_changed():
155
  'file_upload': file_upload
156
  }
157
 
158
- # Function to perform index calculations
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  def calculate_ndvi(image, geometry):
160
  ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
 
 
161
  result = ndvi.reduceRegion(
162
- reducer=ee.Reducer.mean(),
163
  geometry=geometry,
164
  scale=30
165
  )
166
- return result.get('NDVI')
 
 
 
 
 
 
 
 
 
167
 
 
 
168
  def calculate_ndwi(image, geometry):
169
  ndwi = image.normalizedDifference(['B3', 'B8']).rename('NDWI')
170
  result = ndwi.reduceRegion(
171
- reducer=ee.Reducer.mean(),
172
  geometry=geometry,
173
  scale=30
174
  )
175
- return result.get('NDWI')
 
 
 
 
 
 
 
 
 
176
 
 
177
  def calculate_avg_no2_sentinel5p(image, geometry):
178
  no2 = image.select('NO2').reduceRegion(
179
- reducer=ee.Reducer.mean(),
180
  geometry=geometry,
181
  scale=1000
182
- ).get('NO2')
183
- return no2
 
 
 
 
 
 
 
 
 
184
 
 
185
  def calculate_custom_formula(image, geometry, formula, scale=30):
 
 
 
 
 
186
  band_names = image.bandNames().getInfo()
187
  band_dict = {band: image.select(band) for band in band_names}
188
 
 
189
  result_image = image.expression(formula, band_dict).rename('CustomResult')
190
 
 
191
  result = result_image.reduceRegion(
192
- reducer=ee.Reducer.mean(),
193
  geometry=geometry,
194
  scale=scale
195
- )
196
 
197
- return result
 
 
 
 
 
 
 
 
198
 
199
  # Function to get the most recent image from the collection
200
  def get_most_recent_image(image_collection):
201
  image = image_collection.sort('system:time_start', False).first()
202
  return image
203
 
204
- # Function to handle the custom formula choice and calculation
205
- def process_custom_formula(image, roi, custom_formula):
206
- if custom_formula:
207
- result = calculate_custom_formula(image, roi, custom_formula)
208
- if result:
209
- return result.getInfo()
210
- return None
211
 
212
  locations_df = None # Initialize locations_df to None
213
  polygons_df = None # Ensure polygons_df is initialized at the beginning
214
 
215
  # Process each point (with additional checks for file validity)
 
216
  if file_upload:
 
 
 
217
  file_extension = os.path.splitext(file_upload.name)[1].lower()
218
 
219
  # Read file based on shape type
@@ -227,45 +308,58 @@ if file_upload:
227
  else:
228
  st.error("Unsupported file type. Please upload a CSV, GeoJSON, or KML file for points.")
229
  elif shape_type == 'Polygon':
230
- if file _extension == '.geojson':
231
  polygons_df = read_geojson(file_upload)
232
  elif file_extension == '.kml':
233
  polygons_df = read_kml(file_upload)
234
  else:
235
  st.error("Unsupported file type. Please upload a GeoJSON or KML file for polygons.")
236
 
 
237
  if locations_df is not None and not locations_df.empty:
 
238
  if 'latitude' not in locations_df.columns or 'longitude' not in locations_df.columns:
239
  st.error("Uploaded file is missing required 'latitude' or 'longitude' columns.")
240
  else:
 
241
  st.write("Preview of the uploaded points data:")
242
  st.dataframe(locations_df.head())
243
 
 
244
  m = leafmap.Map(center=[locations_df['latitude'].mean(), locations_df['longitude'].mean()], zoom=10)
245
 
 
246
  for _, row in locations_df.iterrows():
247
  latitude = row['latitude']
248
  longitude = row['longitude']
249
 
 
250
  if pd.isna(latitude) or pd.isna(longitude):
251
- continue
252
 
253
  m.add_marker(location=[latitude, longitude], popup=row.get('name', 'No Name'))
254
 
 
255
  st.write("Map of Uploaded Points:")
256
  m.to_streamlit()
 
 
257
  st.session_state.map_data = m
258
 
 
259
  for idx, row in locations_df.iterrows():
260
  latitude = row['latitude']
261
  longitude = row['longitude']
262
  location_name = row.get('name', f"Location_{idx}")
263
 
 
264
  if pd.isna(latitude) or pd.isna(longitude):
265
- continue
266
 
 
267
  roi = ee.Geometry.Point([longitude, latitude])
268
 
 
269
  collection = ee.ImageCollection(sub_options[sub_selection]) \
270
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
271
  .filterBounds(roi)
@@ -275,6 +369,8 @@ if file_upload:
275
  st.warning(f"No images found for {location_name}.")
276
  else:
277
  st.write(f"Found images for {location_name}.")
 
 
278
  result = None
279
  if index_choice == 'NDVI':
280
  result = calculate_ndvi(image, roi)
@@ -288,17 +384,22 @@ if file_upload:
288
  elif index_choice.lower() == 'custom formula' and custom_formula:
289
  result = process_custom_formula(image, roi, custom_formula)
290
 
 
291
  if result is not None:
292
- calculated_value = None
293
 
 
294
  if isinstance(result, dict):
295
- calculated_value = result.get('CustomResult', None)
 
296
  else:
297
  try:
 
298
  calculated_value = result.getInfo()
299
  except Exception as e:
300
  st.error(f"Error getting result info: {e}")
301
-
 
302
  if calculated_value is not None:
303
  st.session_state.results.append({
304
  'Location Name': location_name,
@@ -310,65 +411,70 @@ if file_upload:
310
  st.warning(f"No value calculated for {location_name}.")
311
  else:
312
  st.warning(f"No value calculated for {location_name}.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
 
314
- if polygons_df is not None:
315
- st.write("Preview of the uploaded polygons data:")
316
- st.dataframe(polygons_df.head())
317
-
318
- m = leafmap.Map(center=[polygons_df.geometry.centroid.y.mean(), polygons_df.geometry.centroid.x.mean()], zoom=10)
319
-
320
- for _, row in polygons_df.iterrows():
321
- polygon = row['geometry']
322
- if polygon.is_valid:
323
- gdf = gpd.GeoDataFrame([row], geometry=[polygon], crs=polygons_df.crs)
324
- m.add_gdf(gdf=gdf, layer_name=row.get('name', 'Unnamed Polygon'))
325
-
326
- st.write("Map of Uploaded Polygons:")
327
- m.to_streamlit()
328
- st.session_state.map_data = m
329
-
330
- for idx, row in polygons_df.iterrows():
331
- polygon = row['geometry']
332
- location_name = row.get('name', f"Polygon_{idx}")
333
-
334
- try:
335
- roi = convert_to_ee_geometry(polygon)
336
- except ValueError as e:
337
- st.error(str(e))
338
- continue
339
-
340
- collection = ee.ImageCollection(sub_options[sub_selection]) \
341
- .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
342
- .filterBounds(roi)
343
-
344
- image = get_most_recent_image(collection)
345
 
346
- if not image:
347
- st.warning(f"No images found for {location_name}.")
348
- else:
349
- st.write(f"Found an image for {location_name}.")
350
- result = None
351
- if index_choice.lower() == 'ndvi':
352
- result = calculate_ndvi(image, roi)
353
- elif index_choice.lower() ```python
354
- == 'ndwi':
355
- result = calculate_ndwi(image, roi)
356
- elif index_choice.lower() == 'average no₂':
357
- if 'NO2' in image.bandNames().getInfo():
358
- result = calculate_avg_no2_sentinel5p(image, roi)
359
- else:
360
- st.warning(f"No NO2 band found for {location_name}. Please use Sentinel-5P for NO₂ data.")
361
- elif index_choice.lower() == 'custom formula' and custom_formula:
362
- result = process_custom_formula(image, roi, custom_formula)
363
 
364
- if result is not None:
365
- calculated_value = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
 
 
 
 
 
 
367
  if isinstance(result, dict) and 'CustomResult' in result:
368
- calculated_value = result['CustomResult']
 
369
  elif isinstance(result, (int, float)):
370
  calculated_value = result
371
-
 
372
  if calculated_value is not None:
373
  st.session_state.results.append({
374
  'Location Name': location_name,
@@ -393,4 +499,4 @@ if st.session_state.results:
393
  data=result_df.to_csv(index=False).encode('utf-8'),
394
  file_name=filename,
395
  mime='text/csv'
396
- )
 
12
  st.set_page_config(layout="wide")
13
 
14
  # Custom button styling
15
+ m = st.markdown(
16
  """
17
  <style>
18
  div.stButton > button:first-child {
 
25
 
26
  # Logo
27
  st.write(
28
+ f"""
29
  <div style="display: flex; justify-content: space-between; align-items: center;">
30
  <img src="https://huggingface.co/spaces/YashMK89/GEE_Calculator/resolve/main/ISRO_Logo.png" style="width: 20%; margin-right: auto;">
31
  <img src="https://huggingface.co/spaces/YashMK89/GEE_Calculator/resolve/main/SAC_Logo.png" style="width: 20%; margin-left: auto;">
 
36
 
37
  # Title
38
  st.markdown(
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)
 
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
+ # Reducer selection
91
+ reducer_choice = st.selectbox(
92
+ "Select Reducer",
93
+ ['mean', 'sum', 'median', 'min', 'max', 'count'],
94
+ index=0 # Default to 'mean'
95
+ )
96
+
97
  # Function to check if the polygon geometry is valid and convert it to the correct format
98
  def convert_to_ee_geometry(geometry):
99
  if geometry.is_valid:
 
164
  'file_upload': file_upload
165
  }
166
 
167
+ # Function to get the corresponding reducer based on user input
168
+ def get_reducer(reducer_name):
169
+ """
170
+ Map user-friendly reducer names to Earth Engine reducer objects.
171
+
172
+ Args:
173
+ reducer_name (str): The name of the reducer (e.g., 'mean', 'sum', 'median').
174
+
175
+ Returns:
176
+ ee.Reducer: The corresponding Earth Engine reducer.
177
+ """
178
+ reducers = {
179
+ 'mean': ee.Reducer.mean(),
180
+ 'sum': ee.Reducer.sum(),
181
+ 'median': ee.Reducer.median(),
182
+ 'min': ee.Reducer.min(),
183
+ 'max': ee.Reducer.max(),
184
+ 'count': ee.Reducer.count(),
185
+ }
186
+
187
+ # Default to 'mean' if the reducer_name is not recognized
188
+ return reducers.get(reducer_name.lower(), ee.Reducer.mean())
189
+
190
+ # Function to calculate NDVI
191
  def calculate_ndvi(image, geometry):
192
  ndvi = image.normalizedDifference(['B8', 'B4']).rename('NDVI')
193
+
194
+ # Perform reduction on the region with the selected reducer
195
  result = ndvi.reduceRegion(
196
+ reducer=get_reducer(reducer_choice),
197
  geometry=geometry,
198
  scale=30
199
  )
200
+
201
+ # Output debugging information
202
+ result_value = result.get('NDVI')
203
+ try:
204
+ calculated_value = result_value.getInfo()
205
+ st.write(f"NDVI calculation using {reducer_choice}: {calculated_value}")
206
+ except Exception as e:
207
+ st.error(f"Error retrieving NDVI result: {e}")
208
+
209
+ return result_value
210
 
211
+
212
+ # Function to calculate NDWI
213
  def calculate_ndwi(image, geometry):
214
  ndwi = image.normalizedDifference(['B3', 'B8']).rename('NDWI')
215
  result = ndwi.reduceRegion(
216
+ reducer=get_reducer(reducer_choice),
217
  geometry=geometry,
218
  scale=30
219
  )
220
+
221
+ # Output debugging information
222
+ result_value = result.get('NDWI')
223
+ try:
224
+ calculated_value = result_value.getInfo()
225
+ st.write(f"NDVI calculation using {reducer_choice}: {calculated_value}")
226
+ except Exception as e:
227
+ st.error(f"Error retrieving NDVI result: {e}")
228
+
229
+ return result_value
230
 
231
+ # Function to calculate Average NO₂ for Sentinel-5P
232
  def calculate_avg_no2_sentinel5p(image, geometry):
233
  no2 = image.select('NO2').reduceRegion(
234
+ reducer=get_reducer(reducer_choice),
235
  geometry=geometry,
236
  scale=1000
237
+ )
238
+
239
+ # Output debugging information
240
+ result_value = result.get('NDVI')
241
+ try:
242
+ calculated_value = result_value.getInfo()
243
+ st.write(f"NDVI calculation using {reducer_choice}: {calculated_value}")
244
+ except Exception as e:
245
+ st.error(f"Error retrieving NDVI result: {e}")
246
+
247
+ return result_value
248
 
249
+ # Function to calculate Custom Formula
250
  def calculate_custom_formula(image, geometry, formula, scale=30):
251
+ """
252
+ Calculate a custom formula on an image and return the result for a given geometry,
253
+ using a user-specified reducer.
254
+ """
255
+ # Dynamically generate the dictionary of band references from the image
256
  band_names = image.bandNames().getInfo()
257
  band_dict = {band: image.select(band) for band in band_names}
258
 
259
+ # Use the formula with the bands in the image
260
  result_image = image.expression(formula, band_dict).rename('CustomResult')
261
 
262
+ # Reduce the region to get the result based on the specified reducer
263
  result = result_image.reduceRegion(
264
+ reducer=get_reducer(reducer_choice),
265
  geometry=geometry,
266
  scale=scale
267
+ )
268
 
269
+ # Output debugging information
270
+ result_value = result.get('CustomResult')
271
+ try:
272
+ calculated_value = result_value.getInfo()
273
+ st.write(f"NDVI calculation using {reducer_choice}: {calculated_value}")
274
+ except Exception as e:
275
+ st.error(f"Error retrieving NDVI result: {e}")
276
+
277
+ return result_value
278
 
279
  # Function to get the most recent image from the collection
280
  def get_most_recent_image(image_collection):
281
  image = image_collection.sort('system:time_start', False).first()
282
  return image
283
 
284
+
285
+ # Function to process the custom formula
286
+ def process_custom_formula(image, geometry, formula):
287
+ return calculate_custom_formula(image, geometry, formula)
 
 
 
288
 
289
  locations_df = None # Initialize locations_df to None
290
  polygons_df = None # Ensure polygons_df is initialized at the beginning
291
 
292
  # Process each point (with additional checks for file validity)
293
+ # Check the shape type and assign polygons_df only for Polygon data
294
  if file_upload:
295
+ # locations_df = None # Initialize locations_df to None
296
+ # polygons_df = None # Ensure polygons_df is initialized at the beginning
297
+
298
  file_extension = os.path.splitext(file_upload.name)[1].lower()
299
 
300
  # Read file based on shape type
 
308
  else:
309
  st.error("Unsupported file type. Please upload a CSV, GeoJSON, or KML file for points.")
310
  elif shape_type == 'Polygon':
311
+ if file_extension == '.geojson':
312
  polygons_df = read_geojson(file_upload)
313
  elif file_extension == '.kml':
314
  polygons_df = read_kml(file_upload)
315
  else:
316
  st.error("Unsupported file type. Please upload a GeoJSON or KML file for polygons.")
317
 
318
+
319
  if locations_df is not None and not locations_df.empty:
320
+ # Ensure the necessary columns exist in the dataframe
321
  if 'latitude' not in locations_df.columns or 'longitude' not in locations_df.columns:
322
  st.error("Uploaded file is missing required 'latitude' or 'longitude' columns.")
323
  else:
324
+ # Display a preview of the points data
325
  st.write("Preview of the uploaded points data:")
326
  st.dataframe(locations_df.head())
327
 
328
+ # Create a LeafMap object to display the points
329
  m = leafmap.Map(center=[locations_df['latitude'].mean(), locations_df['longitude'].mean()], zoom=10)
330
 
331
+ # Add points to the map using a loop
332
  for _, row in locations_df.iterrows():
333
  latitude = row['latitude']
334
  longitude = row['longitude']
335
 
336
+ # Check if latitude or longitude are NaN and skip if they are
337
  if pd.isna(latitude) or pd.isna(longitude):
338
+ continue # Skip this row and move to the next one
339
 
340
  m.add_marker(location=[latitude, longitude], popup=row.get('name', 'No Name'))
341
 
342
+ # Display map
343
  st.write("Map of Uploaded Points:")
344
  m.to_streamlit()
345
+
346
+ # Store the map in session_state
347
  st.session_state.map_data = m
348
 
349
+ # Process each point for index calculation
350
  for idx, row in locations_df.iterrows():
351
  latitude = row['latitude']
352
  longitude = row['longitude']
353
  location_name = row.get('name', f"Location_{idx}")
354
 
355
+ # Skip processing if latitude or longitude is NaN
356
  if pd.isna(latitude) or pd.isna(longitude):
357
+ continue # Skip this row and move to the next one
358
 
359
+ # Define the region of interest (ROI)
360
  roi = ee.Geometry.Point([longitude, latitude])
361
 
362
+ # Load Sentinel-2 image collection
363
  collection = ee.ImageCollection(sub_options[sub_selection]) \
364
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
365
  .filterBounds(roi)
 
369
  st.warning(f"No images found for {location_name}.")
370
  else:
371
  st.write(f"Found images for {location_name}.")
372
+ # Perform the calculation based on user selection
373
+ # Perform the calculation based on user selection
374
  result = None
375
  if index_choice == 'NDVI':
376
  result = calculate_ndvi(image, roi)
 
384
  elif index_choice.lower() == 'custom formula' and custom_formula:
385
  result = process_custom_formula(image, roi, custom_formula)
386
 
387
+ # Validate result before using getInfo
388
  if result is not None:
389
+ calculated_value = None # Initialize the calculated_value as None
390
 
391
+ # Check if the result is a dictionary
392
  if isinstance(result, dict):
393
+ # Extract the value using the appropriate key (adjust the key name as needed)
394
+ calculated_value = result.get('CustomResult', None) # Replace 'CustomResult' if using NDVI, NDWI, etc.
395
  else:
396
  try:
397
+ # If it's an Earth Engine object, get the value using getInfo
398
  calculated_value = result.getInfo()
399
  except Exception as e:
400
  st.error(f"Error getting result info: {e}")
401
+
402
+ # If a valid calculated_value is found, append the result to session_state
403
  if calculated_value is not None:
404
  st.session_state.results.append({
405
  'Location Name': location_name,
 
411
  st.warning(f"No value calculated for {location_name}.")
412
  else:
413
  st.warning(f"No value calculated for {location_name}.")
414
+
415
+
416
+ # Check if polygons_df is populated for polygons
417
+ if polygons_df is not None:
418
+ st.write("Preview of the uploaded polygons data:")
419
+ st.dataframe(polygons_df.head())
420
+
421
+ m = leafmap.Map(center=[polygons_df.geometry.centroid.y.mean(), polygons_df.geometry.centroid.x.mean()], zoom=10)
422
+
423
+ for _, row in polygons_df.iterrows():
424
+ polygon = row['geometry']
425
+ if polygon.is_valid:
426
+ gdf = gpd.GeoDataFrame([row], geometry=[polygon], crs=polygons_df.crs)
427
+ m.add_gdf(gdf=gdf, layer_name=row.get('name', 'Unnamed Polygon'))
428
+
429
+ st.write("Map of Uploaded Polygons:")
430
+ m.to_streamlit()
431
+ st.session_state.map_data = m
432
+
433
+ for idx, row in polygons_df.iterrows():
434
+ polygon = row['geometry']
435
+ location_name = row.get('name', f"Polygon_{idx}")
436
+
437
+ try:
438
+ roi = convert_to_ee_geometry(polygon)
439
+ except ValueError as e:
440
+ st.error(str(e))
441
+ continue
442
 
443
+ collection = ee.ImageCollection(sub_options[sub_selection]) \
444
+ .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
445
+ .filterBounds(roi)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
446
 
447
+ image = get_most_recent_image(collection)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
 
449
+ if not image:
450
+ st.warning(f"No images found for {location_name}.")
451
+ else:
452
+ st.write(f"Found an image for {location_name}.")
453
+ result = None
454
+ if index_choice.lower() == 'ndvi':
455
+ result = calculate_ndvi(image, roi)
456
+ elif index_choice.lower() == 'ndwi':
457
+ result = calculate_ndwi(image, roi)
458
+ elif index_choice.lower() == 'average no₂':
459
+ if 'NO2' in image.bandNames().getInfo():
460
+ result = calculate_avg_no2_sentinel5p(image, roi)
461
+ else:
462
+ st.warning(f"No NO2 band found for {location_name}. Please use Sentinel-5P for NO₂ data.")
463
+ elif index_choice.lower() == 'custom formula' and custom_formula:
464
+ result = process_custom_formula(image, roi, custom_formula)
465
 
466
+ if result is not None:
467
+ # Initialize the calculated_value as None
468
+ calculated_value = None
469
+
470
+ # Check if the result is a dictionary (e.g., custom formula result)
471
  if isinstance(result, dict) and 'CustomResult' in result:
472
+ calculated_value = result['CustomResult'] # Extract the numeric value from the dictionary
473
+ # If the result is a numeric value (e.g., NDVI, NDWI, or NO2)
474
  elif isinstance(result, (int, float)):
475
  calculated_value = result
476
+
477
+ # If a valid calculated_value is found, append the result to session_state
478
  if calculated_value is not None:
479
  st.session_state.results.append({
480
  'Location Name': location_name,
 
499
  data=result_df.to_csv(index=False).encode('utf-8'),
500
  file_name=filename,
501
  mime='text/csv'
502
+ )