Add legend and histogram
Browse files
app.py
CHANGED
|
@@ -467,7 +467,7 @@ def get_histogram(index_name, image, geometry, bins):
|
|
| 467 |
return np.array([0] * (len(bins) - 1)), bins
|
| 468 |
|
| 469 |
def generate_comparison_maps(geometry_json, selected_index, selected_years, evi_vars, date_start_str, date_end_str, progress=gr.Progress()):
|
| 470 |
-
"""Generates side-by-side maps for a selected index and two selected years with a
|
| 471 |
if not geometry_json or not selected_index or not selected_years:
|
| 472 |
return "Please process a file and select an index and years first.", "", ""
|
| 473 |
if len(selected_years) != 2:
|
|
@@ -508,16 +508,12 @@ def generate_comparison_maps(geometry_json, selected_index, selected_years, evi_
|
|
| 508 |
print(f"Could not find a suitable Wayback basemap for {year}: {e}")
|
| 509 |
wayback_url = None
|
| 510 |
wayback_title = "Default Satellite"
|
| 511 |
-
|
| 512 |
-
|
| 513 |
collection = (
|
| 514 |
ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
|
| 515 |
.filterDate(start_date, end_date)
|
| 516 |
.filterBounds(ee_geometry)
|
| 517 |
-
.select(
|
| 518 |
-
["B2", "B3", "B4", "B8"],
|
| 519 |
-
["Blue", "Green", "Red", "NIR"]
|
| 520 |
-
)
|
| 521 |
.map(lambda img: add_indices(img, 'NIR', 'Red', 'Blue', 'Green', evi_vars))
|
| 522 |
)
|
| 523 |
|
|
@@ -526,59 +522,64 @@ def generate_comparison_maps(geometry_json, selected_index, selected_years, evi_
|
|
| 526 |
continue
|
| 527 |
|
| 528 |
mosaic = collection.qualityMosaic(selected_index)
|
| 529 |
-
|
| 530 |
-
# Create the base map
|
| 531 |
m = gee_folium.Map(zoom_start=14)
|
| 532 |
if wayback_url:
|
| 533 |
m.add_tile_layer(wayback_url, name=wayback_title, attribution="Esri")
|
| 534 |
else:
|
| 535 |
m.add_basemap("SATELLITE")
|
| 536 |
|
| 537 |
-
# --- START:
|
| 538 |
if selected_index in ["NDVI", "RandomForest", "GujVDI", "CI", "EVI", "EVI2"]:
|
| 539 |
-
# Define bins for classification
|
| 540 |
bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
|
| 541 |
-
histogram,
|
| 542 |
|
| 543 |
total_pix = np.sum(histogram)
|
| 544 |
-
|
| 545 |
if total_pix > 0:
|
| 546 |
formatted_histogram = [f"{h*100/total_pix:.2f}" for h in histogram]
|
| 547 |
-
else:
|
| 548 |
-
formatted_histogram = ["0.00"] * len(histogram)
|
| 549 |
-
|
| 550 |
-
# Add the classified legend to the map
|
| 551 |
-
m.add_legend(
|
| 552 |
-
title=f"{selected_index} Classification ({year})",
|
| 553 |
-
legend_dict={
|
| 554 |
-
"0-0.2: Open/Sparse Vegetation ({}%)".format(formatted_histogram[0]): "#FF0000",
|
| 555 |
-
"0.2-0.4: Low Vegetation ({}%)".format(formatted_histogram[1]): "#FFFF00",
|
| 556 |
-
"0.4-0.6: Moderate Vegetation ({}%)".format(formatted_histogram[2]): "#FFA500",
|
| 557 |
-
"0.6-0.8: Dense Vegetation ({}%)".format(formatted_histogram[3]): "#00FE00",
|
| 558 |
-
"0.8-1: Very Dense Vegetation ({}%)".format(formatted_histogram[4]): "#00A400",
|
| 559 |
-
},
|
| 560 |
-
position="bottomright",
|
| 561 |
-
draggable=False,
|
| 562 |
-
)
|
| 563 |
|
| 564 |
# Define visualization parameters for the classified layer
|
| 565 |
ind_vis_params = {
|
| 566 |
-
"min": 0,
|
| 567 |
-
"max": 1,
|
| 568 |
"palette": ["#FF0000", "#FFFF00", "#FFA500", "#00FE00", "#00A400"],
|
| 569 |
}
|
| 570 |
-
# Add the classified raster layer to the map
|
| 571 |
m.addLayer(mosaic.select(selected_index).clip(ee_geometry), ind_vis_params, f"{selected_index} Classified Layer ({year})")
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
"
|
| 576 |
-
"
|
|
|
|
|
|
|
| 577 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 578 |
clipped_image = mosaic.select(selected_index).clip(ee_geometry)
|
| 579 |
m.addLayer(clipped_image, vis_params, f"{selected_index} {year}")
|
| 580 |
m.add_colorbar(vis_params=vis_params, label=f"{selected_index} Value")
|
| 581 |
-
# --- END: New Legend and Layer Logic ---
|
| 582 |
|
| 583 |
folium.GeoJson(geometry_gdf, name="Geometry", style_function=lambda x: {"color": "yellow", "fillOpacity": 0, "weight": 2.5}).add_to(m)
|
| 584 |
m.fit_bounds(map_bounds, padding=(10, 10))
|
|
@@ -590,7 +591,7 @@ def generate_comparison_maps(geometry_json, selected_index, selected_years, evi_
|
|
| 590 |
|
| 591 |
return f"Comparison generated for {selected_years[0]} and {selected_years[1]}.", maps_html[0], maps_html[1]
|
| 592 |
|
| 593 |
-
# --- Gradio Interface
|
| 594 |
theme = gr.themes.Soft(primary_hue="teal", secondary_hue="orange").set(
|
| 595 |
background_fill_primary="white"
|
| 596 |
)
|
|
|
|
| 467 |
return np.array([0] * (len(bins) - 1)), bins
|
| 468 |
|
| 469 |
def generate_comparison_maps(geometry_json, selected_index, selected_years, evi_vars, date_start_str, date_end_str, progress=gr.Progress()):
|
| 470 |
+
"""Generates side-by-side maps for a selected index and two selected years with a custom HTML legend."""
|
| 471 |
if not geometry_json or not selected_index or not selected_years:
|
| 472 |
return "Please process a file and select an index and years first.", "", ""
|
| 473 |
if len(selected_years) != 2:
|
|
|
|
| 508 |
print(f"Could not find a suitable Wayback basemap for {year}: {e}")
|
| 509 |
wayback_url = None
|
| 510 |
wayback_title = "Default Satellite"
|
| 511 |
+
|
|
|
|
| 512 |
collection = (
|
| 513 |
ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
|
| 514 |
.filterDate(start_date, end_date)
|
| 515 |
.filterBounds(ee_geometry)
|
| 516 |
+
.select(["B2", "B3", "B4", "B8"], ["Blue", "Green", "Red", "NIR"])
|
|
|
|
|
|
|
|
|
|
| 517 |
.map(lambda img: add_indices(img, 'NIR', 'Red', 'Blue', 'Green', evi_vars))
|
| 518 |
)
|
| 519 |
|
|
|
|
| 522 |
continue
|
| 523 |
|
| 524 |
mosaic = collection.qualityMosaic(selected_index)
|
|
|
|
|
|
|
| 525 |
m = gee_folium.Map(zoom_start=14)
|
| 526 |
if wayback_url:
|
| 527 |
m.add_tile_layer(wayback_url, name=wayback_title, attribution="Esri")
|
| 528 |
else:
|
| 529 |
m.add_basemap("SATELLITE")
|
| 530 |
|
| 531 |
+
# --- START: MODIFIED LEGEND AND LAYER LOGIC ---
|
| 532 |
if selected_index in ["NDVI", "RandomForest", "GujVDI", "CI", "EVI", "EVI2"]:
|
|
|
|
| 533 |
bins = [0, 0.2, 0.4, 0.6, 0.8, 1]
|
| 534 |
+
histogram, _ = get_histogram(selected_index, mosaic.select(selected_index), ee_geometry, bins)
|
| 535 |
|
| 536 |
total_pix = np.sum(histogram)
|
| 537 |
+
formatted_histogram = ["0.00"] * len(histogram)
|
| 538 |
if total_pix > 0:
|
| 539 |
formatted_histogram = [f"{h*100/total_pix:.2f}" for h in histogram]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
|
| 541 |
# Define visualization parameters for the classified layer
|
| 542 |
ind_vis_params = {
|
| 543 |
+
"min": 0, "max": 1,
|
|
|
|
| 544 |
"palette": ["#FF0000", "#FFFF00", "#FFA500", "#00FE00", "#00A400"],
|
| 545 |
}
|
|
|
|
| 546 |
m.addLayer(mosaic.select(selected_index).clip(ee_geometry), ind_vis_params, f"{selected_index} Classified Layer ({year})")
|
| 547 |
+
|
| 548 |
+
legend_items = {
|
| 549 |
+
f"0-0.2: Open/Sparse Vegetation ({formatted_histogram[0]}%)": "#FF0000",
|
| 550 |
+
f"0.2-0.4: Low Vegetation ({formatted_histogram[1]}%)": "#FFFF00",
|
| 551 |
+
f"0.4-0.6: Moderate Vegetation ({formatted_histogram[2]}%)": "#FFA500",
|
| 552 |
+
f"0.6-0.8: Dense Vegetation ({formatted_histogram[3]}%)": "#00FE00",
|
| 553 |
+
f"0.8-1: Very Dense Vegetation ({formatted_histogram[4]}%)": "#00A400",
|
| 554 |
}
|
| 555 |
+
|
| 556 |
+
legend_html = f'''
|
| 557 |
+
<div style="
|
| 558 |
+
position: fixed;
|
| 559 |
+
bottom: 20px;
|
| 560 |
+
right: 10px;
|
| 561 |
+
z-index:9999;
|
| 562 |
+
font-size:14px;
|
| 563 |
+
background-color: rgba(255, 255, 255, 0.85);
|
| 564 |
+
border:2px solid grey;
|
| 565 |
+
padding: 10px;
|
| 566 |
+
border-radius: 6px;
|
| 567 |
+
">
|
| 568 |
+
<b>{selected_index} Classification ({year})</b>
|
| 569 |
+
<ul style="list-style-type: none; padding-left: 0; margin: 5px 0 0 0;">
|
| 570 |
+
'''
|
| 571 |
+
for text, color in legend_items.items():
|
| 572 |
+
legend_html += f'<li><i style="background:{color}; width:20px; height:15px; display:inline-block; margin-right:5px; vertical-align:middle; border: 1px solid black;"></i> {text}</li>'
|
| 573 |
+
legend_html += "</ul></div>"
|
| 574 |
+
|
| 575 |
+
# Add the raw HTML to the map
|
| 576 |
+
m.get_root().html.add_child(folium.Element(legend_html))
|
| 577 |
+
|
| 578 |
+
else: # Fallback for indices without a color bar
|
| 579 |
+
vis_params = {"min": 0.0, "max": 1.0, "palette": ['FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901', '66A000', '529400', '3E8601', '207401', '056201', '004C00', '023B01', '012E01', '011D01', '011301']}
|
| 580 |
clipped_image = mosaic.select(selected_index).clip(ee_geometry)
|
| 581 |
m.addLayer(clipped_image, vis_params, f"{selected_index} {year}")
|
| 582 |
m.add_colorbar(vis_params=vis_params, label=f"{selected_index} Value")
|
|
|
|
| 583 |
|
| 584 |
folium.GeoJson(geometry_gdf, name="Geometry", style_function=lambda x: {"color": "yellow", "fillOpacity": 0, "weight": 2.5}).add_to(m)
|
| 585 |
m.fit_bounds(map_bounds, padding=(10, 10))
|
|
|
|
| 591 |
|
| 592 |
return f"Comparison generated for {selected_years[0]} and {selected_years[1]}.", maps_html[0], maps_html[1]
|
| 593 |
|
| 594 |
+
# --- Gradio Interface ---
|
| 595 |
theme = gr.themes.Soft(primary_hue="teal", secondary_hue="orange").set(
|
| 596 |
background_fill_primary="white"
|
| 597 |
)
|