Update app.py
Browse files
app.py
CHANGED
|
@@ -1019,7 +1019,7 @@ EE_JSON = os.getenv("EARTHENGINE_TOKEN")
|
|
| 1019 |
if EE_JSON:
|
| 1020 |
try:
|
| 1021 |
creds = ee.ServiceAccountCredentials(
|
| 1022 |
-
email=os.getenv("SERVICE_ACCOUNT
|
| 1023 |
key_data=EE_JSON # pass raw JSON string, not dict
|
| 1024 |
)
|
| 1025 |
ee.Initialize(creds)
|
|
@@ -1042,7 +1042,176 @@ if "ee_initialized" not in st.session_state:
|
|
| 1042 |
ee.Initialize()
|
| 1043 |
st.session_state["ee_initialized"] = True
|
| 1044 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1045 |
|
|
|
|
|
|
|
| 1046 |
|
| 1047 |
|
| 1048 |
# GeoMate Ask (RAG) β simple chat with memory per site and auto-extract numeric values
|
|
|
|
| 1019 |
if EE_JSON:
|
| 1020 |
try:
|
| 1021 |
creds = ee.ServiceAccountCredentials(
|
| 1022 |
+
email=os.getenv("SERVICE_ACCOUNT"), # <-- replace with your service account email
|
| 1023 |
key_data=EE_JSON # pass raw JSON string, not dict
|
| 1024 |
)
|
| 1025 |
ee.Initialize(creds)
|
|
|
|
| 1042 |
ee.Initialize()
|
| 1043 |
st.session_state["ee_initialized"] = True
|
| 1044 |
|
| 1045 |
+
def locator_page():
|
| 1046 |
+
st.title("π GeoMate Interactive Earth Explorer")
|
| 1047 |
+
|
| 1048 |
+
# -------------------------------
|
| 1049 |
+
# Create Map
|
| 1050 |
+
# -------------------------------
|
| 1051 |
+
m = geemap.Map(center=[20, 78], zoom=3, draw_export=True)
|
| 1052 |
+
|
| 1053 |
+
# --- Add Multiple Basemaps ---
|
| 1054 |
+
basemaps = [
|
| 1055 |
+
"HYBRID", "ROADMAP", "TERRAIN", "SATELLITE",
|
| 1056 |
+
"Esri.WorldImagery", "Esri.WorldTopoMap", "Esri.WorldShadedRelief",
|
| 1057 |
+
"Esri.NatGeoWorldMap", "Esri.OceanBasemap",
|
| 1058 |
+
"CartoDB.Positron", "CartoDB.DarkMatter",
|
| 1059 |
+
"Stamen.Terrain", "Stamen.Watercolor",
|
| 1060 |
+
"OpenStreetMap",
|
| 1061 |
+
]
|
| 1062 |
+
for b in basemaps:
|
| 1063 |
+
try:
|
| 1064 |
+
m.add_basemap(b)
|
| 1065 |
+
except Exception:
|
| 1066 |
+
pass
|
| 1067 |
+
|
| 1068 |
+
# -------------------------------
|
| 1069 |
+
# Boundaries & Grid
|
| 1070 |
+
# -------------------------------
|
| 1071 |
+
countries = ee.FeatureCollection("USDOS/LSIB_SIMPLE/2017")
|
| 1072 |
+
m.addLayer(
|
| 1073 |
+
countries.style(**{"color": "black", "fillColor": "00000000", "width": 1}),
|
| 1074 |
+
{}, "Country Boundaries"
|
| 1075 |
+
)
|
| 1076 |
+
|
| 1077 |
+
states = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level1")
|
| 1078 |
+
m.addLayer(
|
| 1079 |
+
states.style(**{"color": "purple", "fillColor": "00000000", "width": 0.5}),
|
| 1080 |
+
{}, "State/Province Boundaries"
|
| 1081 |
+
)
|
| 1082 |
+
|
| 1083 |
+
graticule = geemap.latlon_grid(5.0, region=ee.Geometry.Rectangle([-180, -90, 180, 90]))
|
| 1084 |
+
m.addLayer(graticule.style(**{"color": "gray", "width": 0.5}), {}, "Lat/Lon Grid")
|
| 1085 |
+
|
| 1086 |
+
# -------------------------------
|
| 1087 |
+
# Datasets
|
| 1088 |
+
# -------------------------------
|
| 1089 |
+
soil = ee.Image("OpenLandMap/SOL/SOL_CLAY-WFRACTION_USDA-4B1C_M/v01").select("b200")
|
| 1090 |
+
soil_vis = {"min": 0, "max": 60, "palette": ["yellow", "brown", "red"]}
|
| 1091 |
+
m.addLayer(soil, soil_vis, "Soil Clay (200cm)")
|
| 1092 |
+
|
| 1093 |
+
dem = ee.Image("USGS/SRTMGL1_003")
|
| 1094 |
+
dem_vis = {"min": 0, "max": 4000, "palette": ["blue", "green", "brown", "white"]}
|
| 1095 |
+
m.addLayer(dem, dem_vis, "Topography (SRTM DEM)")
|
| 1096 |
+
|
| 1097 |
+
seismic = ee.Image("GEM/2015/GlobalSeismicHazard").select("b0") # use band b0
|
| 1098 |
+
m.addLayer(seismic, {"min": 0, "max": 1, "palette": ["white", "red"]}, "Seismic Hazard")
|
| 1099 |
+
|
| 1100 |
+
water = ee.Image("JRC/GSW1_4/GlobalSurfaceWater").select("occurrence")
|
| 1101 |
+
flood_vis = {"min": 0, "max": 100, "palette": ["white", "blue"]}
|
| 1102 |
+
m.addLayer(water, flood_vis, "Flood Hazard")
|
| 1103 |
+
|
| 1104 |
+
landcover = ee.Image("ESA/WorldCover/v200/2021")
|
| 1105 |
+
landcover_vis = {
|
| 1106 |
+
"bands": ["Map"], "min": 10, "max": 100,
|
| 1107 |
+
"palette": [
|
| 1108 |
+
"006400", "ffbb22", "ffff4c", "f096ff", "fa0000",
|
| 1109 |
+
"b4b4b4", "f0f0f0", "0064c8", "0096a0", "00cf75"
|
| 1110 |
+
]
|
| 1111 |
+
}
|
| 1112 |
+
m.addLayer(landcover, landcover_vis, "Landcover 2021")
|
| 1113 |
+
|
| 1114 |
+
# -------------------------------
|
| 1115 |
+
# Drawing Instructions
|
| 1116 |
+
# -------------------------------
|
| 1117 |
+
st.markdown("π Use the **drawing tool** (polygon/rectangle) on the map to select a region.")
|
| 1118 |
+
m.add_draw_control(polyline=False, circle=False, circlemarker=False, rectangle=True, polygon=True)
|
| 1119 |
+
m.to_streamlit(height=700, responsive=True)
|
| 1120 |
+
|
| 1121 |
+
# -------------------------------
|
| 1122 |
+
# ROI Analysis
|
| 1123 |
+
# -------------------------------
|
| 1124 |
+
if m.user_roi is not None:
|
| 1125 |
+
roi = m.user_roi
|
| 1126 |
+
st.success("β
Polygon selected!")
|
| 1127 |
+
|
| 1128 |
+
def safe_get(val):
|
| 1129 |
+
try:
|
| 1130 |
+
return round(val, 2) if val is not None else "N/A"
|
| 1131 |
+
except Exception:
|
| 1132 |
+
return "N/A"
|
| 1133 |
+
|
| 1134 |
+
# Soil
|
| 1135 |
+
soil_val = soil.reduceRegion(
|
| 1136 |
+
ee.Reducer.mean(), roi, 1000, 1e9
|
| 1137 |
+
).get("b200").getInfo()
|
| 1138 |
+
|
| 1139 |
+
# Elevation
|
| 1140 |
+
elev_val = dem.reduceRegion(
|
| 1141 |
+
ee.Reducer.mean(), roi, 1000, 1e9
|
| 1142 |
+
).get("elevation").getInfo()
|
| 1143 |
+
|
| 1144 |
+
# Seismic
|
| 1145 |
+
seismic_val = seismic.reduceRegion(
|
| 1146 |
+
ee.Reducer.mean(), roi, 5000, 1e9
|
| 1147 |
+
).get("b0").getInfo()
|
| 1148 |
+
|
| 1149 |
+
# Flood
|
| 1150 |
+
flood_val = water.reduceRegion(
|
| 1151 |
+
ee.Reducer.mean(), roi, 30, 1e9
|
| 1152 |
+
).get("occurrence").getInfo()
|
| 1153 |
+
|
| 1154 |
+
# Landcover Histogram
|
| 1155 |
+
lc_stats = landcover.reduceRegion(
|
| 1156 |
+
reducer=ee.Reducer.frequencyHistogram(),
|
| 1157 |
+
geometry=roi,
|
| 1158 |
+
scale=30,
|
| 1159 |
+
maxPixels=1e9
|
| 1160 |
+
).get("Map").getInfo()
|
| 1161 |
+
|
| 1162 |
+
# -------------------------------
|
| 1163 |
+
# UI Outputs
|
| 1164 |
+
# -------------------------------
|
| 1165 |
+
st.subheader("π Regional Data Summary")
|
| 1166 |
+
st.write(f"**Average Clay (200cm):** {safe_get(soil_val)} %")
|
| 1167 |
+
st.write(f"**Average Elevation:** {safe_get(elev_val)} m")
|
| 1168 |
+
st.write(f"**Seismic Hazard (PGA):** {safe_get(seismic_val)} g")
|
| 1169 |
+
st.write(f"**Flood Occurrence Probability:** {safe_get(flood_val)} %")
|
| 1170 |
+
|
| 1171 |
+
# Landcover chart
|
| 1172 |
+
if lc_stats:
|
| 1173 |
+
labels = [str(k) for k in lc_stats.keys()]
|
| 1174 |
+
values = list(lc_stats.values())
|
| 1175 |
+
fig, ax = plt.subplots(figsize=(6, 4))
|
| 1176 |
+
ax.pie(values, labels=labels, autopct="%1.1f%%", startangle=90)
|
| 1177 |
+
ax.set_title("Landcover Distribution")
|
| 1178 |
+
st.pyplot(fig)
|
| 1179 |
+
|
| 1180 |
+
# Soil histogram
|
| 1181 |
+
soil_hist = soil.reduceRegion(
|
| 1182 |
+
reducer=ee.Reducer.histogram(maxBuckets=10),
|
| 1183 |
+
geometry=roi,
|
| 1184 |
+
scale=1000,
|
| 1185 |
+
maxPixels=1e9
|
| 1186 |
+
).get("b200").getInfo()
|
| 1187 |
+
if soil_hist:
|
| 1188 |
+
fig, ax = plt.subplots(figsize=(6, 4))
|
| 1189 |
+
ax.bar(
|
| 1190 |
+
soil_hist["bucketMeans"], soil_hist["histogram"],
|
| 1191 |
+
width=2, color="brown", alpha=0.7
|
| 1192 |
+
)
|
| 1193 |
+
ax.set_xlabel("Clay fraction (%)")
|
| 1194 |
+
ax.set_ylabel("Pixel count")
|
| 1195 |
+
ax.set_title("Soil Clay Distribution")
|
| 1196 |
+
st.pyplot(fig)
|
| 1197 |
+
|
| 1198 |
+
# -------------------------------
|
| 1199 |
+
# Save to soil_json for reports
|
| 1200 |
+
# -------------------------------
|
| 1201 |
+
if "soil_json" not in st.session_state:
|
| 1202 |
+
st.session_state["soil_json"] = {}
|
| 1203 |
+
|
| 1204 |
+
st.session_state["soil_json"].update({
|
| 1205 |
+
"Soil": f"{safe_get(soil_val)} %",
|
| 1206 |
+
"Elevation": f"{safe_get(elev_val)} m",
|
| 1207 |
+
"Seismic Hazard": f"{safe_get(seismic_val)} g",
|
| 1208 |
+
"Flood Probability": f"{safe_get(flood_val)} %",
|
| 1209 |
+
"Landcover Stats": lc_stats if lc_stats else {}
|
| 1210 |
+
})
|
| 1211 |
+
st.success("π Data saved to soil_json for report integration!")
|
| 1212 |
|
| 1213 |
+
else:
|
| 1214 |
+
st.warning("β οΈ No polygon selected yet.")
|
| 1215 |
|
| 1216 |
|
| 1217 |
# GeoMate Ask (RAG) β simple chat with memory per site and auto-extract numeric values
|