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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +54 -56
app.py CHANGED
@@ -143,31 +143,51 @@ 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_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):
150
- daily_collection = collection.filter(ee.Filter.eq('day', day))
 
 
 
 
 
 
 
 
 
151
  daily_mean = daily_collection.mean()
152
- return daily_mean.set('day', 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()
165
- def calculate_weekly_mean(week_start):
166
- weekly_collection = collection.filter(ee.Filter.eq('week_start', week_start))
167
- weekly_mean = weekly_collection.mean()
168
- return weekly_mean.set('week_start', week_start)
169
- weekly_images = ee.List(grouped_by_week.map(calculate_weekly_mean))
170
- return ee.ImageCollection(weekly_images)
 
 
 
 
 
 
 
 
 
 
171
 
172
  def aggregate_data_monthly(collection, start_date, end_date):
173
  collection = collection.filterDate(start_date, end_date)
@@ -180,6 +200,7 @@ def aggregate_data_monthly(collection, start_date, end_date):
180
  monthly_images = ee.List(grouped_by_month.map(calculate_monthly_mean))
181
  return ee.ImageCollection(monthly_images)
182
 
 
183
  def aggregate_data_yearly(collection):
184
  collection = collection.map(lambda image: image.set('year', ee.Date(image.get('system:time_start')).format('YYYY')))
185
  grouped_by_year = collection.aggregate_array('year').distinct()
@@ -190,6 +211,7 @@ def aggregate_data_yearly(collection):
190
  yearly_images = ee.List(grouped_by_year.map(calculate_yearly_mean))
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":
@@ -220,11 +242,13 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
220
  collection = ee.ImageCollection(dataset_id) \
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':
@@ -234,13 +258,16 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
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()
 
 
 
 
244
  elif aggregation_period.lower() == 'weekly':
245
  timestamp = image.get('week_start')
246
  period_label = 'Week'
@@ -281,16 +308,15 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
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 = []
@@ -312,7 +338,6 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
312
  include_boundary
313
  )
314
  futures.append(future)
315
-
316
  completed = 0
317
  for future in as_completed(futures):
318
  result = future.result()
@@ -322,11 +347,9 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
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)':
@@ -351,7 +374,6 @@ imagery_base = st.selectbox("Select Imagery Base", ["Sentinel", "Landsat", "MODI
351
 
352
  # Initialize data as an empty dictionary
353
  data = {}
354
-
355
  if imagery_base == "Sentinel":
356
  dataset_file = "sentinel_datasets.json"
357
  try:
@@ -397,18 +419,14 @@ elif imagery_base == "Custom Input":
397
  else:
398
  st.warning("Please enter a custom dataset ID to proceed.")
399
  data = {}
400
-
401
  if not data:
402
  st.error("No valid dataset available. Please check your inputs.")
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,7 +436,6 @@ if main_selection:
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,11 +445,9 @@ if main_selection and sub_selection:
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,7 +460,6 @@ if main_selection and sub_selection:
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,7 +469,6 @@ if main_selection and sub_selection:
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,7 +476,6 @@ if main_selection and sub_selection:
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,8 +490,8 @@ start_date_str = start_date.strftime('%Y-%m-%d')
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
 
@@ -487,7 +499,6 @@ shape_type = st.selectbox("Do you want to process 'Point' or 'Polygon' data?", [
487
 
488
  kernel_size = None
489
  include_boundary = None
490
-
491
  if shape_type.lower() == "point":
492
  kernel_size = st.selectbox(
493
  "Select Calculation Area(e.g, Point , 3x3 Kernel , 5x5 Kernel)",
@@ -512,14 +523,11 @@ if file_upload is not None:
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:
@@ -536,18 +544,15 @@ if file_upload is not None:
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:
@@ -583,7 +588,6 @@ if file_upload is not None:
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)
@@ -595,7 +599,6 @@ if file_upload is not None:
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,7 +628,6 @@ if file_upload is not None:
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()
@@ -658,12 +660,10 @@ if st.button(f"Calculate {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,12 +671,10 @@ if st.button(f"Calculate {custom_formula}"):
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:
 
143
  return ee.Image(0).rename('custom_result').set('error', str(e))
144
 
145
  # Aggregation functions
146
+ def aggregate_data_daily(collection):
147
+ """
148
+ Aggregates data on a daily basis.
149
+ """
150
+ def set_day_start(image):
151
+ date = ee.Date(image.get('system:time_start'))
152
+ day_start = date.format('YYYY-MM-dd')
153
+ return image.set('day_start', day_start)
154
+
155
+ collection = collection.map(set_day_start)
156
+ grouped_by_day = collection.aggregate_array('day_start').distinct()
157
+
158
+ def calculate_daily_mean(day_start):
159
+ daily_collection = collection.filter(ee.Filter.eq('day_start', day_start))
160
  daily_mean = daily_collection.mean()
161
+ return daily_mean.set('day_start', day_start)
162
+
163
  daily_images = ee.List(grouped_by_day.map(calculate_daily_mean))
164
  return ee.ImageCollection(daily_images)
165
 
166
+
167
+ def aggregate_data_weekly(collection, start_date_str, end_date_str):
168
+ """
169
+ Aggregates data on a weekly basis, starting from the exact start date provided by the user.
170
+ """
171
+ start_date = ee.Date(start_date_str)
172
+ end_date = ee.Date(end_date_str)
173
+
174
+ # Calculate the number of weeks between the start and end dates
175
+ days_diff = end_date.difference(start_date, 'day')
176
+ num_weeks = days_diff.divide(7).ceil().getInfo() # Total number of weeks
177
+
178
+ weekly_images = []
179
+ for week in range(num_weeks):
180
+ week_start = start_date.advance(week * 7, 'day') # Start of the week
181
+ week_end = week_start.advance(7, 'day') # End of the week
182
+
183
+ weekly_collection = collection.filterDate(week_start, week_end)
184
+ if weekly_collection.size().getInfo() > 0:
185
+ weekly_mean = weekly_collection.mean()
186
+ weekly_mean = weekly_mean.set('week_start', week_start.format('YYYY-MM-dd'))
187
+ weekly_images.append(weekly_mean)
188
+
189
+ return ee.ImageCollection.fromImages(weekly_images)
190
+
191
 
192
  def aggregate_data_monthly(collection, start_date, end_date):
193
  collection = collection.filterDate(start_date, end_date)
 
200
  monthly_images = ee.List(grouped_by_month.map(calculate_monthly_mean))
201
  return ee.ImageCollection(monthly_images)
202
 
203
+
204
  def aggregate_data_yearly(collection):
205
  collection = collection.map(lambda image: image.set('year', ee.Date(image.get('system:time_start')).format('YYYY')))
206
  grouped_by_year = collection.aggregate_array('year').distinct()
 
211
  yearly_images = ee.List(grouped_by_year.map(calculate_yearly_mean))
212
  return ee.ImageCollection(yearly_images)
213
 
214
+
215
  # Worker function for processing a single geometry
216
  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):
217
  if shape_type.lower() == "point":
 
242
  collection = ee.ImageCollection(dataset_id) \
243
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
244
  .filterBounds(roi)
245
+
246
  if aggregation_period.lower() == 'custom (start date to end date)':
247
  collection = aggregate_data_custom(collection)
248
+ elif aggregation_period.lower() == 'daily':
249
+ collection = aggregate_data_daily(collection)
250
  elif aggregation_period.lower() == 'weekly':
251
+ collection = aggregate_data_weekly(collection, start_date_str, end_date_str)
252
  elif aggregation_period.lower() == 'monthly':
253
  collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
254
  elif aggregation_period.lower() == 'yearly':
 
258
  image_list = collection.toList(collection.size())
259
  processed_weeks = set()
260
  aggregated_results = []
 
261
  for i in range(image_list.size().getInfo()):
262
  image = ee.Image(image_list.get(i))
263
  if aggregation_period.lower() == 'custom (start date to end date)':
264
  timestamp = image.get('day')
265
  period_label = 'Date'
266
  date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
267
+ elif aggregation_period.lower() == 'daily':
268
+ timestamp = image.get('day_start')
269
+ period_label = 'Date'
270
+ date = ee.String(timestamp).getInfo()
271
  elif aggregation_period.lower() == 'weekly':
272
  timestamp = image.get('week_start')
273
  period_label = 'Week'
 
308
  aggregated_results.append(result)
309
  except Exception as e:
310
  st.error(f"Error retrieving value for {location_name}: {e}")
 
311
  return aggregated_results
312
 
313
+
314
  # Main processing function
315
  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):
316
  aggregated_results = []
317
  total_steps = len(locations_df)
318
  progress_bar = st.progress(0)
319
  progress_text = st.empty()
 
320
  start_time = time.time() # Start timing the process
321
  with ThreadPoolExecutor(max_workers=10) as executor:
322
  futures = []
 
338
  include_boundary
339
  )
340
  futures.append(future)
 
341
  completed = 0
342
  for future in as_completed(futures):
343
  result = future.result()
 
347
  progress_percentage = completed / total_steps
348
  progress_bar.progress(progress_percentage)
349
  progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
 
350
  # End timing the process
351
  end_time = time.time()
352
  processing_time = end_time - start_time # Calculate total processing time
 
353
  if aggregated_results:
354
  result_df = pd.DataFrame(aggregated_results)
355
  if aggregation_period.lower() == 'custom (start date to end date)':
 
374
 
375
  # Initialize data as an empty dictionary
376
  data = {}
 
377
  if imagery_base == "Sentinel":
378
  dataset_file = "sentinel_datasets.json"
379
  try:
 
419
  else:
420
  st.warning("Please enter a custom dataset ID to proceed.")
421
  data = {}
 
422
  if not data:
423
  st.error("No valid dataset available. Please check your inputs.")
424
  st.stop()
425
 
426
  st.markdown("<hr><h5><b>{}</b></h5>".format(imagery_base), unsafe_allow_html=True)
 
427
  main_selection = st.selectbox(f"Select {imagery_base} Dataset Category", list(data.keys()))
 
428
  sub_selection = None
429
  dataset_id = None
 
430
  if main_selection:
431
  sub_options = data[main_selection]["sub_options"]
432
  sub_selection = st.selectbox(f"Select Specific {imagery_base} Dataset ID", list(sub_options.keys()))
 
436
  dataset_id = sub_selection
437
 
438
  st.markdown("<hr><h5><b>Earth Engine Index Calculator</b></h5>", unsafe_allow_html=True)
 
439
  if main_selection and sub_selection:
440
  dataset_bands = data[main_selection]["bands"].get(sub_selection, [])
441
  st.write(f"Available Bands for {sub_options[sub_selection]}: {', '.join(dataset_bands)}")
 
445
  default=[dataset_bands[0]] if dataset_bands else [],
446
  help=f"Select 1 or 2 bands from: {', '.join(dataset_bands)}"
447
  )
 
448
  if len(selected_bands) < 1:
449
  st.warning("Please select at least one band.")
450
  st.stop()
 
451
  if selected_bands:
452
  if len(selected_bands) == 1:
453
  default_formula = f"{selected_bands[0]}"
 
460
  value=default_formula,
461
  help=f"Use only these bands: {', '.join(selected_bands)}. Examples: {example}"
462
  )
 
463
  def validate_formula(formula, selected_bands):
464
  allowed_chars = set(" +-*/()0123456789.")
465
  terms = re.findall(r'[a-zA-Z][a-zA-Z0-9_]*', formula)
 
469
  if not all(char in allowed_chars or char in ''.join(selected_bands) for char in formula):
470
  return False, "Formula contains invalid characters. Use only bands, numbers, and operators (+, -, *, /, ())"
471
  return True, ""
 
472
  is_valid, error_message = validate_formula(custom_formula, selected_bands)
473
  if not is_valid:
474
  st.error(error_message)
 
476
  elif not custom_formula:
477
  st.warning("Please enter a custom formula to proceed.")
478
  st.stop()
 
479
  st.write(f"Custom Formula: {custom_formula}")
480
 
481
  reducer_choice = st.selectbox(
 
490
  end_date_str = end_date.strftime('%Y-%m-%d')
491
 
492
  aggregation_period = st.selectbox(
493
+ "Select Aggregation Period (e.g, Custom(Start Date to End Date) , Daily , Weekly , Monthly , Yearly)",
494
+ ["Custom (Start Date to End Date)", "Daily", "Weekly", "Monthly", "Yearly"],
495
  index=0
496
  )
497
 
 
499
 
500
  kernel_size = None
501
  include_boundary = None
 
502
  if shape_type.lower() == "point":
503
  kernel_size = st.selectbox(
504
  "Select Calculation Area(e.g, Point , 3x3 Kernel , 5x5 Kernel)",
 
523
  if file_upload.name.endswith('.csv'):
524
  # Read the CSV file
525
  locations_df = pd.read_csv(file_upload)
 
526
  # Show the first few rows to help user identify columns
527
  st.write("Preview of your uploaded data (first 5 rows):")
528
  st.dataframe(locations_df.head())
 
529
  # Get all column names from the uploaded file
530
  all_columns = locations_df.columns.tolist()
 
531
  # Let user select latitude and longitude columns from dropdown
532
  col1, col2 = st.columns(2)
533
  with col1:
 
544
  index=all_columns.index('longitude') if 'longitude' in all_columns else 0,
545
  help="Select the column containing longitude values"
546
  )
 
547
  # Validate the selected columns contain numeric data
548
  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]):
549
  st.error("Error: Selected Latitude and Longitude columns must contain numeric values")
550
  st.stop()
 
551
  # Rename the selected columns to standard names for processing
552
  locations_df = locations_df.rename(columns={
553
  original_lat_col: 'latitude',
554
  original_lon_col: 'longitude'
555
  })
 
556
  elif file_upload.name.endswith('.geojson'):
557
  locations_df = gpd.read_file(file_upload)
558
  if 'geometry' in locations_df.columns:
 
588
  original_lon_col = 'longitude'
589
  except Exception as e:
590
  st.error(f"Error parsing KML file: {str(e)}")
 
591
  # Display map for points if we have valid data
592
  if not locations_df.empty and 'latitude' in locations_df.columns and 'longitude' in locations_df.columns:
593
  m = leafmap.Map(center=[locations_df['latitude'].mean(), locations_df['longitude'].mean()], zoom=10)
 
599
  m.add_marker(location=[latitude, longitude], popup=row.get('name', 'No Name'))
600
  st.write("Map of Uploaded Points:")
601
  m.to_streamlit()
 
602
  elif shape_type.lower() == "polygon":
603
  if file_upload.name.endswith('.csv'):
604
  st.error("CSV upload not supported for polygons. Please upload a GeoJSON or KML file.")
 
628
  locations_df = gpd.GeoDataFrame(polygons, geometry=gpd.GeoSeries.from_wkt([p['geometry'] for p in polygons]), crs="EPSG:4326")
629
  except Exception as e:
630
  st.error(f"Error parsing KML file: {str(e)}")
 
631
  # Display map for polygons if we have valid data
632
  if not locations_df.empty and 'geometry' in locations_df.columns:
633
  centroid_lat = locations_df.geometry.centroid.y.mean()
 
660
  kernel_size,
661
  include_boundary
662
  )
 
663
  if results:
664
  result_df = pd.DataFrame(results)
665
  st.write(f"Processed Results Table ({aggregation_period}) for Formula: {custom_formula}")
666
  st.dataframe(result_df)
 
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
  st.success(f"Processing complete! Total processing time: {processing_time:.2f} seconds.")
675
  else:
676
  st.warning("No results were generated. Check your inputs or formula.")
677
  st.info(f"Total processing time: {processing_time:.2f} seconds.")
 
678
  except Exception as e:
679
  st.error(f"An error occurred during processing: {str(e)}")
680
  else: