YashMK89 commited on
Commit
129b8c8
·
verified ·
1 Parent(s): 53b3ed0

update app.py

Browse files
Files changed (1) hide show
  1. app.py +233 -123
app.py CHANGED
@@ -7,6 +7,9 @@ import geopandas as gpd
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")
@@ -92,12 +95,20 @@ elif index_choice.lower() == 'ndwi':
92
  elif index_choice.lower() == 'average no₂':
93
  st.write("Formula for Average NO₂: Average NO₂ = Mean(NO2 band)")
94
  elif index_choice.lower() == 'custom formula':
95
- custom_formula = st.text_input("Enter Custom Formula (e.g., B5,B4)")
96
  # Check if custom formula is empty and show warning
97
  if not custom_formula:
98
  st.warning("Please enter a custom formula before proceeding.")
99
  else:
100
- st.write(f"Custom Formula: (band1 - band2) / (band1 + band2)") # Display the custom formula after the user inputs it
 
 
 
 
 
 
 
 
101
 
102
  # Function to get the corresponding reducer based on user input
103
  def get_reducer(reducer_name):
@@ -123,13 +134,63 @@ reducer_choice = st.selectbox(
123
  index=0 # Default to 'mean'
124
  )
125
 
126
- # Function to check if the polygon geometry is valid and convert it to the correct format
127
  def convert_to_ee_geometry(geometry):
128
- if geometry.is_valid:
129
- geojson = geometry.__geo_interface__
130
- return ee.Geometry(geojson)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  else:
132
- raise ValueError("Invalid geometry: The polygon geometry is not valid.")
133
 
134
  # Function to read points from CSV
135
  def read_csv(file_path):
@@ -146,6 +207,18 @@ def read_kml(file_path):
146
  gdf = gpd.read_file(file_path, driver='KML')
147
  return gdf
148
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  # Ask user whether they want to process 'Point' or 'Polygon' data (case-insensitive)
150
  shape_type = st.selectbox("Do you want to process 'Point' or 'Polygon' data?", ["Point", "Polygon"])
151
 
@@ -174,7 +247,7 @@ if file_upload is not None:
174
  st.stop() # Stop further processing if polygons are detected
175
 
176
  # Processing the point data
177
- with st.spinner('Processing data...'):
178
  if locations_df is not None and not locations_df.empty:
179
  # For GeoJSON data, the coordinates are in the geometry column
180
  if 'geometry' in locations_df.columns:
@@ -231,7 +304,7 @@ if file_upload is not None:
231
  st.stop() # Stop further processing if point data is detected
232
 
233
  # Processing the polygon data
234
- with st.spinner('Processing data...'):
235
  if locations_df is not None and not locations_df.empty:
236
  # Ensure the 'geometry' column exists in the dataframe
237
  if 'geometry' not in locations_df.columns:
@@ -263,18 +336,6 @@ if file_upload is not None:
263
  # Store the map in session_state
264
  st.session_state.map_data = m
265
 
266
-
267
- # Date Input for Start and End Dates
268
- start_date = st.date_input("Start Date", value=pd.to_datetime('2020-01-01'))
269
- end_date = st.date_input("End Date", value=pd.to_datetime('2020-12-31'))
270
-
271
- # Convert start_date and end_date to string format for Earth Engine
272
- start_date_str = start_date.strftime('%Y-%m-%d')
273
- end_date_str = end_date.strftime('%Y-%m-%d')
274
-
275
- # Aggregation period selection
276
- aggregation_period = st.selectbox("Select Aggregation Period", ["Daily", "Weekly", "Monthly", "Yearly"], index=0)
277
-
278
  # Initialize session state for storing results if not already done
279
  if 'results' not in st.session_state:
280
  st.session_state.results = []
@@ -318,14 +379,24 @@ def calculate_ndwi(image, geometry, reducer_choice):
318
  ndwi = image.normalizedDifference(['B3', 'B8']).rename('NDWI')
319
  return ndwi
320
 
321
- def calculate_custom_formula(image, geometry, custom_formula, reducer_choice, scale=30):
322
-
323
- # Calculate NDWI using the user-specified bands
324
- band1 = custom_formula[:custom_formula.find(",")]
325
- band2 = custom_formula[custom_formula.find(",")+1:]
326
- custom_formula = image.normalizedDifference([band1, band2]).rename('custom formula')
327
- return custom_formula
328
-
 
 
 
 
 
 
 
 
 
 
329
  # Modify aggregation functions to return the correct time period and aggregated results
330
  def aggregate_data_daily(collection):
331
  # Extract day from the image date (using the exact date)
@@ -346,23 +417,28 @@ def aggregate_data_daily(collection):
346
  return ee.ImageCollection(daily_images)
347
 
348
  def aggregate_data_weekly(collection):
349
- # Extract week and year from the image date
350
- collection = collection.map(lambda image: image.set('week', ee.Date(image.get('system:time_start')).format('YYYY-ww')))
 
 
 
 
 
 
 
 
 
 
 
351
 
352
- # Group images by week
353
- grouped_by_week = collection.aggregate_array('week').distinct()
354
-
355
- def calculate_weekly_mean(week):
356
- weekly_collection = collection.filter(ee.Filter.eq('week', week))
357
- weekly_mean = weekly_collection.mean()
358
- return weekly_mean.set('week', week)
359
-
360
  # Calculate the weekly mean for each week
361
  weekly_images = ee.List(grouped_by_week.map(calculate_weekly_mean))
362
-
363
  return ee.ImageCollection(weekly_images)
364
-
365
- def aggregate_data_monthly(collection):
 
 
 
366
  # Extract month and year from the image date
367
  collection = collection.map(lambda image: image.set('month', ee.Date(image.get('system:time_start')).format('YYYY-MM')))
368
 
@@ -378,7 +454,7 @@ def aggregate_data_monthly(collection):
378
  monthly_images = ee.List(grouped_by_month.map(calculate_monthly_mean))
379
 
380
  return ee.ImageCollection(monthly_images)
381
-
382
  def aggregate_data_yearly(collection):
383
  # Extract year from the image date
384
  collection = collection.map(lambda image: image.set('year', ee.Date(image.get('system:time_start')).format('YYYY')))
@@ -411,189 +487,236 @@ def calculate_index_for_period(image, roi, index_choice, reducer_choice, custom_
411
  else:
412
  st.write("Please Select any one option...."+ index_choice.lower())
413
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id, index_choice, reducer_choice, shape_type, aggregation_period, custom_formula=""):
415
  aggregated_results = []
416
-
417
- # Check if the index_choice is 'custom formula' and the custom formula is empty
418
  if index_choice.lower() == 'custom_formula' and not custom_formula:
419
  st.error("Custom formula cannot be empty. Please provide a formula.")
420
- return aggregated_results # Return early to avoid further processing
421
 
422
- # Initialize progress bar
423
  total_steps = len(locations_df)
424
  progress_bar = st.progress(0)
425
  progress_text = st.empty()
426
-
427
  with st.spinner('Processing data...'):
428
  if shape_type.lower() == "point":
429
  for idx, row in locations_df.iterrows():
430
- # Check if the latitude and longitude columns exist and have values
431
  latitude = row.get('latitude')
432
  longitude = row.get('longitude')
433
-
434
  if pd.isna(latitude) or pd.isna(longitude):
435
  st.warning(f"Skipping location {idx} with missing latitude or longitude")
436
  continue
437
-
438
  location_name = row.get('name', f"Location_{idx}")
439
-
440
  roi = ee.Geometry.Point([longitude, latitude])
441
-
442
  collection = ee.ImageCollection(dataset_id) \
443
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
444
  .filterBounds(roi)
445
-
446
  # Aggregate data based on the selected period
447
  if aggregation_period.lower() == 'daily':
448
  collection = aggregate_data_daily(collection)
449
  elif aggregation_period.lower() == 'weekly':
450
  collection = aggregate_data_weekly(collection)
451
  elif aggregation_period.lower() == 'monthly':
452
- collection = aggregate_data_monthly(collection)
453
  elif aggregation_period.lower() == 'yearly':
454
  collection = aggregate_data_yearly(collection)
455
-
456
  # Process each image in the collection
457
  image_list = collection.toList(collection.size())
458
-
459
  for i in range(image_list.size().getInfo()):
460
  image = ee.Image(image_list.get(i))
461
-
462
  if aggregation_period.lower() == 'daily':
463
  timestamp = image.get('day')
 
 
464
  elif aggregation_period.lower() == 'weekly':
465
- timestamp = image.get('week')
 
 
 
 
 
 
 
 
466
  elif aggregation_period.lower() == 'monthly':
467
  timestamp = image.get('month')
 
 
468
  elif aggregation_period.lower() == 'yearly':
469
  timestamp = image.get('year')
470
-
471
- date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
472
-
473
- # Calculate the index for each period
474
  index_image = calculate_index_for_period(image, roi, index_choice, reducer_choice, custom_formula)
475
-
476
- # Skip if index_image is None
477
- if index_image is None:
478
- st.warning(f"Index calculation failed for {location_name} on {date}. Skipping this entry.")
479
- continue
480
 
481
- # Reduce the region to get the aggregated value
482
  try:
483
  index_value = index_image.reduceRegion(
484
  reducer=get_reducer(reducer_choice),
485
  geometry=roi,
486
  scale=30
487
  ).get(index_image.bandNames().get(0))
488
-
489
  calculated_value = index_value.getInfo()
490
-
491
- # Append the results if valid
492
  if isinstance(calculated_value, (int, float)):
493
  aggregated_results.append({
494
  'Location Name': location_name,
495
  'Latitude': latitude,
496
  'Longitude': longitude,
497
- 'Date': date,
 
 
498
  'Calculated Value': calculated_value
499
  })
500
  else:
501
  st.warning(f"Skipping invalid value for {location_name} on {date}")
502
  except Exception as e:
503
  st.error(f"Error retrieving value for {location_name}: {e}")
504
-
505
- # Update progress bar
506
  progress_percentage = (idx + 1) / total_steps
507
  progress_bar.progress(progress_percentage)
508
  progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
509
-
510
  elif shape_type.lower() == "polygon":
511
  for idx, row in locations_df.iterrows():
512
  polygon_name = row.get('name', f"Polygon_{idx}")
513
  polygon_geometry = row.get('geometry')
514
-
515
  location_name = polygon_name
516
-
517
  try:
518
  roi = convert_to_ee_geometry(polygon_geometry)
519
  except ValueError as e:
520
  st.warning(f"Skipping invalid polygon {polygon_name}: {e}")
521
  continue
522
-
523
  collection = ee.ImageCollection(dataset_id) \
524
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
525
  .filterBounds(roi)
526
-
527
  # Aggregate data based on the selected period
528
  if aggregation_period.lower() == 'daily':
529
  collection = aggregate_data_daily(collection)
530
  elif aggregation_period.lower() == 'weekly':
531
  collection = aggregate_data_weekly(collection)
532
  elif aggregation_period.lower() == 'monthly':
533
- collection = aggregate_data_monthly(collection)
534
  elif aggregation_period.lower() == 'yearly':
535
  collection = aggregate_data_yearly(collection)
536
-
537
  # Process each image in the collection
538
  image_list = collection.toList(collection.size())
539
-
540
  for i in range(image_list.size().getInfo()):
541
  image = ee.Image(image_list.get(i))
542
-
543
  if aggregation_period.lower() == 'daily':
544
  timestamp = image.get('day')
 
 
545
  elif aggregation_period.lower() == 'weekly':
546
- timestamp = image.get('week')
 
 
 
 
 
 
 
 
547
  elif aggregation_period.lower() == 'monthly':
548
  timestamp = image.get('month')
 
 
549
  elif aggregation_period.lower() == 'yearly':
550
  timestamp = image.get('year')
551
-
552
- date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
553
-
554
- # Calculate the index for each period
555
- index_image = calculate_index_for_period(image, roi, index_choice, reducer_choice, custom_formula)
556
 
557
- # Skip if index_image is None
558
- if index_image is None:
559
- st.warning(f"Index calculation failed for {location_name} on {date}. Skipping this entry.")
560
- continue
561
 
562
- # Reduce the region to get the aggregated value
563
  try:
564
  index_value = index_image.reduceRegion(
565
  reducer=get_reducer(reducer_choice),
566
  geometry=roi,
567
  scale=30
568
  ).get(index_image.bandNames().get(0))
569
-
570
  calculated_value = index_value.getInfo()
571
-
572
- # Append the results if valid
573
  if isinstance(calculated_value, (int, float)):
574
  aggregated_results.append({
575
  'Location Name': location_name,
576
- 'Date': date,
 
 
577
  'Calculated Value': calculated_value
578
  })
579
  else:
580
  st.warning(f"Skipping invalid value for {location_name} on {date}")
581
  except Exception as e:
582
  st.error(f"Error retrieving value for {location_name}: {e}")
583
-
584
- # Update progress bar
585
  progress_percentage = (idx + 1) / total_steps
586
  progress_bar.progress(progress_percentage)
587
  progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
588
-
589
- return aggregated_results
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
590
 
591
  # When the user clicks the process button, start the calculation
592
  if st.button(f"Calculate ({index_choice})"):
593
  if file_upload is not None:
594
- # Read the user-uploaded file
595
- if shape_type.lower() == "point":
596
- # Process results for the selected aggregation period
597
  results = process_aggregation(
598
  locations_df,
599
  start_date_str,
@@ -605,14 +728,10 @@ if st.button(f"Calculate ({index_choice})"):
605
  aggregation_period,
606
  custom_formula
607
  )
608
-
609
- # Display the results in a DataFrame
610
  if results:
611
  result_df = pd.DataFrame(results)
612
  st.write(f"Processed Results Table ({aggregation_period}):")
613
  st.dataframe(result_df)
614
-
615
- # Provide a download button for the result CSV file
616
  filename = f"{main_selection}_{dataset_id}_{start_date.strftime('%Y/%m/%d')}_{end_date.strftime('%Y/%m/%d')}_{aggregation_period.lower()}.csv"
617
  st.download_button(
618
  label="Download results as CSV",
@@ -620,15 +739,12 @@ if st.button(f"Calculate ({index_choice})"):
620
  file_name=filename,
621
  mime='text/csv'
622
  )
623
-
624
- # Once processing is complete, hide the spinner
625
- st.spinner('') # This will stop the spinner
626
  st.success('Processing complete!')
627
  else:
628
  st.warning("No results were generated.")
629
 
630
  elif shape_type.lower() == "polygon":
631
- # Process results for the selected aggregation period
632
  results = process_aggregation(
633
  locations_df,
634
  start_date_str,
@@ -640,14 +756,10 @@ if st.button(f"Calculate ({index_choice})"):
640
  aggregation_period,
641
  custom_formula
642
  )
643
-
644
- # Display the results in a DataFrame
645
  if results:
646
  result_df = pd.DataFrame(results)
647
  st.write(f"Processed Results Table ({aggregation_period}):")
648
  st.dataframe(result_df)
649
-
650
- # Provide a download button for the result CSV file
651
  filename = f"{main_selection}_{dataset_id}_{start_date.strftime('%Y/%m/%d')}_{end_date.strftime('%Y/%m/%d')}_{aggregation_period.lower()}.csv"
652
  st.download_button(
653
  label="Download results as CSV",
@@ -655,12 +767,10 @@ if st.button(f"Calculate ({index_choice})"):
655
  file_name=filename,
656
  mime='text/csv'
657
  )
658
- # Once processing is complete, hide the spinner
659
- st.spinner('') # This will stop the spinner
660
  st.success('Processing complete!')
661
  else:
662
  st.warning("No results were generated.")
663
 
664
- else:
665
- st.warning("Please upload a file.")
666
-
 
7
  from datetime import datetime
8
  import leafmap.foliumap as leafmap
9
  import re
10
+ from shapely.geometry import base
11
+ from lxml import etree
12
+ from xml.etree import ElementTree as ET
13
 
14
  # Set up the page layout
15
  st.set_page_config(layout="wide")
 
95
  elif index_choice.lower() == 'average no₂':
96
  st.write("Formula for Average NO₂: Average NO₂ = Mean(NO2 band)")
97
  elif index_choice.lower() == 'custom formula':
98
+ custom_formula = st.text_input("Enter Custom Formula (e.g., B5,B4 for two bands or B3 for one band)")
99
  # Check if custom formula is empty and show warning
100
  if not custom_formula:
101
  st.warning("Please enter a custom formula before proceeding.")
102
  else:
103
+ # Check if the input contains a comma (indicating two bands)
104
+ if ',' in custom_formula:
105
+ # Split the input into two bands and strip whitespace
106
+ band1, band2 = [b.strip() for b in custom_formula.split(',', 1)]
107
+ st.write(f"Custom Formula: ({band1} - {band2}) / ({band1} + {band2})")
108
+ else:
109
+ # Single band case
110
+ band = custom_formula.strip()
111
+ st.write(f"Custom Formula: {band}")
112
 
113
  # Function to get the corresponding reducer based on user input
114
  def get_reducer(reducer_name):
 
134
  index=0 # Default to 'mean'
135
  )
136
 
 
137
  def convert_to_ee_geometry(geometry):
138
+ # Handle Shapely geometry
139
+ if isinstance(geometry, base.BaseGeometry):
140
+ if geometry.is_valid:
141
+ geojson = geometry.__geo_interface__
142
+ print("Shapely GeoJSON:", geojson) # Debugging: Inspect the GeoJSON structure
143
+ return ee.Geometry(geojson)
144
+ else:
145
+ raise ValueError("Invalid geometry: The polygon geometry is not valid.")
146
+
147
+ # Handle GeoJSON input (string or dictionary)
148
+ elif isinstance(geometry, dict) or isinstance(geometry, str):
149
+ try:
150
+ if isinstance(geometry, str):
151
+ geometry = json.loads(geometry)
152
+ if 'type' in geometry and 'coordinates' in geometry:
153
+ print("GeoJSON Geometry:", geometry) # Debugging: Inspect the GeoJSON structure
154
+ return ee.Geometry(geometry)
155
+ else:
156
+ raise ValueError("GeoJSON format is invalid.")
157
+ except Exception as e:
158
+ raise ValueError(f"Error parsing GeoJSON: {e}")
159
+
160
+ # Handle KML input (string or file path)
161
+ elif isinstance(geometry, str) and geometry.lower().endswith(".kml"):
162
+ try:
163
+ # Parse the KML file
164
+ tree = ET.parse(geometry)
165
+ kml_root = tree.getroot()
166
+
167
+ # Extract coordinates from KML geometry (assuming it's a Polygon or MultiPolygon)
168
+ # KML coordinates are usually within the <coordinates> tag
169
+ kml_namespace = {'kml': 'http://www.opengis.net/kml/2.2'}
170
+ coordinates = kml_root.findall(".//kml:coordinates", kml_namespace)
171
+
172
+ if coordinates:
173
+ # Extract and format coordinates
174
+ coords_text = coordinates[0].text.strip()
175
+ coords = coords_text.split()
176
+ # Convert KML coordinates (comma-separated) into a list of tuples
177
+ coords = [tuple(map(float, coord.split(','))) for coord in coords]
178
+ geojson = {
179
+ "type": "Polygon", # Make sure the GeoJSON type is Polygon
180
+ "coordinates": [coords] # Wrap the coordinates in a list (required by GeoJSON format)
181
+ }
182
+
183
+ # Debugging: Inspect the KML-to-GeoJSON structure
184
+ print("KML GeoJSON:", geojson)
185
+
186
+ return ee.Geometry(geojson)
187
+ else:
188
+ raise ValueError("KML does not contain valid coordinates.")
189
+ except Exception as e:
190
+ raise ValueError(f"Error parsing KML: {e}")
191
+
192
  else:
193
+ raise ValueError("Unsupported geometry input type. Supported types are Shapely, GeoJSON, and KML.")
194
 
195
  # Function to read points from CSV
196
  def read_csv(file_path):
 
207
  gdf = gpd.read_file(file_path, driver='KML')
208
  return gdf
209
 
210
+
211
+ # Date Input for Start and End Dates
212
+ start_date = st.date_input("Start Date", value=pd.to_datetime('2024-11-01'))
213
+ end_date = st.date_input("End Date", value=pd.to_datetime('2024-12-01'))
214
+
215
+ # Convert start_date and end_date to string format for Earth Engine
216
+ start_date_str = start_date.strftime('%Y-%m-%d')
217
+ end_date_str = end_date.strftime('%Y-%m-%d')
218
+
219
+ # Aggregation period selection
220
+ aggregation_period = st.selectbox("Select Aggregation Period", ["Daily", "Weekly", "Monthly", "Yearly"], index=0)
221
+
222
  # Ask user whether they want to process 'Point' or 'Polygon' data (case-insensitive)
223
  shape_type = st.selectbox("Do you want to process 'Point' or 'Polygon' data?", ["Point", "Polygon"])
224
 
 
247
  st.stop() # Stop further processing if polygons are detected
248
 
249
  # Processing the point data
250
+ with st.spinner('Processing Map...'):
251
  if locations_df is not None and not locations_df.empty:
252
  # For GeoJSON data, the coordinates are in the geometry column
253
  if 'geometry' in locations_df.columns:
 
304
  st.stop() # Stop further processing if point data is detected
305
 
306
  # Processing the polygon data
307
+ with st.spinner('Processing Map...'):
308
  if locations_df is not None and not locations_df.empty:
309
  # Ensure the 'geometry' column exists in the dataframe
310
  if 'geometry' not in locations_df.columns:
 
336
  # Store the map in session_state
337
  st.session_state.map_data = m
338
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  # Initialize session state for storing results if not already done
340
  if 'results' not in st.session_state:
341
  st.session_state.results = []
 
379
  ndwi = image.normalizedDifference(['B3', 'B8']).rename('NDWI')
380
  return ndwi
381
 
382
+ def calculate_custom_formula(image, geometry, custom_formula, reducer_choice, scale=30):
383
+ try:
384
+ if "," in custom_formula:
385
+ band1, band2 = [b.strip() for b in custom_formula.split(",")]
386
+ band_names = image.bandNames().getInfo()
387
+ if band1 not in band_names or band2 not in band_names:
388
+ raise ValueError(f"One or both bands ({band1}, {band2}) do not exist in the image.")
389
+ result = image.normalizedDifference([band1, band2]).rename('custom_formula')
390
+ else:
391
+ band = custom_formula.strip()
392
+ band_names = image.bandNames().getInfo()
393
+ if band not in band_names:
394
+ raise ValueError(f"The band '{band}' does not exist in the image.")
395
+ result = image.select(band).rename('custom_formula')
396
+ return result
397
+ except Exception as e:
398
+ return ee.Image(0).rename('custom_formula').set('error', str(e))
399
+
400
  # Modify aggregation functions to return the correct time period and aggregated results
401
  def aggregate_data_daily(collection):
402
  # Extract day from the image date (using the exact date)
 
417
  return ee.ImageCollection(daily_images)
418
 
419
  def aggregate_data_weekly(collection):
420
+ # Extract the start date of the week from the image date
421
+ collection = collection.map(lambda image: image.set(
422
+ 'week_start', ee.Date(image.get('system:time_start'))
423
+ .advance(-ee.Date(image.get('system:time_start')).getRelative('day', 'week'), 'day')
424
+ ))
425
+ # Group images by week start date
426
+ grouped_by_week = collection.aggregate_array('week_start').distinct()
427
+
428
+ def calculate_weekly_mean(week_start):
429
+ # Filter the collection by the specific week start date
430
+ weekly_collection = collection.filter(ee.Filter.eq('week_start', week_start))
431
+ weekly_mean = weekly_collection.mean() # Calculate mean for the week
432
+ return weekly_mean.set('week_start', week_start)
433
 
 
 
 
 
 
 
 
 
434
  # Calculate the weekly mean for each week
435
  weekly_images = ee.List(grouped_by_week.map(calculate_weekly_mean))
 
436
  return ee.ImageCollection(weekly_images)
437
+
438
+ def aggregate_data_monthly(collection, start_date, end_date):
439
+ # Filter the collection for the specific date range
440
+ collection = collection.filterDate(start_date, end_date)
441
+
442
  # Extract month and year from the image date
443
  collection = collection.map(lambda image: image.set('month', ee.Date(image.get('system:time_start')).format('YYYY-MM')))
444
 
 
454
  monthly_images = ee.List(grouped_by_month.map(calculate_monthly_mean))
455
 
456
  return ee.ImageCollection(monthly_images)
457
+
458
  def aggregate_data_yearly(collection):
459
  # Extract year from the image date
460
  collection = collection.map(lambda image: image.set('year', ee.Date(image.get('system:time_start')).format('YYYY')))
 
487
  else:
488
  st.write("Please Select any one option...."+ index_choice.lower())
489
 
490
+ def aggregate_data_weekly(collection):
491
+ def set_week_start(image):
492
+ # Get the image timestamp
493
+ date = ee.Date(image.get('system:time_start'))
494
+ # Calculate days since the start of the week (0 = Monday, 6 = Sunday)
495
+ days_since_week_start = date.getRelative('day', 'week')
496
+ # Convert to ee.Number and negate it to get the offset to the week start
497
+ offset = ee.Number(days_since_week_start).multiply(-1)
498
+ # Advance the date by the negative offset to get the week start
499
+ week_start = date.advance(offset, 'day')
500
+ return image.set('week_start', week_start.format('YYYY-MM-dd')) # Ensure string format
501
+
502
+ # Apply the week start calculation to each image
503
+ collection = collection.map(set_week_start)
504
+
505
+ # Group images by week start date
506
+ grouped_by_week = collection.aggregate_array('week_start').distinct()
507
+
508
+ def calculate_weekly_mean(week_start):
509
+ # Filter the collection by the specific week start date
510
+ weekly_collection = collection.filter(ee.Filter.eq('week_start', week_start))
511
+ weekly_mean = weekly_collection.mean() # Calculate mean for the week
512
+ return weekly_mean.set('week_start', week_start)
513
+
514
+ # Calculate the weekly mean for each week
515
+ weekly_images = ee.List(grouped_by_week.map(calculate_weekly_mean))
516
+ return ee.ImageCollection(weekly_images)
517
+
518
  def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id, index_choice, reducer_choice, shape_type, aggregation_period, custom_formula=""):
519
  aggregated_results = []
520
+
 
521
  if index_choice.lower() == 'custom_formula' and not custom_formula:
522
  st.error("Custom formula cannot be empty. Please provide a formula.")
523
+ return aggregated_results
524
 
 
525
  total_steps = len(locations_df)
526
  progress_bar = st.progress(0)
527
  progress_text = st.empty()
528
+
529
  with st.spinner('Processing data...'):
530
  if shape_type.lower() == "point":
531
  for idx, row in locations_df.iterrows():
 
532
  latitude = row.get('latitude')
533
  longitude = row.get('longitude')
 
534
  if pd.isna(latitude) or pd.isna(longitude):
535
  st.warning(f"Skipping location {idx} with missing latitude or longitude")
536
  continue
537
+
538
  location_name = row.get('name', f"Location_{idx}")
 
539
  roi = ee.Geometry.Point([longitude, latitude])
540
+
541
  collection = ee.ImageCollection(dataset_id) \
542
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
543
  .filterBounds(roi)
544
+
545
  # Aggregate data based on the selected period
546
  if aggregation_period.lower() == 'daily':
547
  collection = aggregate_data_daily(collection)
548
  elif aggregation_period.lower() == 'weekly':
549
  collection = aggregate_data_weekly(collection)
550
  elif aggregation_period.lower() == 'monthly':
551
+ collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
552
  elif aggregation_period.lower() == 'yearly':
553
  collection = aggregate_data_yearly(collection)
554
+
555
  # Process each image in the collection
556
  image_list = collection.toList(collection.size())
557
+ processed_weeks = set() # Track processed weeks to avoid duplicates
558
  for i in range(image_list.size().getInfo()):
559
  image = ee.Image(image_list.get(i))
560
+
561
  if aggregation_period.lower() == 'daily':
562
  timestamp = image.get('day')
563
+ period_label = 'Date'
564
+ date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
565
  elif aggregation_period.lower() == 'weekly':
566
+ timestamp = image.get('week_start')
567
+ period_label = 'Week'
568
+ date = ee.String(timestamp).getInfo() # Already formatted as YYYY-MM-dd
569
+ # Skip if week is outside the date range or already processed
570
+ if (pd.to_datetime(date) < pd.to_datetime(start_date_str) or
571
+ pd.to_datetime(date) > pd.to_datetime(end_date_str) or
572
+ date in processed_weeks):
573
+ continue
574
+ processed_weeks.add(date)
575
  elif aggregation_period.lower() == 'monthly':
576
  timestamp = image.get('month')
577
+ period_label = 'Month'
578
+ date = ee.Date(timestamp).format('YYYY-MM').getInfo()
579
  elif aggregation_period.lower() == 'yearly':
580
  timestamp = image.get('year')
581
+ period_label = 'Year'
582
+ date = ee.Date(timestamp).format('YYYY').getInfo()
583
+
 
584
  index_image = calculate_index_for_period(image, roi, index_choice, reducer_choice, custom_formula)
 
 
 
 
 
585
 
 
586
  try:
587
  index_value = index_image.reduceRegion(
588
  reducer=get_reducer(reducer_choice),
589
  geometry=roi,
590
  scale=30
591
  ).get(index_image.bandNames().get(0))
592
+
593
  calculated_value = index_value.getInfo()
594
+
 
595
  if isinstance(calculated_value, (int, float)):
596
  aggregated_results.append({
597
  'Location Name': location_name,
598
  'Latitude': latitude,
599
  'Longitude': longitude,
600
+ period_label: date,
601
+ 'Start Date': start_date_str,
602
+ 'End Date': end_date_str,
603
  'Calculated Value': calculated_value
604
  })
605
  else:
606
  st.warning(f"Skipping invalid value for {location_name} on {date}")
607
  except Exception as e:
608
  st.error(f"Error retrieving value for {location_name}: {e}")
609
+
 
610
  progress_percentage = (idx + 1) / total_steps
611
  progress_bar.progress(progress_percentage)
612
  progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
613
+
614
  elif shape_type.lower() == "polygon":
615
  for idx, row in locations_df.iterrows():
616
  polygon_name = row.get('name', f"Polygon_{idx}")
617
  polygon_geometry = row.get('geometry')
 
618
  location_name = polygon_name
619
+
620
  try:
621
  roi = convert_to_ee_geometry(polygon_geometry)
622
  except ValueError as e:
623
  st.warning(f"Skipping invalid polygon {polygon_name}: {e}")
624
  continue
625
+
626
  collection = ee.ImageCollection(dataset_id) \
627
  .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
628
  .filterBounds(roi)
629
+
630
  # Aggregate data based on the selected period
631
  if aggregation_period.lower() == 'daily':
632
  collection = aggregate_data_daily(collection)
633
  elif aggregation_period.lower() == 'weekly':
634
  collection = aggregate_data_weekly(collection)
635
  elif aggregation_period.lower() == 'monthly':
636
+ collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
637
  elif aggregation_period.lower() == 'yearly':
638
  collection = aggregate_data_yearly(collection)
639
+
640
  # Process each image in the collection
641
  image_list = collection.toList(collection.size())
642
+ processed_weeks = set() # Track processed weeks to avoid duplicates
643
  for i in range(image_list.size().getInfo()):
644
  image = ee.Image(image_list.get(i))
645
+
646
  if aggregation_period.lower() == 'daily':
647
  timestamp = image.get('day')
648
+ period_label = 'Date'
649
+ date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
650
  elif aggregation_period.lower() == 'weekly':
651
+ timestamp = image.get('week_start')
652
+ period_label = 'Week'
653
+ date = ee.String(timestamp).getInfo() # Already formatted as YYYY-MM-dd
654
+ # Skip if week is outside the date range or already processed
655
+ if (pd.to_datetime(date) < pd.to_datetime(start_date_str) or
656
+ pd.to_datetime(date) > pd.to_datetime(end_date_str) or
657
+ date in processed_weeks):
658
+ continue
659
+ processed_weeks.add(date)
660
  elif aggregation_period.lower() == 'monthly':
661
  timestamp = image.get('month')
662
+ period_label = 'Month'
663
+ date = ee.Date(timestamp).format('YYYY-MM').getInfo()
664
  elif aggregation_period.lower() == 'yearly':
665
  timestamp = image.get('year')
666
+ period_label = 'Year'
667
+ date = ee.Date(timestamp).format('YYYY').getInfo()
 
 
 
668
 
669
+ index_image = calculate_index_for_period(image, roi, index_choice, reducer_choice, custom_formula)
 
 
 
670
 
 
671
  try:
672
  index_value = index_image.reduceRegion(
673
  reducer=get_reducer(reducer_choice),
674
  geometry=roi,
675
  scale=30
676
  ).get(index_image.bandNames().get(0))
677
+
678
  calculated_value = index_value.getInfo()
679
+
 
680
  if isinstance(calculated_value, (int, float)):
681
  aggregated_results.append({
682
  'Location Name': location_name,
683
+ period_label: date,
684
+ 'Start Date': start_date_str,
685
+ 'End Date': end_date_str,
686
  'Calculated Value': calculated_value
687
  })
688
  else:
689
  st.warning(f"Skipping invalid value for {location_name} on {date}")
690
  except Exception as e:
691
  st.error(f"Error retrieving value for {location_name}: {e}")
692
+
 
693
  progress_percentage = (idx + 1) / total_steps
694
  progress_bar.progress(progress_percentage)
695
  progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
696
+
697
+ if aggregated_results:
698
+ result_df = pd.DataFrame(aggregated_results)
699
+ if aggregation_period.lower() == 'daily':
700
+ aggregated_output = result_df.groupby('Location Name').agg({
701
+ 'Latitude': 'first' if shape_type.lower() == 'point' else None,
702
+ 'Longitude': 'first' if shape_type.lower() == 'point' else None,
703
+ 'Start Date': 'first',
704
+ 'End Date': 'first',
705
+ 'Calculated Value': 'mean'
706
+ }).reset_index()
707
+ # Remove None columns (for polygons)
708
+ aggregated_output = aggregated_output[[col for col in aggregated_output.columns if col is not None]]
709
+ aggregated_output.rename(columns={'Calculated Value': 'Aggregated Value'}, inplace=True)
710
+ return aggregated_output.to_dict(orient='records')
711
+ else:
712
+ return result_df.to_dict(orient='records')
713
+
714
+ return []
715
 
716
  # When the user clicks the process button, start the calculation
717
  if st.button(f"Calculate ({index_choice})"):
718
  if file_upload is not None:
719
+ if shape_type.lower() == "point":
 
 
720
  results = process_aggregation(
721
  locations_df,
722
  start_date_str,
 
728
  aggregation_period,
729
  custom_formula
730
  )
 
 
731
  if results:
732
  result_df = pd.DataFrame(results)
733
  st.write(f"Processed Results Table ({aggregation_period}):")
734
  st.dataframe(result_df)
 
 
735
  filename = f"{main_selection}_{dataset_id}_{start_date.strftime('%Y/%m/%d')}_{end_date.strftime('%Y/%m/%d')}_{aggregation_period.lower()}.csv"
736
  st.download_button(
737
  label="Download results as CSV",
 
739
  file_name=filename,
740
  mime='text/csv'
741
  )
742
+ st.spinner('')
 
 
743
  st.success('Processing complete!')
744
  else:
745
  st.warning("No results were generated.")
746
 
747
  elif shape_type.lower() == "polygon":
 
748
  results = process_aggregation(
749
  locations_df,
750
  start_date_str,
 
756
  aggregation_period,
757
  custom_formula
758
  )
 
 
759
  if results:
760
  result_df = pd.DataFrame(results)
761
  st.write(f"Processed Results Table ({aggregation_period}):")
762
  st.dataframe(result_df)
 
 
763
  filename = f"{main_selection}_{dataset_id}_{start_date.strftime('%Y/%m/%d')}_{end_date.strftime('%Y/%m/%d')}_{aggregation_period.lower()}.csv"
764
  st.download_button(
765
  label="Download results as CSV",
 
767
  file_name=filename,
768
  mime='text/csv'
769
  )
770
+ st.spinner('')
 
771
  st.success('Processing complete!')
772
  else:
773
  st.warning("No results were generated.")
774
 
775
+ else:
776
+ st.warning("Please upload a file.")