YashMK89 commited on
Commit
3597c83
·
verified ·
1 Parent(s): e7d2f93

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +136 -189
app.py CHANGED
@@ -175,6 +175,7 @@ def aggregate_data_daily(collection):
175
  daily_images = ee.List(grouped_by_day.map(calculate_daily_mean))
176
  return ee.ImageCollection(daily_images)
177
 
 
178
  def aggregate_data_weekly(collection, start_date_str, end_date_str):
179
  """
180
  Aggregates data on a weekly basis, starting from the exact start date provided by the user.
@@ -199,6 +200,7 @@ def aggregate_data_weekly(collection, start_date_str, end_date_str):
199
 
200
  return ee.ImageCollection.fromImages(weekly_images)
201
 
 
202
  def aggregate_data_monthly(collection, start_date, end_date):
203
  collection = collection.filterDate(start_date, end_date)
204
  collection = collection.map(lambda image: image.set('month', ee.Date(image.get('system:time_start')).format('YYYY-MM')))
@@ -210,6 +212,7 @@ def aggregate_data_monthly(collection, start_date, end_date):
210
  monthly_images = ee.List(grouped_by_month.map(calculate_monthly_mean))
211
  return ee.ImageCollection(monthly_images)
212
 
 
213
  def aggregate_data_yearly(collection):
214
  collection = collection.map(lambda image: image.set('year', ee.Date(image.get('system:time_start')).format('YYYY')))
215
  grouped_by_year = collection.aggregate_array('year').distinct()
@@ -252,6 +255,7 @@ def calculate_cloud_percentage(image, cloud_band='QA60'):
252
  return 0 # Avoid division by zero
253
  return ee.Number(cloudy_pixels).divide(ee.Number(total_pixels)).multiply(100).getInfo()
254
 
 
255
  def preprocess_collection(collection, cloud_threshold):
256
  """
257
  Apply cloud filtering to the image collection using the QA60 bitmask.
@@ -283,109 +287,106 @@ def preprocess_collection(collection, cloud_threshold):
283
  masked_collection = filtered_collection.map(mask_cloudy_pixels)
284
  return masked_collection
285
 
286
- 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, cloud_threshold=0):
287
- try:
288
- if shape_type.lower() == "point":
289
- latitude = row.get('latitude')
290
- longitude = row.get('longitude')
291
- if pd.isna(latitude) or pd.isna(longitude):
292
- return None # Skip invalid points
293
- location_name = row.get('name', f"Location_{row.name}")
294
- if kernel_size == "3x3 Kernel":
295
- buffer_size = 45 # 90m x 90m
296
- roi = ee.Geometry.Point([longitude, latitude]).buffer(buffer_size).bounds()
297
- elif kernel_size == "5x5 Kernel":
298
- buffer_size = 75 # 150m x 150m
299
- roi = ee.Geometry.Point([longitude, latitude]).buffer(buffer_size).bounds()
300
- else: # Point
301
- roi = ee.Geometry.Point([longitude, latitude])
302
- elif shape_type.lower() == "polygon":
303
- polygon_geometry = row.get('geometry')
304
- location_name = row.get('name', f"Polygon_{row.name}")
 
305
  roi = convert_to_ee_geometry(polygon_geometry)
306
  if not include_boundary:
307
  roi = roi.buffer(-30).bounds()
 
 
308
 
309
- # Filter and aggregate the image collection
310
- collection = ee.ImageCollection(dataset_id) \
311
- .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
312
- .filterBounds(roi)
313
-
314
- # Apply cloud filtering if threshold > 0
315
- if cloud_threshold > 0:
316
- collection = preprocess_collection(collection, cloud_threshold)
317
-
318
- # Apply aggregation
 
 
 
 
 
 
 
 
 
 
 
 
319
  if aggregation_period.lower() == 'custom (start date to end date)':
320
- collection = aggregate_data_custom(collection)
 
 
321
  elif aggregation_period.lower() == 'daily':
322
- collection = aggregate_data_daily(collection)
 
 
323
  elif aggregation_period.lower() == 'weekly':
324
- collection = aggregate_data_weekly(collection, start_date_str, end_date_str)
 
 
 
 
 
 
 
325
  elif aggregation_period.lower() == 'monthly':
326
- collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
 
 
327
  elif aggregation_period.lower() == 'yearly':
328
- collection = aggregate_data_yearly(collection)
 
 
329
 
330
- # Get the list of images
331
- image_list = collection.toList(collection.size())
332
- n_images = image_list.size().getInfo()
333
-
334
- aggregated_results = []
335
- for i in range(n_images):
336
- image = ee.Image(image_list.get(i))
337
-
338
- # Get the timestamp based on aggregation period
339
- if aggregation_period.lower() == 'custom (start date to end date)':
340
- timestamp = image.get('day')
341
- period_label = 'Date'
342
- date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
343
- elif aggregation_period.lower() == 'daily':
344
- timestamp = image.get('day_start')
345
- period_label = 'Date'
346
- date = ee.String(timestamp).getInfo()
347
- elif aggregation_period.lower() == 'weekly':
348
- timestamp = image.get('week_start')
349
- period_label = 'Week'
350
- date = ee.String(timestamp).getInfo()
351
- elif aggregation_period.lower() == 'monthly':
352
- timestamp = image.get('month')
353
- period_label = 'Month'
354
- date = ee.Date(timestamp).format('YYYY-MM').getInfo()
355
- elif aggregation_period.lower() == 'yearly':
356
- timestamp = image.get('year')
357
- period_label = 'Year'
358
- date = ee.Date(timestamp).format('YYYY').getInfo()
359
-
360
- # Calculate the custom formula
361
- index_image = calculate_custom_formula(image, roi, selected_bands, custom_formula, reducer_choice, scale=30)
362
-
363
- # Get the reduced value
364
  index_value = index_image.reduceRegion(
365
  reducer=get_reducer(reducer_choice),
366
  geometry=roi,
367
  scale=30
368
- ).get('custom_result').getInfo()
369
-
370
- if isinstance(index_value, (int, float)):
371
  result = {
372
  'Location Name': location_name,
373
  period_label: date,
374
  'Start Date': start_date_str,
375
  'End Date': end_date_str,
376
- 'Calculated Value': index_value
377
  }
378
  if shape_type.lower() == 'point':
379
- result[original_lat_col] = latitude
380
- result[original_lon_col] = longitude
381
  aggregated_results.append(result)
382
-
383
- return aggregated_results
384
-
385
- except Exception as e:
386
- st.error(f"Error processing {location_name if 'location_name' in locals() else 'location'}: {str(e)}")
387
- return None
388
 
 
389
  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, cloud_threshold=0):
390
  aggregated_results = []
391
  total_steps = len(locations_df)
@@ -393,6 +394,20 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
393
  progress_text = st.empty()
394
  start_time = time.time() # Start timing the process
395
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
396
  with ThreadPoolExecutor(max_workers=10) as executor:
397
  futures = []
398
  for idx, row in locations_df.iterrows():
@@ -410,8 +425,7 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
410
  original_lat_col,
411
  original_lon_col,
412
  kernel_size,
413
- include_boundary,
414
- cloud_threshold
415
  )
416
  futures.append(future)
417
  completed = 0
@@ -435,6 +449,7 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
435
  'Start Date': 'first',
436
  'End Date': 'first',
437
  'Calculated Value': 'mean'
 
438
  }
439
  if shape_type.lower() == 'point':
440
  agg_dict[original_lat_col] = 'first'
@@ -578,6 +593,7 @@ cloud_threshold = st.slider(
578
  help="Tiles with cloud coverage exceeding this threshold will be excluded. Individual cloudy pixels will also be masked."
579
  )
580
 
 
581
  aggregation_period = st.selectbox(
582
  "Select Aggregation Period (e.g, Custom(Start Date to End Date) , Daily , Weekly , Monthly , Yearly)",
583
  ["Custom (Start Date to End Date)", "Daily", "Weekly", "Monthly", "Yearly"],
@@ -730,113 +746,44 @@ if file_upload is not None:
730
  st.write("Map of Uploaded Polygons:")
731
  m.to_streamlit()
732
 
733
- def visualize_clouds(collection, roi, cloud_band='QA60'):
734
- """
735
- Visualize cloud coverage in the selected region of interest (ROI).
736
- """
737
- # Select the first image in the collection for visualization
738
- first_image = ee.Image(collection.first())
739
-
740
- # Decode the QA60 bitmask for clouds
741
- qa60 = first_image.select(cloud_band)
742
- opaque_clouds = qa60.bitwiseAnd(1 << 10) # Bit 10: Opaque clouds
743
- cirrus_clouds = qa60.bitwiseAnd(1 << 11) # Bit 11: Cirrus clouds
744
- cloud_mask = opaque_clouds.Or(cirrus_clouds)
745
-
746
- # Create a cloud visualization layer
747
- cloud_vis = {
748
- 'min': 0,
749
- 'max': 1,
750
- 'palette': ['white', 'blue'] # White = clear, Blue = cloudy
751
- }
752
-
753
- return cloud_mask, cloud_vis
754
-
755
- # In the cloud preview button section:
756
- def visualize_clouds(collection, roi, cloud_band='QA60'):
757
- """
758
- Visualize cloud coverage in the selected region of interest (ROI).
759
- """
760
- # Select the first image in the collection for visualization
761
- first_image = ee.Image(collection.first())
762
-
763
- # Decode the QA60 bitmask for clouds
764
- qa60 = first_image.select(cloud_band)
765
- opaque_clouds = qa60.bitwiseAnd(1 << 10) # Bit 10: Opaque clouds
766
- cirrus_clouds = qa60.bitwiseAnd(1 << 11) # Bit 11: Cirrus clouds
767
- cloud_mask = opaque_clouds.Or(cirrus_clouds)
768
-
769
- # Create a cloud visualization layer
770
- cloud_vis = {
771
- 'min': 0,
772
- 'max': 1,
773
- 'palette': ['white', 'blue'] # White = clear, Blue = cloudy
774
- }
775
-
776
- return cloud_mask, cloud_vis
777
-
778
- # In the cloud preview button section:
779
- if st.button("Preview Cloud Coverage"):
780
- if not locations_df.empty:
781
- with st.spinner("Loading Cloud Coverage Preview..."):
782
- try:
783
- # Debug prints (properly indented)
784
- st.write(f"Leafmap version: {leafmap.__version__}")
785
-
786
- # Filter the collection for the uploaded geometry
787
- if shape_type.lower() == "point":
788
- latitude = locations_df.iloc[0]['latitude']
789
- longitude = locations_df.iloc[0]['longitude']
790
- roi = ee.Geometry.Point([longitude, latitude])
791
- elif shape_type.lower() == "polygon":
792
- polygon_geometry = locations_df.iloc[0]['geometry']
793
- roi = convert_to_ee_geometry(polygon_geometry)
794
-
795
- # Load the image collection
796
- raw_collection = ee.ImageCollection(dataset_id) \
797
- .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
798
- .filterBounds(roi)
799
-
800
- # Debug prints (properly indented)
801
- st.write(f"Collection size: {raw_collection.size().getInfo()}")
802
- st.write(f"Available bands: {raw_collection.first().bandNames().getInfo()}")
803
-
804
- if raw_collection.size().getInfo() == 0:
805
- st.warning("No images found for the selected date range and location.")
806
- st.stop()
807
-
808
- # Visualize cloud coverage
809
- cloud_mask, cloud_vis = visualize_clouds(raw_collection, roi, cloud_band='QA60')
810
-
811
- # Create a new map centered on the ROI
812
- centroid = roi.centroid().coordinates().getInfo()
813
- m = leafmap.Map(center=[centroid[1], centroid[0]], zoom=10)
814
-
815
- # Add the layers using the proper Leafmap methods
816
- m.add_layer(cloud_mask, cloud_vis, "Cloud Mask")
817
- m.add_layer(roi, {'color': 'red'}, "Region of Interest")
818
-
819
- st.write("Cloud Coverage Preview:")
820
- m.to_streamlit()
821
- except Exception as e:
822
- st.error(f"Error visualizing cloud coverage: {str(e)}")
823
- else:
824
- st.warning("Please upload a valid file to proceed.")
825
-
826
- # After visualizing cloud coverage
827
- st.markdown("<h5>Adjust Cloud Threshold</h5>", unsafe_allow_html=True)
828
- new_cloud_threshold = st.slider(
829
- "Select Maximum Cloud Coverage Threshold (%)",
830
- min_value=0,
831
- max_value=50,
832
- value=cloud_threshold, # Start with the current threshold
833
- step=5,
834
- help="Lower the threshold to exclude more cloudy images."
835
- )
836
-
837
- if st.button("Apply New Cloud Threshold"):
838
- cloud_threshold = new_cloud_threshold
839
- st.success(f"New cloud threshold applied: {cloud_threshold}%")
840
 
841
  if st.button(f"Calculate {custom_formula}"):
842
  if not locations_df.empty:
 
175
  daily_images = ee.List(grouped_by_day.map(calculate_daily_mean))
176
  return ee.ImageCollection(daily_images)
177
 
178
+
179
  def aggregate_data_weekly(collection, start_date_str, end_date_str):
180
  """
181
  Aggregates data on a weekly basis, starting from the exact start date provided by the user.
 
200
 
201
  return ee.ImageCollection.fromImages(weekly_images)
202
 
203
+
204
  def aggregate_data_monthly(collection, start_date, end_date):
205
  collection = collection.filterDate(start_date, end_date)
206
  collection = collection.map(lambda image: image.set('month', ee.Date(image.get('system:time_start')).format('YYYY-MM')))
 
212
  monthly_images = ee.List(grouped_by_month.map(calculate_monthly_mean))
213
  return ee.ImageCollection(monthly_images)
214
 
215
+
216
  def aggregate_data_yearly(collection):
217
  collection = collection.map(lambda image: image.set('year', ee.Date(image.get('system:time_start')).format('YYYY')))
218
  grouped_by_year = collection.aggregate_array('year').distinct()
 
255
  return 0 # Avoid division by zero
256
  return ee.Number(cloudy_pixels).divide(ee.Number(total_pixels)).multiply(100).getInfo()
257
 
258
+ # Preprocessing function with cloud filtering
259
  def preprocess_collection(collection, cloud_threshold):
260
  """
261
  Apply cloud filtering to the image collection using the QA60 bitmask.
 
287
  masked_collection = filtered_collection.map(mask_cloudy_pixels)
288
  return masked_collection
289
 
290
+ # Worker function for processing a single geometry
291
+ 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):
292
+ if shape_type.lower() == "point":
293
+ latitude = row.get('latitude')
294
+ longitude = row.get('longitude')
295
+ if pd.isna(latitude) or pd.isna(longitude):
296
+ return None # Skip invalid points
297
+ location_name = row.get('name', f"Location_{row.name}")
298
+ if kernel_size == "3x3 Kernel":
299
+ buffer_size = 45 # 90m x 90m
300
+ roi = ee.Geometry.Point([longitude, latitude]).buffer(buffer_size).bounds()
301
+ elif kernel_size == "5x5 Kernel":
302
+ buffer_size = 75 # 150m x 150m
303
+ roi = ee.Geometry.Point([longitude, latitude]).buffer(buffer_size).bounds()
304
+ else: # Point
305
+ roi = ee.Geometry.Point([longitude, latitude])
306
+ elif shape_type.lower() == "polygon":
307
+ polygon_geometry = row.get('geometry')
308
+ location_name = row.get('name', f"Polygon_{row.name}")
309
+ try:
310
  roi = convert_to_ee_geometry(polygon_geometry)
311
  if not include_boundary:
312
  roi = roi.buffer(-30).bounds()
313
+ except ValueError:
314
+ return None # Skip invalid polygons
315
 
316
+ # Filter and aggregate the image collection
317
+ collection = ee.ImageCollection(dataset_id) \
318
+ .filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
319
+ .filterBounds(roi)
320
+
321
+ if aggregation_period.lower() == 'custom (start date to end date)':
322
+ collection = aggregate_data_custom(collection)
323
+ elif aggregation_period.lower() == 'daily':
324
+ collection = aggregate_data_daily(collection)
325
+ elif aggregation_period.lower() == 'weekly':
326
+ collection = aggregate_data_weekly(collection, start_date_str, end_date_str)
327
+ elif aggregation_period.lower() == 'monthly':
328
+ collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
329
+ elif aggregation_period.lower() == 'yearly':
330
+ collection = aggregate_data_yearly(collection)
331
+
332
+ # Process each image in the collection
333
+ image_list = collection.toList(collection.size())
334
+ processed_weeks = set()
335
+ aggregated_results = []
336
+ for i in range(image_list.size().getInfo()):
337
+ image = ee.Image(image_list.get(i))
338
  if aggregation_period.lower() == 'custom (start date to end date)':
339
+ timestamp = image.get('day')
340
+ period_label = 'Date'
341
+ date = ee.Date(timestamp).format('YYYY-MM-dd').getInfo()
342
  elif aggregation_period.lower() == 'daily':
343
+ timestamp = image.get('day_start')
344
+ period_label = 'Date'
345
+ date = ee.String(timestamp).getInfo()
346
  elif aggregation_period.lower() == 'weekly':
347
+ timestamp = image.get('week_start')
348
+ period_label = 'Week'
349
+ date = ee.String(timestamp).getInfo()
350
+ if (pd.to_datetime(date) < pd.to_datetime(start_date_str) or
351
+ pd.to_datetime(date) > pd.to_datetime(end_date_str) or
352
+ date in processed_weeks):
353
+ continue
354
+ processed_weeks.add(date)
355
  elif aggregation_period.lower() == 'monthly':
356
+ timestamp = image.get('month')
357
+ period_label = 'Month'
358
+ date = ee.Date(timestamp).format('YYYY-MM').getInfo()
359
  elif aggregation_period.lower() == 'yearly':
360
+ timestamp = image.get('year')
361
+ period_label = 'Year'
362
+ date = ee.Date(timestamp).format('YYYY').getInfo()
363
 
364
+ index_image = calculate_custom_formula(image, roi, selected_bands, custom_formula, reducer_choice, scale=30)
365
+ try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
  index_value = index_image.reduceRegion(
367
  reducer=get_reducer(reducer_choice),
368
  geometry=roi,
369
  scale=30
370
+ ).get('custom_result')
371
+ calculated_value = index_value.getInfo()
372
+ if isinstance(calculated_value, (int, float)):
373
  result = {
374
  'Location Name': location_name,
375
  period_label: date,
376
  'Start Date': start_date_str,
377
  'End Date': end_date_str,
378
+ 'Calculated Value': calculated_value
379
  }
380
  if shape_type.lower() == 'point':
381
+ result[original_lat_col] = latitude # Use original column name
382
+ result[original_lon_col] = longitude # Use original column name
383
  aggregated_results.append(result)
384
+ except Exception as e:
385
+ st.error(f"Error retrieving value for {location_name}: {e}")
386
+ return aggregated_results
387
+
 
 
388
 
389
+ # Main processing function
390
  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, cloud_threshold=0):
391
  aggregated_results = []
392
  total_steps = len(locations_df)
 
394
  progress_text = st.empty()
395
  start_time = time.time() # Start timing the process
396
 
397
+ # Preprocess the image collection with cloud filtering
398
+ raw_collection = ee.ImageCollection(dataset_id) \
399
+ .filterDate(ee.Date(start_date_str), ee.Date(end_date_str))
400
+
401
+ # Print the size of the original collection
402
+ st.write(f"Original Collection Size: {raw_collection.size().getInfo()}")
403
+
404
+ # Apply cloud filtering if threshold > 0
405
+ if cloud_threshold > 0:
406
+ raw_collection = preprocess_collection(raw_collection, cloud_threshold)
407
+
408
+ # Print the size of the preprocessed collection
409
+ st.write(f"Preprocessed Collection Size: {raw_collection.size().getInfo()}")
410
+
411
  with ThreadPoolExecutor(max_workers=10) as executor:
412
  futures = []
413
  for idx, row in locations_df.iterrows():
 
425
  original_lat_col,
426
  original_lon_col,
427
  kernel_size,
428
+ include_boundary
 
429
  )
430
  futures.append(future)
431
  completed = 0
 
449
  'Start Date': 'first',
450
  'End Date': 'first',
451
  'Calculated Value': 'mean'
452
+ # 'Date Range': 'first' # Include Date Range in aggregation
453
  }
454
  if shape_type.lower() == 'point':
455
  agg_dict[original_lat_col] = 'first'
 
593
  help="Tiles with cloud coverage exceeding this threshold will be excluded. Individual cloudy pixels will also be masked."
594
  )
595
 
596
+
597
  aggregation_period = st.selectbox(
598
  "Select Aggregation Period (e.g, Custom(Start Date to End Date) , Daily , Weekly , Monthly , Yearly)",
599
  ["Custom (Start Date to End Date)", "Daily", "Weekly", "Monthly", "Yearly"],
 
746
  st.write("Map of Uploaded Polygons:")
747
  m.to_streamlit()
748
 
749
+ # if st.button(f"Calculate {custom_formula}"):
750
+ # if not locations_df.empty:
751
+ # with st.spinner("Processing Data..."):
752
+ # try:
753
+ # results, processing_time = process_aggregation(
754
+ # locations_df,
755
+ # start_date_str,
756
+ # end_date_str,
757
+ # dataset_id,
758
+ # selected_bands,
759
+ # reducer_choice,
760
+ # shape_type,
761
+ # aggregation_period,
762
+ # original_lat_col,
763
+ # original_lon_col,
764
+ # custom_formula,
765
+ # kernel_size,
766
+ # include_boundary
767
+ # )
768
+ # if results:
769
+ # result_df = pd.DataFrame(results)
770
+ # st.write(f"Processed Results Table ({aggregation_period}) for Formula: {custom_formula}")
771
+ # st.dataframe(result_df)
772
+ # filename = f"{main_selection}_{dataset_id}_{start_date.strftime('%Y%m%d')}_{end_date.strftime('%Y%m%d')}_{aggregation_period.lower()}.csv"
773
+ # st.download_button(
774
+ # label="Download results as CSV",
775
+ # data=result_df.to_csv(index=False).encode('utf-8'),
776
+ # file_name=filename,
777
+ # mime='text/csv'
778
+ # )
779
+ # st.success(f"Processing complete! Total processing time: {processing_time:.2f} seconds.")
780
+ # else:
781
+ # st.warning("No results were generated. Check your inputs or formula.")
782
+ # st.info(f"Total processing time: {processing_time:.2f} seconds.")
783
+ # except Exception as e:
784
+ # st.error(f"An error occurred during processing: {str(e)}")
785
+ # else:
786
+ # st.warning("Please upload a valid file to proceed.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
787
 
788
  if st.button(f"Calculate {custom_formula}"):
789
  if not locations_df.empty: