YashMK89 commited on
Commit
1b7b230
·
verified ·
1 Parent(s): c8ca8f7

update app.py

Browse files
Files changed (1) hide show
  1. app.py +94 -30
app.py CHANGED
@@ -4,7 +4,7 @@ import ee
4
  import os
5
  import pandas as pd
6
  import geopandas as gpd
7
- from datetime import datetime, timedelta
8
  import leafmap.foliumap as leafmap
9
  import re
10
  from shapely.geometry import base
@@ -143,7 +143,7 @@ def calculate_custom_formula(image, geometry, selected_bands, custom_formula, re
143
  return ee.Image(0).rename('custom_result').set('error', str(e))
144
 
145
  # Aggregation functions
146
- def aggregate_data_daily(collection):
147
  collection = collection.map(lambda image: image.set('day', ee.Date(image.get('system:time_start')).format('YYYY-MM-dd')))
148
  grouped_by_day = collection.aggregate_array('day').distinct()
149
  def calculate_daily_mean(day):
@@ -153,12 +153,12 @@ def aggregate_data_daily(collection):
153
  daily_images = ee.List(grouped_by_day.map(calculate_daily_mean))
154
  return ee.ImageCollection(daily_images)
155
 
156
- def aggregate_data_weekly(collection, start_date_str, end_date_str):
157
  def set_week_start(image):
158
  date = ee.Date(image.get('system:time_start'))
159
- days_since_start = date.difference(ee.Date(start_date_str), 'day')
160
- week_index = days_since_start.divide(7).floor()
161
- week_start = ee.Date(start_date_str).advance(week_index.multiply(7), 'day')
162
  return image.set('week_start', week_start.format('YYYY-MM-dd'))
163
  collection = collection.map(set_week_start)
164
  grouped_by_week = collection.aggregate_array('week_start').distinct()
@@ -191,7 +191,7 @@ def aggregate_data_yearly(collection):
191
  return ee.ImageCollection(yearly_images)
192
 
193
  # Worker function for processing a single geometry
194
- def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selected_bands, reducer_choice, shape_type, aggregation_period, custom_formula, kernel_size=None, include_boundary=None):
195
  if shape_type.lower() == "point":
196
  latitude = row.get('latitude')
197
  longitude = row.get('longitude')
@@ -221,10 +221,10 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
221
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
222
  .filterBounds(roi)
223
 
224
- if aggregation_period.lower() == 'daily':
225
- collection = aggregate_data_daily(collection)
226
  elif aggregation_period.lower() == 'weekly':
227
- collection = aggregate_data_weekly(collection, start_date_str, end_date_str)
228
  elif aggregation_period.lower() == 'monthly':
229
  collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
230
  elif aggregation_period.lower() == 'yearly':
@@ -232,11 +232,12 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
232
 
233
  # Process each image in the collection
234
  image_list = collection.toList(collection.size())
235
- processed_periods = set()
236
  aggregated_results = []
 
237
  for i in range(image_list.size().getInfo()):
238
  image = ee.Image(image_list.get(i))
239
- if aggregation_period.lower() == 'daily':
240
  timestamp = image.get('day')
241
  period_label = 'Date'
242
  date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
@@ -244,9 +245,11 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
244
  timestamp = image.get('week_start')
245
  period_label = 'Week'
246
  date = ee.String(timestamp).getInfo()
247
- if date in processed_periods:
 
 
248
  continue
249
- processed_periods.add(date)
250
  elif aggregation_period.lower() == 'monthly':
251
  timestamp = image.get('month')
252
  period_label = 'Month'
@@ -273,19 +276,21 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
273
  'Calculated Value': calculated_value
274
  }
275
  if shape_type.lower() == 'point':
276
- result['Latitude'] = latitude
277
- result['Longitude'] = longitude
278
  aggregated_results.append(result)
279
  except Exception as e:
280
  st.error(f"Error retrieving value for {location_name}: {e}")
 
281
  return aggregated_results
282
 
283
  # Main processing function
284
- def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id, selected_bands, reducer_choice, shape_type, aggregation_period, custom_formula="", kernel_size=None, include_boundary=None):
285
  aggregated_results = []
286
  total_steps = len(locations_df)
287
  progress_bar = st.progress(0)
288
  progress_text = st.empty()
 
289
  start_time = time.time() # Start timing the process
290
  with ThreadPoolExecutor(max_workers=10) as executor:
291
  futures = []
@@ -301,10 +306,13 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
301
  shape_type,
302
  aggregation_period,
303
  custom_formula,
 
 
304
  kernel_size,
305
  include_boundary
306
  )
307
  futures.append(future)
 
308
  completed = 0
309
  for future in as_completed(futures):
310
  result = future.result()
@@ -314,9 +322,11 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
314
  progress_percentage = completed / total_steps
315
  progress_bar.progress(progress_percentage)
316
  progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
 
317
  # End timing the process
318
  end_time = time.time()
319
  processing_time = end_time - start_time # Calculate total processing time
 
320
  if aggregated_results:
321
  result_df = pd.DataFrame(aggregated_results)
322
  if aggregation_period.lower() == 'custom (start date to end date)':
@@ -326,11 +336,11 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
326
  'Calculated Value': 'mean'
327
  }
328
  if shape_type.lower() == 'point':
329
- agg_dict['Latitude'] = 'first'
330
- agg_dict['Longitude'] = 'first'
331
  aggregated_output = result_df.groupby('Location Name').agg(agg_dict).reset_index()
332
  aggregated_output.rename(columns={'Calculated Value': 'Aggregated Value'}, inplace=True)
333
- return aggregated_output.to_dict(orient='records'), processing_time # Return processing time
334
  else:
335
  return result_df.to_dict(orient='records'), processing_time
336
  return [], processing_time
@@ -341,6 +351,7 @@ imagery_base = st.selectbox("Select Imagery Base", ["Sentinel", "Landsat", "MODI
341
 
342
  # Initialize data as an empty dictionary
343
  data = {}
 
344
  if imagery_base == "Sentinel":
345
  dataset_file = "sentinel_datasets.json"
346
  try:
@@ -392,9 +403,12 @@ if not data:
392
  st.stop()
393
 
394
  st.markdown("<hr><h5><b>{}</b></h5>".format(imagery_base), unsafe_allow_html=True)
 
395
  main_selection = st.selectbox(f"Select {imagery_base} Dataset Category", list(data.keys()))
 
396
  sub_selection = None
397
  dataset_id = None
 
398
  if main_selection:
399
  sub_options = data[main_selection]["sub_options"]
400
  sub_selection = st.selectbox(f"Select Specific {imagery_base} Dataset ID", list(sub_options.keys()))
@@ -404,6 +418,7 @@ if main_selection:
404
  dataset_id = sub_selection
405
 
406
  st.markdown("<hr><h5><b>Earth Engine Index Calculator</b></h5>", unsafe_allow_html=True)
 
407
  if main_selection and sub_selection:
408
  dataset_bands = data[main_selection]["bands"].get(sub_selection, [])
409
  st.write(f"Available Bands for {sub_options[sub_selection]}: {', '.join(dataset_bands)}")
@@ -413,9 +428,11 @@ if main_selection and sub_selection:
413
  default=[dataset_bands[0]] if dataset_bands else [],
414
  help=f"Select 1 or 2 bands from: {', '.join(dataset_bands)}"
415
  )
 
416
  if len(selected_bands) < 1:
417
  st.warning("Please select at least one band.")
418
  st.stop()
 
419
  if selected_bands:
420
  if len(selected_bands) == 1:
421
  default_formula = f"{selected_bands[0]}"
@@ -428,6 +445,7 @@ if main_selection and sub_selection:
428
  value=default_formula,
429
  help=f"Use only these bands: {', '.join(selected_bands)}. Examples: {example}"
430
  )
 
431
  def validate_formula(formula, selected_bands):
432
  allowed_chars = set(" +-*/()0123456789.")
433
  terms = re.findall(r'[a-zA-Z][a-zA-Z0-9_]*', formula)
@@ -437,6 +455,7 @@ if main_selection and sub_selection:
437
  if not all(char in allowed_chars or char in ''.join(selected_bands) for char in formula):
438
  return False, "Formula contains invalid characters. Use only bands, numbers, and operators (+, -, *, /, ())"
439
  return True, ""
 
440
  is_valid, error_message = validate_formula(custom_formula, selected_bands)
441
  if not is_valid:
442
  st.error(error_message)
@@ -444,6 +463,7 @@ if main_selection and sub_selection:
444
  elif not custom_formula:
445
  st.warning("Please enter a custom formula to proceed.")
446
  st.stop()
 
447
  st.write(f"Custom Formula: {custom_formula}")
448
 
449
  reducer_choice = st.selectbox(
@@ -458,12 +478,13 @@ start_date_str = start_date.strftime('%Y-%m-%d')
458
  end_date_str = end_date.strftime('%Y-%m-%d')
459
 
460
  aggregation_period = st.selectbox(
461
- "Select Aggregation Period (e.g, Daily, Weekly, Monthly, Yearly)",
462
- ["Daily", "Weekly", "Monthly", "Yearly"],
463
  index=0
464
  )
465
 
466
  shape_type = st.selectbox("Do you want to process 'Point' or 'Polygon' data?", ["Point", "Polygon"])
 
467
  kernel_size = None
468
  include_boundary = None
469
 
@@ -483,26 +504,57 @@ elif shape_type.lower() == "polygon":
483
 
484
  file_upload = st.file_uploader(f"Upload your {shape_type} data (CSV, GeoJSON, KML)", type=["csv", "geojson", "kml"])
485
  locations_df = pd.DataFrame()
 
 
486
 
487
  if file_upload is not None:
488
  if shape_type.lower() == "point":
489
  if file_upload.name.endswith('.csv'):
 
490
  locations_df = pd.read_csv(file_upload)
491
- st.write("Preview of Uploaded Data:")
 
 
492
  st.dataframe(locations_df.head())
 
 
493
  all_columns = locations_df.columns.tolist()
494
- lat_col = st.selectbox("Select Latitude Column", options=all_columns)
495
- lon_col = st.selectbox("Select Longitude Column", options=all_columns)
496
- if not pd.api.types.is_numeric_dtype(locations_df[lat_col]) or not pd.api.types.is_numeric_dtype(locations_df[lon_col]):
497
- st.error("Selected Latitude and Longitude columns must contain numeric values.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498
  st.stop()
499
- locations_df['latitude'] = locations_df[lat_col]
500
- locations_df['longitude'] = locations_df[lon_col]
 
 
 
 
 
501
  elif file_upload.name.endswith('.geojson'):
502
  locations_df = gpd.read_file(file_upload)
503
  if 'geometry' in locations_df.columns:
504
  locations_df['latitude'] = locations_df['geometry'].y
505
  locations_df['longitude'] = locations_df['geometry'].x
 
 
506
  else:
507
  st.error("GeoJSON file doesn't contain geometry column")
508
  st.stop()
@@ -527,8 +579,12 @@ if file_upload is not None:
527
  locations_df = gpd.GeoDataFrame(points, geometry=gpd.GeoSeries.from_wkt([p['geometry'] for p in points]), crs="EPSG:4326")
528
  locations_df['latitude'] = locations_df['geometry'].y
529
  locations_df['longitude'] = locations_df['geometry'].x
 
 
530
  except Exception as e:
531
  st.error(f"Error parsing KML file: {str(e)}")
 
 
532
  if not locations_df.empty and 'latitude' in locations_df.columns and 'longitude' in locations_df.columns:
533
  m = leafmap.Map(center=[locations_df['latitude'].mean(), locations_df['longitude'].mean()], zoom=10)
534
  for _, row in locations_df.iterrows():
@@ -539,7 +595,7 @@ if file_upload is not None:
539
  m.add_marker(location=[latitude, longitude], popup=row.get('name', 'No Name'))
540
  st.write("Map of Uploaded Points:")
541
  m.to_streamlit()
542
-
543
  elif shape_type.lower() == "polygon":
544
  if file_upload.name.endswith('.csv'):
545
  st.error("CSV upload not supported for polygons. Please upload a GeoJSON or KML file.")
@@ -569,6 +625,8 @@ if file_upload is not None:
569
  locations_df = gpd.GeoDataFrame(polygons, geometry=gpd.GeoSeries.from_wkt([p['geometry'] for p in polygons]), crs="EPSG:4326")
570
  except Exception as e:
571
  st.error(f"Error parsing KML file: {str(e)}")
 
 
572
  if not locations_df.empty and 'geometry' in locations_df.columns:
573
  centroid_lat = locations_df.geometry.centroid.y.mean()
574
  centroid_lon = locations_df.geometry.centroid.x.mean()
@@ -594,14 +652,18 @@ if st.button(f"Calculate {custom_formula}"):
594
  reducer_choice,
595
  shape_type,
596
  aggregation_period,
 
 
597
  custom_formula,
598
  kernel_size,
599
  include_boundary
600
  )
 
601
  if results:
602
  result_df = pd.DataFrame(results)
603
  st.write(f"Processed Results Table ({aggregation_period}) for Formula: {custom_formula}")
604
  st.dataframe(result_df)
 
605
  filename = f"{main_selection}_{dataset_id}_{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}_{aggregation_period.lower()}.csv"
606
  st.download_button(
607
  label="Download results as CSV",
@@ -609,10 +671,12 @@ if st.button(f"Calculate {custom_formula}"):
609
  file_name=filename,
610
  mime='text/csv'
611
  )
 
612
  st.success(f"Processing complete! Total processing time: {processing_time:.2f} seconds.")
613
  else:
614
  st.warning("No results were generated. Check your inputs or formula.")
615
  st.info(f"Total processing time: {processing_time:.2f} seconds.")
 
616
  except Exception as e:
617
  st.error(f"An error occurred during processing: {str(e)}")
618
  else:
 
4
  import os
5
  import pandas as pd
6
  import geopandas as gpd
7
+ from datetime import datetime
8
  import leafmap.foliumap as leafmap
9
  import re
10
  from shapely.geometry import base
 
143
  return ee.Image(0).rename('custom_result').set('error', str(e))
144
 
145
  # Aggregation functions
146
+ def aggregate_data_custom(collection):
147
  collection = collection.map(lambda image: image.set('day', ee.Date(image.get('system:time_start')).format('YYYY-MM-dd')))
148
  grouped_by_day = collection.aggregate_array('day').distinct()
149
  def calculate_daily_mean(day):
 
153
  daily_images = ee.List(grouped_by_day.map(calculate_daily_mean))
154
  return ee.ImageCollection(daily_images)
155
 
156
+ def aggregate_data_weekly(collection):
157
  def set_week_start(image):
158
  date = ee.Date(image.get('system:time_start'))
159
+ days_since_week_start = date.getRelative('day', 'week')
160
+ offset = ee.Number(days_since_week_start).multiply(-1)
161
+ week_start = date.advance(offset, 'day')
162
  return image.set('week_start', week_start.format('YYYY-MM-dd'))
163
  collection = collection.map(set_week_start)
164
  grouped_by_week = collection.aggregate_array('week_start').distinct()
 
191
  return ee.ImageCollection(yearly_images)
192
 
193
  # Worker function for processing a single geometry
194
+ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selected_bands, reducer_choice, shape_type, aggregation_period, custom_formula, original_lat_col, original_lon_col, kernel_size=None, include_boundary=None):
195
  if shape_type.lower() == "point":
196
  latitude = row.get('latitude')
197
  longitude = row.get('longitude')
 
221
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
222
  .filterBounds(roi)
223
 
224
+ if aggregation_period.lower() == 'custom (start date to end date)':
225
+ collection = aggregate_data_custom(collection)
226
  elif aggregation_period.lower() == 'weekly':
227
+ collection = aggregate_data_weekly(collection)
228
  elif aggregation_period.lower() == 'monthly':
229
  collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
230
  elif aggregation_period.lower() == 'yearly':
 
232
 
233
  # Process each image in the collection
234
  image_list = collection.toList(collection.size())
235
+ processed_weeks = set()
236
  aggregated_results = []
237
+
238
  for i in range(image_list.size().getInfo()):
239
  image = ee.Image(image_list.get(i))
240
+ if aggregation_period.lower() == 'custom (start date to end date)':
241
  timestamp = image.get('day')
242
  period_label = 'Date'
243
  date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
 
245
  timestamp = image.get('week_start')
246
  period_label = 'Week'
247
  date = ee.String(timestamp).getInfo()
248
+ if (pd.to_datetime(date) < pd.to_datetime(start_date_str) or
249
+ pd.to_datetime(date) > pd.to_datetime(end_date_str) or
250
+ date in processed_weeks):
251
  continue
252
+ processed_weeks.add(date)
253
  elif aggregation_period.lower() == 'monthly':
254
  timestamp = image.get('month')
255
  period_label = 'Month'
 
276
  'Calculated Value': calculated_value
277
  }
278
  if shape_type.lower() == 'point':
279
+ result[original_lat_col] = latitude # Use original column name
280
+ result[original_lon_col] = longitude # Use original column name
281
  aggregated_results.append(result)
282
  except Exception as e:
283
  st.error(f"Error retrieving value for {location_name}: {e}")
284
+
285
  return aggregated_results
286
 
287
  # Main processing function
288
+ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id, selected_bands, reducer_choice, shape_type, aggregation_period, original_lat_col, original_lon_col, custom_formula="", kernel_size=None, include_boundary=None):
289
  aggregated_results = []
290
  total_steps = len(locations_df)
291
  progress_bar = st.progress(0)
292
  progress_text = st.empty()
293
+
294
  start_time = time.time() # Start timing the process
295
  with ThreadPoolExecutor(max_workers=10) as executor:
296
  futures = []
 
306
  shape_type,
307
  aggregation_period,
308
  custom_formula,
309
+ original_lat_col,
310
+ original_lon_col,
311
  kernel_size,
312
  include_boundary
313
  )
314
  futures.append(future)
315
+
316
  completed = 0
317
  for future in as_completed(futures):
318
  result = future.result()
 
322
  progress_percentage = completed / total_steps
323
  progress_bar.progress(progress_percentage)
324
  progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
325
+
326
  # End timing the process
327
  end_time = time.time()
328
  processing_time = end_time - start_time # Calculate total processing time
329
+
330
  if aggregated_results:
331
  result_df = pd.DataFrame(aggregated_results)
332
  if aggregation_period.lower() == 'custom (start date to end date)':
 
336
  'Calculated Value': 'mean'
337
  }
338
  if shape_type.lower() == 'point':
339
+ agg_dict[original_lat_col] = 'first'
340
+ agg_dict[original_lon_col] = 'first'
341
  aggregated_output = result_df.groupby('Location Name').agg(agg_dict).reset_index()
342
  aggregated_output.rename(columns={'Calculated Value': 'Aggregated Value'}, inplace=True)
343
+ return aggregated_output.to_dict(orient='records'), processing_time
344
  else:
345
  return result_df.to_dict(orient='records'), processing_time
346
  return [], processing_time
 
351
 
352
  # Initialize data as an empty dictionary
353
  data = {}
354
+
355
  if imagery_base == "Sentinel":
356
  dataset_file = "sentinel_datasets.json"
357
  try:
 
403
  st.stop()
404
 
405
  st.markdown("<hr><h5><b>{}</b></h5>".format(imagery_base), unsafe_allow_html=True)
406
+
407
  main_selection = st.selectbox(f"Select {imagery_base} Dataset Category", list(data.keys()))
408
+
409
  sub_selection = None
410
  dataset_id = None
411
+
412
  if main_selection:
413
  sub_options = data[main_selection]["sub_options"]
414
  sub_selection = st.selectbox(f"Select Specific {imagery_base} Dataset ID", list(sub_options.keys()))
 
418
  dataset_id = sub_selection
419
 
420
  st.markdown("<hr><h5><b>Earth Engine Index Calculator</b></h5>", unsafe_allow_html=True)
421
+
422
  if main_selection and sub_selection:
423
  dataset_bands = data[main_selection]["bands"].get(sub_selection, [])
424
  st.write(f"Available Bands for {sub_options[sub_selection]}: {', '.join(dataset_bands)}")
 
428
  default=[dataset_bands[0]] if dataset_bands else [],
429
  help=f"Select 1 or 2 bands from: {', '.join(dataset_bands)}"
430
  )
431
+
432
  if len(selected_bands) < 1:
433
  st.warning("Please select at least one band.")
434
  st.stop()
435
+
436
  if selected_bands:
437
  if len(selected_bands) == 1:
438
  default_formula = f"{selected_bands[0]}"
 
445
  value=default_formula,
446
  help=f"Use only these bands: {', '.join(selected_bands)}. Examples: {example}"
447
  )
448
+
449
  def validate_formula(formula, selected_bands):
450
  allowed_chars = set(" +-*/()0123456789.")
451
  terms = re.findall(r'[a-zA-Z][a-zA-Z0-9_]*', formula)
 
455
  if not all(char in allowed_chars or char in ''.join(selected_bands) for char in formula):
456
  return False, "Formula contains invalid characters. Use only bands, numbers, and operators (+, -, *, /, ())"
457
  return True, ""
458
+
459
  is_valid, error_message = validate_formula(custom_formula, selected_bands)
460
  if not is_valid:
461
  st.error(error_message)
 
463
  elif not custom_formula:
464
  st.warning("Please enter a custom formula to proceed.")
465
  st.stop()
466
+
467
  st.write(f"Custom Formula: {custom_formula}")
468
 
469
  reducer_choice = st.selectbox(
 
478
  end_date_str = end_date.strftime('%Y-%m-%d')
479
 
480
  aggregation_period = st.selectbox(
481
+ "Select Aggregation Period (e.g, Custom(Start Date to End Date) , Weekly , Monthly , Yearly)",
482
+ ["Custom (Start Date to End Date)", "Weekly", "Monthly", "Yearly"],
483
  index=0
484
  )
485
 
486
  shape_type = st.selectbox("Do you want to process 'Point' or 'Polygon' data?", ["Point", "Polygon"])
487
+
488
  kernel_size = None
489
  include_boundary = None
490
 
 
504
 
505
  file_upload = st.file_uploader(f"Upload your {shape_type} data (CSV, GeoJSON, KML)", type=["csv", "geojson", "kml"])
506
  locations_df = pd.DataFrame()
507
+ original_lat_col = None
508
+ original_lon_col = None
509
 
510
  if file_upload is not None:
511
  if shape_type.lower() == "point":
512
  if file_upload.name.endswith('.csv'):
513
+ # Read the CSV file
514
  locations_df = pd.read_csv(file_upload)
515
+
516
+ # Show the first few rows to help user identify columns
517
+ st.write("Preview of your uploaded data (first 5 rows):")
518
  st.dataframe(locations_df.head())
519
+
520
+ # Get all column names from the uploaded file
521
  all_columns = locations_df.columns.tolist()
522
+
523
+ # Let user select latitude and longitude columns from dropdown
524
+ col1, col2 = st.columns(2)
525
+ with col1:
526
+ original_lat_col = st.selectbox(
527
+ "Select Latitude Column",
528
+ options=all_columns,
529
+ index=all_columns.index('latitude') if 'latitude' in all_columns else 0,
530
+ help="Select the column containing latitude values"
531
+ )
532
+ with col2:
533
+ original_lon_col = st.selectbox(
534
+ "Select Longitude Column",
535
+ options=all_columns,
536
+ index=all_columns.index('longitude') if 'longitude' in all_columns else 0,
537
+ help="Select the column containing longitude values"
538
+ )
539
+
540
+ # Validate the selected columns contain numeric data
541
+ if not pd.api.types.is_numeric_dtype(locations_df[original_lat_col]) or not pd.api.types.is_numeric_dtype(locations_df[original_lon_col]):
542
+ st.error("Error: Selected Latitude and Longitude columns must contain numeric values")
543
  st.stop()
544
+
545
+ # Rename the selected columns to standard names for processing
546
+ locations_df = locations_df.rename(columns={
547
+ original_lat_col: 'latitude',
548
+ original_lon_col: 'longitude'
549
+ })
550
+
551
  elif file_upload.name.endswith('.geojson'):
552
  locations_df = gpd.read_file(file_upload)
553
  if 'geometry' in locations_df.columns:
554
  locations_df['latitude'] = locations_df['geometry'].y
555
  locations_df['longitude'] = locations_df['geometry'].x
556
+ original_lat_col = 'latitude'
557
+ original_lon_col = 'longitude'
558
  else:
559
  st.error("GeoJSON file doesn't contain geometry column")
560
  st.stop()
 
579
  locations_df = gpd.GeoDataFrame(points, geometry=gpd.GeoSeries.from_wkt([p['geometry'] for p in points]), crs="EPSG:4326")
580
  locations_df['latitude'] = locations_df['geometry'].y
581
  locations_df['longitude'] = locations_df['geometry'].x
582
+ original_lat_col = 'latitude'
583
+ original_lon_col = 'longitude'
584
  except Exception as e:
585
  st.error(f"Error parsing KML file: {str(e)}")
586
+
587
+ # Display map for points if we have valid data
588
  if not locations_df.empty and 'latitude' in locations_df.columns and 'longitude' in locations_df.columns:
589
  m = leafmap.Map(center=[locations_df['latitude'].mean(), locations_df['longitude'].mean()], zoom=10)
590
  for _, row in locations_df.iterrows():
 
595
  m.add_marker(location=[latitude, longitude], popup=row.get('name', 'No Name'))
596
  st.write("Map of Uploaded Points:")
597
  m.to_streamlit()
598
+
599
  elif shape_type.lower() == "polygon":
600
  if file_upload.name.endswith('.csv'):
601
  st.error("CSV upload not supported for polygons. Please upload a GeoJSON or KML file.")
 
625
  locations_df = gpd.GeoDataFrame(polygons, geometry=gpd.GeoSeries.from_wkt([p['geometry'] for p in polygons]), crs="EPSG:4326")
626
  except Exception as e:
627
  st.error(f"Error parsing KML file: {str(e)}")
628
+
629
+ # Display map for polygons if we have valid data
630
  if not locations_df.empty and 'geometry' in locations_df.columns:
631
  centroid_lat = locations_df.geometry.centroid.y.mean()
632
  centroid_lon = locations_df.geometry.centroid.x.mean()
 
652
  reducer_choice,
653
  shape_type,
654
  aggregation_period,
655
+ original_lat_col,
656
+ original_lon_col,
657
  custom_formula,
658
  kernel_size,
659
  include_boundary
660
  )
661
+
662
  if results:
663
  result_df = pd.DataFrame(results)
664
  st.write(f"Processed Results Table ({aggregation_period}) for Formula: {custom_formula}")
665
  st.dataframe(result_df)
666
+
667
  filename = f"{main_selection}_{dataset_id}_{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}_{aggregation_period.lower()}.csv"
668
  st.download_button(
669
  label="Download results as CSV",
 
671
  file_name=filename,
672
  mime='text/csv'
673
  )
674
+
675
  st.success(f"Processing complete! Total processing time: {processing_time:.2f} seconds.")
676
  else:
677
  st.warning("No results were generated. Check your inputs or formula.")
678
  st.info(f"Total processing time: {processing_time:.2f} seconds.")
679
+
680
  except Exception as e:
681
  st.error(f"An error occurred during processing: {str(e)}")
682
  else: