Spaces:
Running
Running
update app.py
Browse files
app.py
CHANGED
@@ -106,34 +106,59 @@ def convert_to_ee_geometry(geometry):
|
|
106 |
else:
|
107 |
raise ValueError("Unsupported geometry input type. Supported types are Shapely, GeoJSON, and KML.")
|
108 |
|
109 |
-
# Function to calculate custom formula
|
110 |
def calculate_custom_formula(image, geometry, selected_bands, custom_formula, reducer_choice, dataset_id, user_scale=None):
|
111 |
try:
|
112 |
-
#
|
113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
scale = user_scale if user_scale is not None else default_scale
|
115 |
-
|
116 |
-
|
|
|
117 |
for band in selected_bands:
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
reduced_values = {}
|
|
|
123 |
for band in selected_bands:
|
124 |
-
value =
|
125 |
reducer=reducer,
|
126 |
geometry=geometry,
|
127 |
-
scale=scale
|
128 |
).get(band).getInfo()
|
129 |
reduced_values[band] = float(value if value is not None else 0)
|
|
|
|
|
130 |
formula = custom_formula
|
131 |
for band in selected_bands:
|
132 |
formula = formula.replace(band, str(reduced_values[band]))
|
133 |
result = eval(formula, {"__builtins__": {}}, reduced_values)
|
|
|
|
|
134 |
if not isinstance(result, (int, float)):
|
135 |
raise ValueError("Formula did not result in a numeric value.")
|
|
|
136 |
return ee.Image.constant(result).rename('custom_result')
|
|
|
137 |
except ZeroDivisionError:
|
138 |
st.error("Error: Division by zero in the formula.")
|
139 |
return ee.Image(0).rename('custom_result').set('error', 'Division by zero')
|
@@ -438,7 +463,6 @@ elif imagery_base == "Custom Input":
|
|
438 |
if not data:
|
439 |
st.error("No valid dataset available. Please check your inputs.")
|
440 |
st.stop()
|
441 |
-
|
442 |
st.markdown("<hr><h5><b>{}</b></h5>".format(imagery_base), unsafe_allow_html=True)
|
443 |
main_selection = st.selectbox(f"Select {imagery_base} Dataset Category", list(data.keys()))
|
444 |
sub_selection = None
|
@@ -450,7 +474,6 @@ if main_selection:
|
|
450 |
st.write(f"You selected: {main_selection} -> {sub_options[sub_selection]}")
|
451 |
st.write(f"Dataset ID: {sub_selection}")
|
452 |
dataset_id = sub_selection
|
453 |
-
|
454 |
st.markdown("<hr><h5><b>Earth Engine Index Calculator</b></h5>", unsafe_allow_html=True)
|
455 |
if main_selection and sub_selection:
|
456 |
dataset_bands = data[main_selection]["bands"].get(sub_selection, [])
|
@@ -493,7 +516,6 @@ if main_selection and sub_selection:
|
|
493 |
st.warning("Please enter a custom formula to proceed.")
|
494 |
st.stop()
|
495 |
st.write(f"Custom Formula: {custom_formula}")
|
496 |
-
|
497 |
reducer_choice = st.selectbox(
|
498 |
"Select Reducer (e.g, mean , sum , median , min , max , count)",
|
499 |
['mean', 'sum', 'median', 'min', 'max', 'count'],
|
@@ -503,7 +525,6 @@ start_date = st.date_input("Start Date", value=pd.to_datetime('2024-11-01'))
|
|
503 |
end_date = st.date_input("End Date", value=pd.to_datetime('2024-12-01'))
|
504 |
start_date_str = start_date.strftime('%Y-%m-%d')
|
505 |
end_date_str = end_date.strftime('%Y-%m-%d')
|
506 |
-
|
507 |
if imagery_base == "Sentinel" and "Sentinel-2" in sub_options[sub_selection]:
|
508 |
st.markdown("<h5>Cloud Filtering</h5>", unsafe_allow_html=True)
|
509 |
tile_cloud_threshold = st.slider(
|
@@ -522,7 +543,6 @@ if imagery_base == "Sentinel" and "Sentinel-2" in sub_options[sub_selection]:
|
|
522 |
step=5,
|
523 |
help="Individual pixels with cloud coverage exceeding this threshold will be masked."
|
524 |
)
|
525 |
-
|
526 |
aggregation_period = st.selectbox(
|
527 |
"Select Aggregation Period (e.g, Custom(Start Date to End Date) , Daily , Weekly , Monthly , Yearly)",
|
528 |
["Custom (Start Date to End Date)", "Daily", "Weekly", "Monthly", "Yearly"],
|
@@ -544,7 +564,6 @@ elif shape_type.lower() == "polygon":
|
|
544 |
value=True,
|
545 |
help="Check to include pixels on the polygon boundary; uncheck to exclude them."
|
546 |
)
|
547 |
-
|
548 |
st.markdown("<h5>Calculation Scale</h5>", unsafe_allow_html=True)
|
549 |
default_scale = ee.ImageCollection(dataset_id).first().select(0).projection().nominalScale().getInfo()
|
550 |
user_scale = st.number_input(
|
@@ -553,7 +572,6 @@ user_scale = st.number_input(
|
|
553 |
value=float(default_scale),
|
554 |
help=f"Default scale for this dataset is {default_scale} meters. Adjust if needed."
|
555 |
)
|
556 |
-
|
557 |
file_upload = st.file_uploader(f"Upload your {shape_type} data (CSV, GeoJSON, KML)", type=["csv", "geojson", "kml"])
|
558 |
locations_df = pd.DataFrame()
|
559 |
original_lat_col = None
|
@@ -672,7 +690,6 @@ if file_upload is not None:
|
|
672 |
m.add_gdf(gdf=gdf, layer_name=row.get('name', 'Unnamed Polygon'))
|
673 |
st.write("Map of Uploaded Polygons:")
|
674 |
m.to_streamlit()
|
675 |
-
|
676 |
if st.button(f"Calculate {custom_formula}"):
|
677 |
if not locations_df.empty:
|
678 |
with st.spinner("Processing Data..."):
|
|
|
106 |
else:
|
107 |
raise ValueError("Unsupported geometry input type. Supported types are Shapely, GeoJSON, and KML.")
|
108 |
|
109 |
+
# Function to calculate custom formula with dynamic scale handling and normalization
|
110 |
def calculate_custom_formula(image, geometry, selected_bands, custom_formula, reducer_choice, dataset_id, user_scale=None):
|
111 |
try:
|
112 |
+
# Fetch the nominal scales of the selected bands
|
113 |
+
band_scales = []
|
114 |
+
for band in selected_bands:
|
115 |
+
band_scale = image.select(band).projection().nominalScale().getInfo()
|
116 |
+
band_scales.append(band_scale)
|
117 |
+
|
118 |
+
# Determine the finest (smallest) scale among the selected bands
|
119 |
+
default_scale = min(band_scales) if band_scales else 30 # Default to 30m if no bands are found
|
120 |
+
|
121 |
+
# Use user-defined scale if provided, otherwise use the finest scale
|
122 |
scale = user_scale if user_scale is not None else default_scale
|
123 |
+
|
124 |
+
# Rescale all bands to the chosen scale
|
125 |
+
rescaled_bands = {}
|
126 |
for band in selected_bands:
|
127 |
+
band_image = image.select(band)
|
128 |
+
band_scale = band_image.projection().nominalScale().getInfo()
|
129 |
+
if band_scale != scale:
|
130 |
+
# Resample the band to match the target scale
|
131 |
+
rescaled_band = band_image.resample('bilinear').reproject(
|
132 |
+
crs=band_image.projection().crs(),
|
133 |
+
scale=scale
|
134 |
+
)
|
135 |
+
rescaled_bands[band] = rescaled_band
|
136 |
+
else:
|
137 |
+
rescaled_bands[band] = band_image
|
138 |
+
|
139 |
+
# Validate and extract band values
|
140 |
reduced_values = {}
|
141 |
+
reducer = get_reducer(reducer_choice)
|
142 |
for band in selected_bands:
|
143 |
+
value = rescaled_bands[band].reduceRegion(
|
144 |
reducer=reducer,
|
145 |
geometry=geometry,
|
146 |
+
scale=scale # Use the determined scale here
|
147 |
).get(band).getInfo()
|
148 |
reduced_values[band] = float(value if value is not None else 0)
|
149 |
+
|
150 |
+
# Evaluate the custom formula
|
151 |
formula = custom_formula
|
152 |
for band in selected_bands:
|
153 |
formula = formula.replace(band, str(reduced_values[band]))
|
154 |
result = eval(formula, {"__builtins__": {}}, reduced_values)
|
155 |
+
|
156 |
+
# Validate the result
|
157 |
if not isinstance(result, (int, float)):
|
158 |
raise ValueError("Formula did not result in a numeric value.")
|
159 |
+
|
160 |
return ee.Image.constant(result).rename('custom_result')
|
161 |
+
|
162 |
except ZeroDivisionError:
|
163 |
st.error("Error: Division by zero in the formula.")
|
164 |
return ee.Image(0).rename('custom_result').set('error', 'Division by zero')
|
|
|
463 |
if not data:
|
464 |
st.error("No valid dataset available. Please check your inputs.")
|
465 |
st.stop()
|
|
|
466 |
st.markdown("<hr><h5><b>{}</b></h5>".format(imagery_base), unsafe_allow_html=True)
|
467 |
main_selection = st.selectbox(f"Select {imagery_base} Dataset Category", list(data.keys()))
|
468 |
sub_selection = None
|
|
|
474 |
st.write(f"You selected: {main_selection} -> {sub_options[sub_selection]}")
|
475 |
st.write(f"Dataset ID: {sub_selection}")
|
476 |
dataset_id = sub_selection
|
|
|
477 |
st.markdown("<hr><h5><b>Earth Engine Index Calculator</b></h5>", unsafe_allow_html=True)
|
478 |
if main_selection and sub_selection:
|
479 |
dataset_bands = data[main_selection]["bands"].get(sub_selection, [])
|
|
|
516 |
st.warning("Please enter a custom formula to proceed.")
|
517 |
st.stop()
|
518 |
st.write(f"Custom Formula: {custom_formula}")
|
|
|
519 |
reducer_choice = st.selectbox(
|
520 |
"Select Reducer (e.g, mean , sum , median , min , max , count)",
|
521 |
['mean', 'sum', 'median', 'min', 'max', 'count'],
|
|
|
525 |
end_date = st.date_input("End Date", value=pd.to_datetime('2024-12-01'))
|
526 |
start_date_str = start_date.strftime('%Y-%m-%d')
|
527 |
end_date_str = end_date.strftime('%Y-%m-%d')
|
|
|
528 |
if imagery_base == "Sentinel" and "Sentinel-2" in sub_options[sub_selection]:
|
529 |
st.markdown("<h5>Cloud Filtering</h5>", unsafe_allow_html=True)
|
530 |
tile_cloud_threshold = st.slider(
|
|
|
543 |
step=5,
|
544 |
help="Individual pixels with cloud coverage exceeding this threshold will be masked."
|
545 |
)
|
|
|
546 |
aggregation_period = st.selectbox(
|
547 |
"Select Aggregation Period (e.g, Custom(Start Date to End Date) , Daily , Weekly , Monthly , Yearly)",
|
548 |
["Custom (Start Date to End Date)", "Daily", "Weekly", "Monthly", "Yearly"],
|
|
|
564 |
value=True,
|
565 |
help="Check to include pixels on the polygon boundary; uncheck to exclude them."
|
566 |
)
|
|
|
567 |
st.markdown("<h5>Calculation Scale</h5>", unsafe_allow_html=True)
|
568 |
default_scale = ee.ImageCollection(dataset_id).first().select(0).projection().nominalScale().getInfo()
|
569 |
user_scale = st.number_input(
|
|
|
572 |
value=float(default_scale),
|
573 |
help=f"Default scale for this dataset is {default_scale} meters. Adjust if needed."
|
574 |
)
|
|
|
575 |
file_upload = st.file_uploader(f"Upload your {shape_type} data (CSV, GeoJSON, KML)", type=["csv", "geojson", "kml"])
|
576 |
locations_df = pd.DataFrame()
|
577 |
original_lat_col = None
|
|
|
690 |
m.add_gdf(gdf=gdf, layer_name=row.get('name', 'Unnamed Polygon'))
|
691 |
st.write("Map of Uploaded Polygons:")
|
692 |
m.to_streamlit()
|
|
|
693 |
if st.button(f"Calculate {custom_formula}"):
|
694 |
if not locations_df.empty:
|
695 |
with st.spinner("Processing Data..."):
|