Spaces:
Running
Running
update app.py
Browse files
app.py
CHANGED
@@ -12,9 +12,6 @@ from xml.etree import ElementTree as XET
|
|
12 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
13 |
import time
|
14 |
|
15 |
-
# Define default scale for calculations (in meters)
|
16 |
-
DEFAULT_SCALE = 30
|
17 |
-
|
18 |
# Set up the page layout
|
19 |
st.set_page_config(layout="wide")
|
20 |
|
@@ -108,7 +105,7 @@ def convert_to_ee_geometry(geometry):
|
|
108 |
raise ValueError("Unsupported geometry input type. Supported types are Shapely, GeoJSON, and KML.")
|
109 |
|
110 |
# Function to calculate custom formula
|
111 |
-
def calculate_custom_formula(image, geometry, selected_bands, custom_formula, reducer_choice, scale=
|
112 |
try:
|
113 |
band_values = {}
|
114 |
band_names = image.bandNames().getInfo()
|
@@ -145,7 +142,8 @@ def calculate_custom_formula(image, geometry, selected_bands, custom_formula, re
|
|
145 |
st.error(f"Unexpected error: {e}")
|
146 |
return ee.Image(0).rename('custom_result').set('error', str(e))
|
147 |
|
148 |
-
# Aggregation functions
|
|
|
149 |
def aggregate_data_custom(collection):
|
150 |
collection = collection.map(lambda image: image.set('day', ee.Date(image.get('system:time_start')).format('YYYY-MM-dd')))
|
151 |
grouped_by_day = collection.aggregate_array('day').distinct()
|
@@ -208,7 +206,7 @@ def aggregate_data_daily(collection):
|
|
208 |
return ee.ImageCollection(daily_images)
|
209 |
|
210 |
# Worker function for processing a single geometry
|
211 |
-
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):
|
212 |
if shape_type.lower() == "point":
|
213 |
latitude = row.get('latitude')
|
214 |
longitude = row.get('longitude')
|
@@ -232,11 +230,12 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
|
|
232 |
roi = roi.buffer(-30).bounds()
|
233 |
except ValueError:
|
234 |
return None # Skip invalid polygons
|
235 |
-
|
236 |
collection = ee.ImageCollection(dataset_id) \
|
237 |
.filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
|
238 |
.filterBounds(roi) \
|
239 |
-
.select(selected_bands)
|
|
|
240 |
if aggregation_period.lower() == 'custom (start date to end date)':
|
241 |
collection = aggregate_data_custom(collection)
|
242 |
elif aggregation_period.lower() == 'daily':
|
@@ -247,10 +246,11 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
|
|
247 |
collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
|
248 |
elif aggregation_period.lower() == 'yearly':
|
249 |
collection = aggregate_data_yearly(collection)
|
250 |
-
|
251 |
image_list = collection.toList(collection.size())
|
252 |
processed_days = set()
|
253 |
aggregated_results = []
|
|
|
254 |
for i in range(image_list.size().getInfo()):
|
255 |
image = ee.Image(image_list.get(i))
|
256 |
if aggregation_period.lower() == 'custom (start date to end date)':
|
@@ -273,12 +273,14 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
|
|
273 |
timestamp = image.get('year')
|
274 |
period_label = 'Year'
|
275 |
date = ee.Date(timestamp).format('YYYY').getInfo()
|
276 |
-
|
|
|
|
|
277 |
try:
|
278 |
index_value = index_image.reduceRegion(
|
279 |
reducer=get_reducer(reducer_choice),
|
280 |
geometry=roi,
|
281 |
-
scale=
|
282 |
).get('custom_result')
|
283 |
calculated_value = index_value.getInfo()
|
284 |
if isinstance(calculated_value, (int, float)):
|
@@ -295,15 +297,17 @@ def process_single_geometry(row, start_date_str, end_date_str, dataset_id, selec
|
|
295 |
aggregated_results.append(result)
|
296 |
except Exception as e:
|
297 |
st.error(f"Error retrieving value for {location_name}: {e}")
|
|
|
298 |
return aggregated_results
|
299 |
|
300 |
# Main processing function
|
301 |
-
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):
|
302 |
aggregated_results = []
|
303 |
total_steps = len(locations_df)
|
304 |
progress_bar = st.progress(0)
|
305 |
progress_text = st.empty()
|
306 |
-
start_time = time.time()
|
|
|
307 |
with ThreadPoolExecutor(max_workers=10) as executor:
|
308 |
futures = []
|
309 |
for idx, row in locations_df.iterrows():
|
@@ -319,9 +323,11 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
|
|
319 |
aggregation_period,
|
320 |
custom_formula,
|
321 |
kernel_size,
|
322 |
-
include_boundary
|
|
|
323 |
)
|
324 |
futures.append(future)
|
|
|
325 |
completed = 0
|
326 |
for future in as_completed(futures):
|
327 |
result = future.result()
|
@@ -331,9 +337,10 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
|
|
331 |
progress_percentage = completed / total_steps
|
332 |
progress_bar.progress(progress_percentage)
|
333 |
progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
|
334 |
-
|
335 |
end_time = time.time()
|
336 |
-
processing_time = end_time - start_time
|
|
|
337 |
if aggregated_results:
|
338 |
result_df = pd.DataFrame(aggregated_results)
|
339 |
if aggregation_period.lower() == 'custom (start date to end date)':
|
@@ -347,10 +354,10 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
|
|
347 |
agg_dict['Longitude'] = 'first'
|
348 |
aggregated_output = result_df.groupby('Location Name').agg(agg_dict).reset_index()
|
349 |
aggregated_output.rename(columns={'Calculated Value': 'Aggregated Value'}, inplace=True)
|
350 |
-
return aggregated_output.to_dict(orient='records'), processing_time
|
351 |
else:
|
352 |
-
return result_df.to_dict(orient='records'), processing_time
|
353 |
-
return [], processing_time
|
354 |
|
355 |
# Streamlit App Logic
|
356 |
st.markdown("<h5>Image Collection</h5>", unsafe_allow_html=True)
|
@@ -662,7 +669,8 @@ if st.button(f"Calculate {custom_formula}"):
|
|
662 |
aggregation_period,
|
663 |
custom_formula,
|
664 |
kernel_size,
|
665 |
-
include_boundary
|
|
|
666 |
)
|
667 |
# Check if results were generated
|
668 |
if results:
|
|
|
12 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
13 |
import time
|
14 |
|
|
|
|
|
|
|
15 |
# Set up the page layout
|
16 |
st.set_page_config(layout="wide")
|
17 |
|
|
|
105 |
raise ValueError("Unsupported geometry input type. Supported types are Shapely, GeoJSON, and KML.")
|
106 |
|
107 |
# Function to calculate custom formula
|
108 |
+
def calculate_custom_formula(image, geometry, selected_bands, custom_formula, reducer_choice, scale=30):
|
109 |
try:
|
110 |
band_values = {}
|
111 |
band_names = image.bandNames().getInfo()
|
|
|
142 |
st.error(f"Unexpected error: {e}")
|
143 |
return ee.Image(0).rename('custom_result').set('error', str(e))
|
144 |
|
145 |
+
# Aggregation functions (unchanged)
|
146 |
+
|
147 |
def aggregate_data_custom(collection):
|
148 |
collection = collection.map(lambda image: image.set('day', ee.Date(image.get('system:time_start')).format('YYYY-MM-dd')))
|
149 |
grouped_by_day = collection.aggregate_array('day').distinct()
|
|
|
206 |
return ee.ImageCollection(daily_images)
|
207 |
|
208 |
# Worker function for processing a single geometry
|
209 |
+
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, default_scale=30):
|
210 |
if shape_type.lower() == "point":
|
211 |
latitude = row.get('latitude')
|
212 |
longitude = row.get('longitude')
|
|
|
230 |
roi = roi.buffer(-30).bounds()
|
231 |
except ValueError:
|
232 |
return None # Skip invalid polygons
|
233 |
+
|
234 |
collection = ee.ImageCollection(dataset_id) \
|
235 |
.filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
|
236 |
.filterBounds(roi) \
|
237 |
+
.select(selected_bands)
|
238 |
+
|
239 |
if aggregation_period.lower() == 'custom (start date to end date)':
|
240 |
collection = aggregate_data_custom(collection)
|
241 |
elif aggregation_period.lower() == 'daily':
|
|
|
246 |
collection = aggregate_data_monthly(collection, start_date_str, end_date_str)
|
247 |
elif aggregation_period.lower() == 'yearly':
|
248 |
collection = aggregate_data_yearly(collection)
|
249 |
+
|
250 |
image_list = collection.toList(collection.size())
|
251 |
processed_days = set()
|
252 |
aggregated_results = []
|
253 |
+
|
254 |
for i in range(image_list.size().getInfo()):
|
255 |
image = ee.Image(image_list.get(i))
|
256 |
if aggregation_period.lower() == 'custom (start date to end date)':
|
|
|
273 |
timestamp = image.get('year')
|
274 |
period_label = 'Year'
|
275 |
date = ee.Date(timestamp).format('YYYY').getInfo()
|
276 |
+
|
277 |
+
index_image = calculate_custom_formula(image, roi, selected_bands, custom_formula, reducer_choice, scale=default_scale)
|
278 |
+
|
279 |
try:
|
280 |
index_value = index_image.reduceRegion(
|
281 |
reducer=get_reducer(reducer_choice),
|
282 |
geometry=roi,
|
283 |
+
scale=default_scale
|
284 |
).get('custom_result')
|
285 |
calculated_value = index_value.getInfo()
|
286 |
if isinstance(calculated_value, (int, float)):
|
|
|
297 |
aggregated_results.append(result)
|
298 |
except Exception as e:
|
299 |
st.error(f"Error retrieving value for {location_name}: {e}")
|
300 |
+
|
301 |
return aggregated_results
|
302 |
|
303 |
# Main processing function
|
304 |
+
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, default_scale=30):
|
305 |
aggregated_results = []
|
306 |
total_steps = len(locations_df)
|
307 |
progress_bar = st.progress(0)
|
308 |
progress_text = st.empty()
|
309 |
+
start_time = time.time()
|
310 |
+
|
311 |
with ThreadPoolExecutor(max_workers=10) as executor:
|
312 |
futures = []
|
313 |
for idx, row in locations_df.iterrows():
|
|
|
323 |
aggregation_period,
|
324 |
custom_formula,
|
325 |
kernel_size,
|
326 |
+
include_boundary,
|
327 |
+
default_scale
|
328 |
)
|
329 |
futures.append(future)
|
330 |
+
|
331 |
completed = 0
|
332 |
for future in as_completed(futures):
|
333 |
result = future.result()
|
|
|
337 |
progress_percentage = completed / total_steps
|
338 |
progress_bar.progress(progress_percentage)
|
339 |
progress_text.markdown(f"Processing: {int(progress_percentage * 100)}%")
|
340 |
+
|
341 |
end_time = time.time()
|
342 |
+
processing_time = end_time - start_time
|
343 |
+
|
344 |
if aggregated_results:
|
345 |
result_df = pd.DataFrame(aggregated_results)
|
346 |
if aggregation_period.lower() == 'custom (start date to end date)':
|
|
|
354 |
agg_dict['Longitude'] = 'first'
|
355 |
aggregated_output = result_df.groupby('Location Name').agg(agg_dict).reset_index()
|
356 |
aggregated_output.rename(columns={'Calculated Value': 'Aggregated Value'}, inplace=True)
|
357 |
+
return aggregated_output.to_dict(orient='records'), processing_time
|
358 |
else:
|
359 |
+
return result_df.to_dict(orient='records'), processing_time
|
360 |
+
return [], processing_time
|
361 |
|
362 |
# Streamlit App Logic
|
363 |
st.markdown("<h5>Image Collection</h5>", unsafe_allow_html=True)
|
|
|
669 |
aggregation_period,
|
670 |
custom_formula,
|
671 |
kernel_size,
|
672 |
+
include_boundary,
|
673 |
+
default_scale
|
674 |
)
|
675 |
# Check if results were generated
|
676 |
if results:
|