Update app.py
Browse files
app.py
CHANGED
|
@@ -205,57 +205,59 @@ def get_wayback_data():
|
|
| 205 |
print(f"Could not fetch or parse Wayback data: {e}")
|
| 206 |
return pd.DataFrame() # Return empty dataframe on failure
|
| 207 |
|
| 208 |
-
def get_dem_slope_maps(ee_geometry, wayback_url=None, wayback_title=None):
|
| 209 |
"""Creates DEM and Slope maps from SRTM data, using wayback tiles as a basemap if available."""
|
| 210 |
one_time_setup()
|
| 211 |
|
| 212 |
# --- DEM Map ---
|
| 213 |
-
dem_map = gee_folium.Map(
|
| 214 |
if wayback_url:
|
| 215 |
dem_map.add_tile_layer(wayback_url, name=wayback_title, attribution="Esri")
|
| 216 |
else:
|
| 217 |
dem_map.add_basemap("SATELLITE")
|
| 218 |
|
|
|
|
| 219 |
try:
|
| 220 |
dem_layer = ee.Image("USGS/SRTMGL1_003").resample("bilinear").reproject(crs="EPSG:4326", scale=30).clip(ee_geometry)
|
| 221 |
stats = dem_layer.reduceRegion(reducer=ee.Reducer.minMax(), geometry=ee_geometry, scale=30, maxPixels=1e9).getInfo()
|
| 222 |
|
| 223 |
if stats and stats.get('elevation_min') is not None:
|
| 224 |
min_val, max_val = stats['elevation_min'], stats['elevation_max']
|
| 225 |
-
vis_params = {"min": min_val, "max": max_val, "palette": ['#0000FF', '#00FF00', '#FFFF00', '#FF0000']}
|
| 226 |
dem_map.addLayer(dem_layer, vis_params, "Elevation")
|
| 227 |
dem_map.add_colorbar(vis_params=vis_params, label="Elevation (m)")
|
| 228 |
-
|
| 229 |
-
|
|
|
|
|
|
|
| 230 |
except Exception as e:
|
| 231 |
print(f"Error creating DEM map: {e}")
|
| 232 |
dem_map_html = f"<div>Error creating DEM map: {e}</div>"
|
| 233 |
|
| 234 |
-
dem_map.addLayerControl()
|
| 235 |
-
dem_map_html = dem_map._repr_html_()
|
| 236 |
|
| 237 |
# --- Slope Map ---
|
| 238 |
-
slope_map = gee_folium.Map(
|
| 239 |
if wayback_url:
|
| 240 |
slope_map.add_tile_layer(wayback_url, name=wayback_title, attribution="Esri")
|
| 241 |
else:
|
| 242 |
slope_map.add_basemap("SATELLITE")
|
| 243 |
|
|
|
|
| 244 |
try:
|
| 245 |
slope_layer = ee.Terrain.slope(dem_layer)
|
| 246 |
-
slope_vis_params = {"min": 0, "max": 60, "palette": ['#00FF00', '#FFFF00', '#FFA500', '#FF0000']}
|
| 247 |
slope_map.addLayer(slope_layer, slope_vis_params, "Slope")
|
| 248 |
slope_map.add_colorbar(vis_params=slope_vis_params, label="Slope (degrees)")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
except Exception as e:
|
| 250 |
print(f"Error creating Slope map: {e}")
|
| 251 |
slope_map_html = f"<div>Error creating Slope map: {e}</div>"
|
| 252 |
|
| 253 |
-
slope_map.addLayerControl()
|
| 254 |
-
slope_map_html = slope_map._repr_html_()
|
| 255 |
-
|
| 256 |
return dem_map_html, slope_map_html
|
| 257 |
|
| 258 |
-
|
| 259 |
def add_indices(image, nir_band, red_band, blue_band, green_band, evi_vars):
|
| 260 |
"""Calculates and adds multiple vegetation indices to an Earth Engine image."""
|
| 261 |
# It's safer to work with the image bands directly
|
|
@@ -331,25 +333,19 @@ def process_and_display(file_obj, url_str, buffer_m, progress=gr.Progress()):
|
|
| 331 |
|
| 332 |
progress(0, desc="Reading and processing geometry...")
|
| 333 |
try:
|
|
|
|
| 334 |
input_gdf = get_gdf_from_file(file_obj) if file_obj is not None else get_gdf_from_url(url_str)
|
| 335 |
input_gdf = preprocess_gdf(input_gdf)
|
| 336 |
-
|
| 337 |
-
# Find the first valid polygon
|
| 338 |
geometry_gdf = next((input_gdf.iloc[[i]] for i in range(len(input_gdf)) if is_valid_polygon(input_gdf.iloc[[i]])), None)
|
| 339 |
-
|
| 340 |
if geometry_gdf is None:
|
| 341 |
return None, "No valid polygon found in the provided file.", None, None, None, None, None
|
| 342 |
-
|
| 343 |
geometry_gdf = to_best_crs(geometry_gdf)
|
| 344 |
-
|
| 345 |
-
# Create buffer
|
| 346 |
outer_geometry_gdf = geometry_gdf.copy()
|
| 347 |
outer_geometry_gdf["geometry"] = outer_geometry_gdf["geometry"].buffer(buffer_m)
|
| 348 |
buffer_geometry_gdf = gpd.GeoDataFrame(
|
| 349 |
geometry=[outer_geometry_gdf.unary_union.difference(geometry_gdf.unary_union)],
|
| 350 |
crs=geometry_gdf.crs
|
| 351 |
)
|
| 352 |
-
|
| 353 |
except Exception as e:
|
| 354 |
return None, f"Error processing file: {e}", None, None, None, None, None
|
| 355 |
|
|
@@ -368,13 +364,15 @@ def process_and_display(file_obj, url_str, buffer_m, progress=gr.Progress()):
|
|
| 368 |
.replace("{TileCol}", "{x}")
|
| 369 |
)
|
| 370 |
|
| 371 |
-
#
|
| 372 |
-
ee_geometry = ee.Geometry(json.loads(geometry_gdf.to_crs(4326).to_json())['features'][0]['geometry'])
|
| 373 |
-
dem_html, slope_html = get_dem_slope_maps(ee_geometry, wayback_url, wayback_title)
|
| 374 |
-
|
| 375 |
-
# Create main map with folium
|
| 376 |
bounds = geometry_gdf.to_crs(epsg=4326).total_bounds
|
| 377 |
map_center = [(bounds[1] + bounds[3]) / 2, (bounds[0] + bounds[2]) / 2]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
m = folium.Map(location=map_center)
|
| 379 |
|
| 380 |
if wayback_url:
|
|
@@ -384,17 +382,14 @@ def process_and_display(file_obj, url_str, buffer_m, progress=gr.Progress()):
|
|
| 384 |
m.add_child(folium.LayerControl())
|
| 385 |
m.fit_bounds([[bounds[1], bounds[0]], [bounds[3], bounds[2]]])
|
| 386 |
|
| 387 |
-
#
|
| 388 |
stats_df = pd.DataFrame({
|
| 389 |
"Area (ha)": [f"{geometry_gdf.area.item() / 10000:.2f}"],
|
| 390 |
"Perimeter (m)": [f"{geometry_gdf.length.item():.2f}"],
|
| 391 |
"Centroid (Lat, Lon)": [f"({geometry_gdf.to_crs(4326).centroid.y.iloc[0]:.6f}, {geometry_gdf.to_crs(4326).centroid.x.iloc[0]:.6f})"]
|
| 392 |
})
|
| 393 |
-
|
| 394 |
-
# Save geometry data for later use
|
| 395 |
geometry_json = geometry_gdf.to_json()
|
| 396 |
buffer_geometry_json = buffer_geometry_gdf.to_json()
|
| 397 |
-
|
| 398 |
progress(1, desc="Done!")
|
| 399 |
return m._repr_html_(), None, stats_df, dem_html, slope_html, geometry_json, buffer_geometry_json
|
| 400 |
|
|
|
|
| 205 |
print(f"Could not fetch or parse Wayback data: {e}")
|
| 206 |
return pd.DataFrame() # Return empty dataframe on failure
|
| 207 |
|
| 208 |
+
def get_dem_slope_maps(ee_geometry, map_center, zoom=12, wayback_url=None, wayback_title=None):
|
| 209 |
"""Creates DEM and Slope maps from SRTM data, using wayback tiles as a basemap if available."""
|
| 210 |
one_time_setup()
|
| 211 |
|
| 212 |
# --- DEM Map ---
|
| 213 |
+
dem_map = gee_folium.Map(location=map_center, zoom_start=zoom)
|
| 214 |
if wayback_url:
|
| 215 |
dem_map.add_tile_layer(wayback_url, name=wayback_title, attribution="Esri")
|
| 216 |
else:
|
| 217 |
dem_map.add_basemap("SATELLITE")
|
| 218 |
|
| 219 |
+
dem_map_html = "<div>No DEM data available for this area.</div>"
|
| 220 |
try:
|
| 221 |
dem_layer = ee.Image("USGS/SRTMGL1_003").resample("bilinear").reproject(crs="EPSG:4326", scale=30).clip(ee_geometry)
|
| 222 |
stats = dem_layer.reduceRegion(reducer=ee.Reducer.minMax(), geometry=ee_geometry, scale=30, maxPixels=1e9).getInfo()
|
| 223 |
|
| 224 |
if stats and stats.get('elevation_min') is not None:
|
| 225 |
min_val, max_val = stats['elevation_min'], stats['elevation_max']
|
| 226 |
+
vis_params = {"min": min_val, "max": max_val, "palette": ['#0000FF', '#00FF00', '#FFFF00', '#FF0000']}
|
| 227 |
dem_map.addLayer(dem_layer, vis_params, "Elevation")
|
| 228 |
dem_map.add_colorbar(vis_params=vis_params, label="Elevation (m)")
|
| 229 |
+
|
| 230 |
+
dem_map.addLayerControl()
|
| 231 |
+
dem_map_html = dem_map._repr_html_()
|
| 232 |
+
|
| 233 |
except Exception as e:
|
| 234 |
print(f"Error creating DEM map: {e}")
|
| 235 |
dem_map_html = f"<div>Error creating DEM map: {e}</div>"
|
| 236 |
|
|
|
|
|
|
|
| 237 |
|
| 238 |
# --- Slope Map ---
|
| 239 |
+
slope_map = gee_folium.Map(location=map_center, zoom_start=zoom)
|
| 240 |
if wayback_url:
|
| 241 |
slope_map.add_tile_layer(wayback_url, name=wayback_title, attribution="Esri")
|
| 242 |
else:
|
| 243 |
slope_map.add_basemap("SATELLITE")
|
| 244 |
|
| 245 |
+
slope_map_html = "<div>No Slope data available for this area.</div>"
|
| 246 |
try:
|
| 247 |
slope_layer = ee.Terrain.slope(dem_layer)
|
| 248 |
+
slope_vis_params = {"min": 0, "max": 60, "palette": ['#00FF00', '#FFFF00', '#FFA500', '#FF0000']}
|
| 249 |
slope_map.addLayer(slope_layer, slope_vis_params, "Slope")
|
| 250 |
slope_map.add_colorbar(vis_params=slope_vis_params, label="Slope (degrees)")
|
| 251 |
+
|
| 252 |
+
slope_map.addLayerControl()
|
| 253 |
+
slope_map_html = slope_map._repr_html_()
|
| 254 |
+
|
| 255 |
except Exception as e:
|
| 256 |
print(f"Error creating Slope map: {e}")
|
| 257 |
slope_map_html = f"<div>Error creating Slope map: {e}</div>"
|
| 258 |
|
|
|
|
|
|
|
|
|
|
| 259 |
return dem_map_html, slope_map_html
|
| 260 |
|
|
|
|
| 261 |
def add_indices(image, nir_band, red_band, blue_band, green_band, evi_vars):
|
| 262 |
"""Calculates and adds multiple vegetation indices to an Earth Engine image."""
|
| 263 |
# It's safer to work with the image bands directly
|
|
|
|
| 333 |
|
| 334 |
progress(0, desc="Reading and processing geometry...")
|
| 335 |
try:
|
| 336 |
+
# ... (no changes to the geometry processing part) ...
|
| 337 |
input_gdf = get_gdf_from_file(file_obj) if file_obj is not None else get_gdf_from_url(url_str)
|
| 338 |
input_gdf = preprocess_gdf(input_gdf)
|
|
|
|
|
|
|
| 339 |
geometry_gdf = next((input_gdf.iloc[[i]] for i in range(len(input_gdf)) if is_valid_polygon(input_gdf.iloc[[i]])), None)
|
|
|
|
| 340 |
if geometry_gdf is None:
|
| 341 |
return None, "No valid polygon found in the provided file.", None, None, None, None, None
|
|
|
|
| 342 |
geometry_gdf = to_best_crs(geometry_gdf)
|
|
|
|
|
|
|
| 343 |
outer_geometry_gdf = geometry_gdf.copy()
|
| 344 |
outer_geometry_gdf["geometry"] = outer_geometry_gdf["geometry"].buffer(buffer_m)
|
| 345 |
buffer_geometry_gdf = gpd.GeoDataFrame(
|
| 346 |
geometry=[outer_geometry_gdf.unary_union.difference(geometry_gdf.unary_union)],
|
| 347 |
crs=geometry_gdf.crs
|
| 348 |
)
|
|
|
|
| 349 |
except Exception as e:
|
| 350 |
return None, f"Error processing file: {e}", None, None, None, None, None
|
| 351 |
|
|
|
|
| 364 |
.replace("{TileCol}", "{x}")
|
| 365 |
)
|
| 366 |
|
| 367 |
+
# **NEW**: Calculate map center from the geometry's bounds
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
bounds = geometry_gdf.to_crs(epsg=4326).total_bounds
|
| 369 |
map_center = [(bounds[1] + bounds[3]) / 2, (bounds[0] + bounds[2]) / 2]
|
| 370 |
+
|
| 371 |
+
# **MODIFIED**: Pass map_center to the function
|
| 372 |
+
ee_geometry = ee.Geometry(json.loads(geometry_gdf.to_crs(4326).to_json())['features'][0]['geometry'])
|
| 373 |
+
dem_html, slope_html = get_dem_slope_maps(ee_geometry, map_center, wayback_url=wayback_url, wayback_title=wayback_title)
|
| 374 |
+
|
| 375 |
+
# Create main map with folium using the calculated center
|
| 376 |
m = folium.Map(location=map_center)
|
| 377 |
|
| 378 |
if wayback_url:
|
|
|
|
| 382 |
m.add_child(folium.LayerControl())
|
| 383 |
m.fit_bounds([[bounds[1], bounds[0]], [bounds[3], bounds[2]]])
|
| 384 |
|
| 385 |
+
# ... (rest of the function remains the same) ...
|
| 386 |
stats_df = pd.DataFrame({
|
| 387 |
"Area (ha)": [f"{geometry_gdf.area.item() / 10000:.2f}"],
|
| 388 |
"Perimeter (m)": [f"{geometry_gdf.length.item():.2f}"],
|
| 389 |
"Centroid (Lat, Lon)": [f"({geometry_gdf.to_crs(4326).centroid.y.iloc[0]:.6f}, {geometry_gdf.to_crs(4326).centroid.x.iloc[0]:.6f})"]
|
| 390 |
})
|
|
|
|
|
|
|
| 391 |
geometry_json = geometry_gdf.to_json()
|
| 392 |
buffer_geometry_json = buffer_geometry_gdf.to_json()
|
|
|
|
| 393 |
progress(1, desc="Done!")
|
| 394 |
return m._repr_html_(), None, stats_df, dem_html, slope_html, geometry_json, buffer_geometry_json
|
| 395 |
|