MSU576 commited on
Commit
33c6c16
Β·
verified Β·
1 Parent(s): 771dd70

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +263 -0
app.py CHANGED
@@ -1005,7 +1005,270 @@ def ocr_page():
1005
  st.error(f"OCR failed: {e}")
1006
  else:
1007
  st.warning("OCR not available in this deployment.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1008
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1009
 
1010
  # GeoMate Ask (RAG) β€” simple chat with memory per site and auto-extract numeric values
1011
  def rag_page():
 
1005
  st.error(f"OCR failed: {e}")
1006
  else:
1007
  st.warning("OCR not available in this deployment.")
1008
+ # Locator Page (with Earth Engine auth at top)
1009
+ import os
1010
+ import streamlit as st
1011
+ import geemap.foliumap as geemap
1012
+ import ee
1013
+ import matplotlib.pyplot as plt
1014
+
1015
+ # ----------------------------
1016
+ # ----------------------------
1017
+ def locator_page():
1018
+ # ----------------------------
1019
+ # Earth Engine Initialization (service account or interactive)
1020
+ # ----------------------------
1021
+ EARTHENGINE_TOKEN = os.getenv("EARTHENGINE_TOKEN")
1022
+ SERVICE_ACCOUNT = os.getenv("SERVICE_ACCOUNT") # optional: service account email
1023
+
1024
+ def initialize_ee():
1025
+ """Initialize Earth Engine with multiple fallbacks."""
1026
+ if "ee_initialized" in st.session_state and st.session_state["ee_initialized"]:
1027
+ return True
1028
+
1029
+ if EARTHENGINE_TOKEN and SERVICE_ACCOUNT:
1030
+ try:
1031
+ creds = ee.ServiceAccountCredentials(
1032
+ email=SERVICE_ACCOUNT,
1033
+ key_data=EARTHENGINE_TOKEN
1034
+ )
1035
+ ee.Initialize(creds)
1036
+ st.session_state["ee_initialized"] = True
1037
+ return True
1038
+ except Exception as e:
1039
+ st.warning(f"Service account init failed: {e} β€” trying default/interactive auth...")
1040
+
1041
+ try:
1042
+ ee.Initialize()
1043
+ st.session_state["ee_initialized"] = True
1044
+ return True
1045
+ except Exception:
1046
+ try:
1047
+ ee.Authenticate()
1048
+ ee.Initialize()
1049
+ st.session_state["ee_initialized"] = True
1050
+ return True
1051
+ except Exception as e:
1052
+ st.error(f"Earth Engine authentication failed: {e}")
1053
+ return False
1054
+
1055
+ if not initialize_ee():
1056
+ st.stop()
1057
 
1058
+ # ----------------------------
1059
+ # Helper functions
1060
+ # ----------------------------
1061
+ def safe_get_reduce(region, image, band: str, scale: int, default=None, max_pixels=1e9):
1062
+ try:
1063
+ res = image.reduceRegion(
1064
+ reducer=ee.Reducer.mean(),
1065
+ geometry=region,
1066
+ scale=scale,
1067
+ maxPixels=int(max_pixels)
1068
+ )
1069
+ val = res.get(band)
1070
+ if val is None:
1071
+ return default
1072
+ return float(val.getInfo())
1073
+ except Exception:
1074
+ return default
1075
+
1076
+ def safe_reduce_histogram(region, image, band: str, scale: int, max_pixels=1e9):
1077
+ try:
1078
+ hist = image.reduceRegion(
1079
+ reducer=ee.Reducer.frequencyHistogram(),
1080
+ geometry=region,
1081
+ scale=scale,
1082
+ maxPixels=int(max_pixels)
1083
+ ).get(band)
1084
+ if hist is None:
1085
+ return None
1086
+ return hist.getInfo()
1087
+ except Exception:
1088
+ return None
1089
+
1090
+ def get_roi_from_map(m: geemap.Map):
1091
+ """Try multiple ways to extract the ROI from the drawn shapes."""
1092
+ candidates = [
1093
+ getattr(m, "user_roi", None),
1094
+ getattr(m, "last_drawn_geojson", None),
1095
+ getattr(m, "draw_last_feature", None),
1096
+ getattr(m, "draw_features", None),
1097
+ getattr(m, "user_drawn_features", None),
1098
+ getattr(m, "drawn_features", None),
1099
+ ]
1100
+ for c in candidates:
1101
+ if c:
1102
+ if isinstance(c, ee.Geometry):
1103
+ return c
1104
+ try:
1105
+ if isinstance(c, dict):
1106
+ return ee.Geometry(c)
1107
+ if isinstance(c, list) and len(c) > 0 and isinstance(c[0], dict):
1108
+ return ee.Geometry(c[0])
1109
+ except Exception:
1110
+ pass
1111
+ try:
1112
+ s = getattr(m, "draw_last_feature", None)
1113
+ if s:
1114
+ import json
1115
+ if isinstance(s, str):
1116
+ return ee.Geometry(json.loads(s))
1117
+ except Exception:
1118
+ pass
1119
+ return None
1120
+
1121
+ # ----------------------------
1122
+ # Page title
1123
+ # ----------------------------
1124
+ st.title("🌍 GeoMate Interactive Earth Explorer")
1125
+ st.markdown(
1126
+ "Draw a polygon or rectangle on the map (use the drawing tool), then the app will compute "
1127
+ "regional summaries (soil clay, elevation, seismic, flood occurrence, landcover distribution)."
1128
+ )
1129
+
1130
+ # ----------------------------
1131
+ # Map setup
1132
+ # ----------------------------
1133
+ m = geemap.Map(center=[28.0, 72.0], zoom=4, draw_export=True)
1134
+
1135
+ basemaps = [
1136
+ "HYBRID", "ROADMAP", "TERRAIN", "SATELLITE",
1137
+ "Esri.WorldImagery", "Esri.WorldTopoMap", "Esri.WorldShadedRelief",
1138
+ "Esri.NatGeoWorldMap", "Esri.OceanBasemap",
1139
+ "CartoDB.Positron", "CartoDB.DarkMatter",
1140
+ "Stamen.Terrain", "Stamen.Watercolor",
1141
+ "OpenStreetMap",
1142
+ ]
1143
+ for b in basemaps:
1144
+ try:
1145
+ m.add_basemap(b)
1146
+ except Exception:
1147
+ pass
1148
+
1149
+ # ----------------------------
1150
+ # Datasets
1151
+ # ----------------------------
1152
+ soil = ee.Image("OpenLandMap/SOL/SOL_CLAY-WFRACTION_USDA-4B1C_M/v01").select("b200")
1153
+ soil_vis = {"min": 0, "max": 60, "palette": ["yellow", "brown", "red"]}
1154
+ m.addLayer(soil, soil_vis, "Soil Clay (200cm)")
1155
+
1156
+ dem = ee.Image("USGS/SRTMGL1_003")
1157
+ dem_vis = {"min": 0, "max": 4000, "palette": ["blue", "green", "brown", "white"]}
1158
+ m.addLayer(dem, dem_vis, "Topography (SRTM DEM)")
1159
+
1160
+ seismic = ee.Image("GEM/2015/GlobalSeismicHazard").select("b0")
1161
+ m.addLayer(seismic, {"min": 0, "max": 1, "palette": ["white", "red"]}, "Seismic Hazard")
1162
+
1163
+ water = ee.Image("JRC/GSW1_4/GlobalSurfaceWater").select("occurrence")
1164
+ flood_vis = {"min": 0, "max": 100, "palette": ["white", "blue"]}
1165
+ m.addLayer(water, flood_vis, "Flood Hazard")
1166
+
1167
+ landcover = ee.Image("ESA/WorldCover/v200/2021")
1168
+ landcover_vis = {
1169
+ "bands": ["Map"], "min": 10, "max": 100,
1170
+ "palette": [
1171
+ "006400", "ffbb22", "ffff4c", "f096ff", "fa0000",
1172
+ "b4b4b4", "f0f0f0", "0064c8", "0096a0", "00cf75"
1173
+ ]
1174
+ }
1175
+ m.addLayer(landcover, landcover_vis, "Landcover 2021")
1176
+
1177
+ # Boundaries & grid
1178
+ countries = ee.FeatureCollection("USDOS/LSIB_SIMPLE/2017")
1179
+ m.addLayer(
1180
+ countries.style(**{"color": "black", "fillColor": "00000000", "width": 1}),
1181
+ {}, "Country Boundaries"
1182
+ )
1183
+ states = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level1")
1184
+ m.addLayer(
1185
+ states.style(**{"color": "purple", "fillColor": "00000000", "width": 0.5}),
1186
+ {}, "State/Province Boundaries"
1187
+ )
1188
+ graticule = geemap.latlon_grid(5.0, region=ee.Geometry.Rectangle([-180, -90, 180, 90]))
1189
+ m.addLayer(graticule.style(**{"color": "gray", "width": 0.5}), {}, "Lat/Lon Grid")
1190
+
1191
+ # Drawing tools
1192
+ m.add_draw_control(polyline=False, circle=False, circlemarker=False, rectangle=True, polygon=True)
1193
+ m.to_streamlit(height=700, responsive=True)
1194
+ st.markdown("πŸ‘‰ Use the map drawing tool to select a polygon/rectangle.")
1195
+
1196
+ # ----------------------------
1197
+ # ROI extraction
1198
+ # ----------------------------
1199
+ roi = get_roi_from_map(m)
1200
+ if roi is None:
1201
+ st.warning("⚠️ No polygon selected yet.")
1202
+ return
1203
+
1204
+ try:
1205
+ if not isinstance(roi, ee.Geometry):
1206
+ roi = ee.Geometry(roi)
1207
+ except Exception:
1208
+ st.error("πŸ‘Ž Could not parse the drawn ROI into an Earth Engine geometry.")
1209
+ return
1210
+
1211
+ st.success("βœ… Polygon selected! Computing regional summaries...")
1212
+
1213
+ # ----------------------------
1214
+ # Compute stats
1215
+ # ----------------------------
1216
+ soil_val = safe_get_reduce(roi, soil, "b200", scale=1000, default=None)
1217
+ elev_val = safe_get_reduce(roi, dem, "elevation", scale=1000, default=None)
1218
+ seismic_val = safe_get_reduce(roi, seismic, "b0", scale=5000, default=None)
1219
+ flood_val = safe_get_reduce(roi, water, "occurrence", scale=30, default=None)
1220
+ lc_stats = safe_reduce_histogram(roi, landcover, "Map", scale=30)
1221
+
1222
+ def safe_display(x):
1223
+ return "N/A" if x is None else round(x, 2)
1224
+
1225
+ st.subheader("πŸ“Š Regional Data Summary")
1226
+ st.write(f"**Average Clay (200cm):** {safe_display(soil_val)} %")
1227
+ st.write(f"**Average Elevation:** {safe_display(elev_val)} m")
1228
+ st.write(f"**Seismic Hazard (PGA):** {safe_display(seismic_val)} g")
1229
+ st.write(f"**Flood Occurrence Probability:** {safe_display(flood_val)} %")
1230
+
1231
+ if lc_stats:
1232
+ labels = [str(k) for k in lc_stats.keys()]
1233
+ values = list(lc_stats.values())
1234
+ fig1, ax1 = plt.subplots(figsize=(6, 4))
1235
+ ax1.pie(values, labels=labels, autopct="%1.1f%%", startangle=90)
1236
+ ax1.set_title("Landcover Distribution")
1237
+ st.pyplot(fig1)
1238
+
1239
+ try:
1240
+ soil_hist = soil.reduceRegion(
1241
+ reducer=ee.Reducer.histogram(maxBuckets=10),
1242
+ geometry=roi,
1243
+ scale=1000,
1244
+ maxPixels=1e9
1245
+ ).get("b200").getInfo()
1246
+ except Exception:
1247
+ soil_hist = None
1248
+
1249
+ if soil_hist and isinstance(soil_hist, dict) and "bucketMeans" in soil_hist:
1250
+ fig2, ax2 = plt.subplots(figsize=(6, 4))
1251
+ ax2.bar(soil_hist["bucketMeans"], soil_hist["histogram"],
1252
+ width=2, color="brown", alpha=0.7)
1253
+ ax2.set_xlabel("Clay fraction (%)")
1254
+ ax2.set_ylabel("Pixel count")
1255
+ ax2.set_title("Soil Clay Distribution")
1256
+ st.pyplot(fig2)
1257
+
1258
+ # ----------------------------
1259
+ # Save for reports
1260
+ # ----------------------------
1261
+ if "soil_json" not in st.session_state:
1262
+ st.session_state["soil_json"] = {}
1263
+
1264
+ st.session_state["soil_json"].update({
1265
+ "Soil": f"{safe_display(soil_val)} %",
1266
+ "Elevation": f"{safe_display(elev_val)} m",
1267
+ "Seismic Hazard": f"{safe_display(seismic_val)} g",
1268
+ "Flood Probability": f"{safe_display(flood_val)} %",
1269
+ "Landcover Stats": lc_stats if lc_stats else {}
1270
+ })
1271
+ st.success("πŸ“‘ Data saved to `soil_json` for report integration!")
1272
 
1273
  # GeoMate Ask (RAG) β€” simple chat with memory per site and auto-extract numeric values
1274
  def rag_page():