Spaces:
Running
Running
update app.py
Browse files
app.py
CHANGED
@@ -73,22 +73,50 @@ elif imagery_base == "MODIS":
|
|
73 |
with open(dataset_file) as f:
|
74 |
data = json.load(f)
|
75 |
elif imagery_base == "Custom Input":
|
76 |
-
custom_dataset_id = st.text_input(
|
|
|
|
|
|
|
77 |
if custom_dataset_id:
|
78 |
try:
|
79 |
-
#
|
80 |
-
if custom_dataset_id.startswith("ee.
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
}
|
90 |
-
|
91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
except Exception as e:
|
93 |
st.error(f"Error fetching dataset: {str(e)}. Please check the dataset ID and ensure it's valid in Google Earth Engine.")
|
94 |
data = {}
|
@@ -157,17 +185,7 @@ if main_selection and sub_selection:
|
|
157 |
help=f"Use only these bands: {', '.join(selected_bands)}. Examples: {example}"
|
158 |
)
|
159 |
|
160 |
-
# Validate the formula
|
161 |
-
def validate_formula(formula, selected_bands):
|
162 |
-
allowed_chars = set(" +-*/()0123456789.")
|
163 |
-
terms = re.findall(r'[a-zA-Z][a-zA-Z0-9_]*', formula)
|
164 |
-
invalid_terms = [term for term in terms if term not in selected_bands]
|
165 |
-
if invalid_terms:
|
166 |
-
return False, f"Invalid terms in formula: {', '.join(invalid_terms)}. Use only {', '.join(selected_bands)}."
|
167 |
-
if not all(char in allowed_chars or char in ''.join(selected_bands) for char in formula):
|
168 |
-
return False, "Formula contains invalid characters. Use only bands, numbers, and operators (+, -, *, /, ())"
|
169 |
-
return True, ""
|
170 |
-
|
171 |
is_valid, error_message = validate_formula(custom_formula, selected_bands)
|
172 |
if not is_valid:
|
173 |
st.error(error_message)
|
@@ -176,8 +194,9 @@ if main_selection and sub_selection:
|
|
176 |
st.warning("Please enter a custom formula to proceed.")
|
177 |
st.stop()
|
178 |
|
179 |
-
# Display the validated formula
|
180 |
st.write(f"Custom Formula: {custom_formula}")
|
|
|
|
|
181 |
|
182 |
# The rest of your code (reducer, geometry conversion, date input, aggregation, etc.) remains unchanged...
|
183 |
|
@@ -494,6 +513,21 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
|
|
494 |
if not custom_formula:
|
495 |
st.error("Custom formula cannot be empty. Please provide a formula.")
|
496 |
return aggregated_results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
497 |
|
498 |
total_steps = len(locations_df)
|
499 |
progress_bar = st.progress(0)
|
@@ -522,7 +556,36 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
|
|
522 |
collection = ee.ImageCollection(dataset_id) \
|
523 |
.filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
|
524 |
.filterBounds(roi)
|
525 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
526 |
if aggregation_period.lower() == 'custom (start date to end date)':
|
527 |
collection = aggregate_data_custom(collection)
|
528 |
elif aggregation_period.lower() == 'weekly':
|
@@ -602,6 +665,17 @@ def process_aggregation(locations_df, start_date_str, end_date_str, dataset_id,
|
|
602 |
except ValueError as e:
|
603 |
st.warning(f"Skipping invalid polygon {polygon_name}: {e}")
|
604 |
continue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
605 |
|
606 |
collection = ee.ImageCollection(dataset_id) \
|
607 |
.filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
|
|
|
73 |
with open(dataset_file) as f:
|
74 |
data = json.load(f)
|
75 |
elif imagery_base == "Custom Input":
|
76 |
+
custom_dataset_id = st.text_input(
|
77 |
+
"Enter Custom Earth Engine Dataset ID (e.g., ee.Image('ACA/reef_habitat/v2_0') or ee.ImageCollection('AHN/AHN4'))",
|
78 |
+
value=""
|
79 |
+
)
|
80 |
if custom_dataset_id:
|
81 |
try:
|
82 |
+
# Clean up the input by removing "ee.Image(" or "ee.ImageCollection(" prefixes
|
83 |
+
if custom_dataset_id.startswith("ee.Image("):
|
84 |
+
dataset_id = custom_dataset_id.replace("ee.Image('", "").replace("')", "")
|
85 |
+
is_image = True
|
86 |
+
elif custom_dataset_id.startswith("ee.ImageCollection("):
|
87 |
+
dataset_id = custom_dataset_id.replace("ee.ImageCollection('", "").replace("')", "")
|
88 |
+
is_image = False
|
89 |
+
else:
|
90 |
+
dataset_id = custom_dataset_id
|
91 |
+
# Attempt to infer type by trying ee.Image first, then ee.ImageCollection
|
92 |
+
try:
|
93 |
+
ee.Image(dataset_id).getInfo()
|
94 |
+
is_image = True
|
95 |
+
except:
|
96 |
+
is_image = False
|
97 |
+
|
98 |
+
# Fetch dataset info from GEE based on type
|
99 |
+
if is_image:
|
100 |
+
image = ee.Image(dataset_id)
|
101 |
+
band_names = image.bandNames().getInfo()
|
102 |
+
data = {
|
103 |
+
f"Custom Image: {dataset_id}": {
|
104 |
+
"sub_options": {dataset_id: f"Custom Image ({dataset_id})"},
|
105 |
+
"bands": {dataset_id: band_names}
|
106 |
+
}
|
107 |
}
|
108 |
+
st.write(f"Fetched bands for {dataset_id} (Image): {', '.join(band_names)}")
|
109 |
+
else:
|
110 |
+
collection = ee.ImageCollection(dataset_id)
|
111 |
+
band_names = collection.first().bandNames().getInfo()
|
112 |
+
data = {
|
113 |
+
f"Custom ImageCollection: {dataset_id}": {
|
114 |
+
"sub_options": {dataset_id: f"Custom ImageCollection ({dataset_id})"},
|
115 |
+
"bands": {dataset_id: band_names}
|
116 |
+
}
|
117 |
+
}
|
118 |
+
st.write(f"Fetched bands for {dataset_id} (ImageCollection): {', '.join(band_names)}")
|
119 |
+
|
120 |
except Exception as e:
|
121 |
st.error(f"Error fetching dataset: {str(e)}. Please check the dataset ID and ensure it's valid in Google Earth Engine.")
|
122 |
data = {}
|
|
|
185 |
help=f"Use only these bands: {', '.join(selected_bands)}. Examples: {example}"
|
186 |
)
|
187 |
|
188 |
+
# Validate the formula (existing validate_formula function remains unchanged)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
189 |
is_valid, error_message = validate_formula(custom_formula, selected_bands)
|
190 |
if not is_valid:
|
191 |
st.error(error_message)
|
|
|
194 |
st.warning("Please enter a custom formula to proceed.")
|
195 |
st.stop()
|
196 |
|
|
|
197 |
st.write(f"Custom Formula: {custom_formula}")
|
198 |
+
|
199 |
+
# Rest of your code (reducers, geometry conversion, aggregation, etc.) remains unchanged...
|
200 |
|
201 |
# The rest of your code (reducer, geometry conversion, date input, aggregation, etc.) remains unchanged...
|
202 |
|
|
|
513 |
if not custom_formula:
|
514 |
st.error("Custom formula cannot be empty. Please provide a formula.")
|
515 |
return aggregated_results
|
516 |
+
|
517 |
+
# Check if dataset_id is an ee.Image or ee.ImageCollection
|
518 |
+
try:
|
519 |
+
dataset = ee.Image(dataset_id)
|
520 |
+
is_image = True
|
521 |
+
# Try to get the nominal scale from the dataset (default to 30m if unavailable)
|
522 |
+
try:
|
523 |
+
scale = dataset.projection().nominalScale().getInfo()
|
524 |
+
except:
|
525 |
+
scale = 30
|
526 |
+
st.warning(f"Could not determine nominal scale for {dataset_id}. Using default scale of 30m.")
|
527 |
+
except:
|
528 |
+
dataset = ee.ImageCollection(dataset_id)
|
529 |
+
is_image = False
|
530 |
+
scale = 30
|
531 |
|
532 |
total_steps = len(locations_df)
|
533 |
progress_bar = st.progress(0)
|
|
|
556 |
collection = ee.ImageCollection(dataset_id) \
|
557 |
.filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
|
558 |
.filterBounds(roi)
|
559 |
+
|
560 |
+
if is_image:
|
561 |
+
# Handle any ee.Image dataset
|
562 |
+
image = dataset
|
563 |
+
index_image = calculate_custom_formula(image, roi, selected_bands, custom_formula, reducer_choice, scale=scale)
|
564 |
+
index_value = index_image.reduceRegion(
|
565 |
+
reducer=get_reducer(reducer_choice),
|
566 |
+
geometry=roi,
|
567 |
+
scale=scale # Use the dataset's native scale or default
|
568 |
+
).get('custom_result')
|
569 |
+
calculated_value = index_value.getInfo()
|
570 |
+
|
571 |
+
if isinstance(calculated_value, (int, float)):
|
572 |
+
aggregated_results.append({
|
573 |
+
'Location Name': location_name,
|
574 |
+
'Latitude': latitude,
|
575 |
+
'Longitude': longitude,
|
576 |
+
'Date': 'Static', # Static image has no time dimension
|
577 |
+
'Start Date': start_date_str,
|
578 |
+
'End Date': end_date_str,
|
579 |
+
'Calculated Value': calculated_value
|
580 |
+
})
|
581 |
+
else:
|
582 |
+
st.warning(f"Skipping invalid value for {location_name}")
|
583 |
+
else:
|
584 |
+
# Handle ee.ImageCollection (existing logic)
|
585 |
+
collection = dataset \
|
586 |
+
.filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
|
587 |
+
.filterBounds(roi)
|
588 |
+
|
589 |
if aggregation_period.lower() == 'custom (start date to end date)':
|
590 |
collection = aggregate_data_custom(collection)
|
591 |
elif aggregation_period.lower() == 'weekly':
|
|
|
665 |
except ValueError as e:
|
666 |
st.warning(f"Skipping invalid polygon {polygon_name}: {e}")
|
667 |
continue
|
668 |
+
|
669 |
+
if is_image:
|
670 |
+
# Handle any ee.Image dataset
|
671 |
+
image = dataset
|
672 |
+
index_image = calculate_custom_formula(image, roi, selected_bands, custom_formula, reducer_choice, scale=scale)
|
673 |
+
index_value = index_image.reduceRegion(
|
674 |
+
reducer=get_reducer(reducer_choice),
|
675 |
+
geometry=roi,
|
676 |
+
scale=scale
|
677 |
+
).get('custom_result')
|
678 |
+
calculated_value = index_value.getInfo()
|
679 |
|
680 |
collection = ee.ImageCollection(dataset_id) \
|
681 |
.filterDate(ee.Date(start_date_str), ee.Date(end_date_str)) \
|