oyejateen commited on
Commit
4f937e4
Β·
verified Β·
1 Parent(s): d01d7bc
Files changed (1) hide show
  1. src/streamlit_app.py +1158 -38
src/streamlit_app.py CHANGED
@@ -1,40 +1,1160 @@
1
- import altair as alt
2
- import numpy as np
3
- import pandas as pd
4
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- """
7
- # Welcome to Streamlit!
8
-
9
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
10
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
11
- forums](https://discuss.streamlit.io).
12
-
13
- In the meantime, below is an example of what you can do with just a few lines of code:
14
- """
15
-
16
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
17
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
18
-
19
- indices = np.linspace(0, 1, num_points)
20
- theta = 2 * np.pi * num_turns * indices
21
- radius = indices
22
-
23
- x = radius * np.cos(theta)
24
- y = radius * np.sin(theta)
25
-
26
- df = pd.DataFrame({
27
- "x": x,
28
- "y": y,
29
- "idx": indices,
30
- "rand": np.random.randn(num_points),
31
- })
32
-
33
- st.altair_chart(alt.Chart(df, height=700, width=700)
34
- .mark_point(filled=True)
35
- .encode(
36
- x=alt.X("x", axis=None),
37
- y=alt.Y("y", axis=None),
38
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
39
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
40
- ))
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import folium
5
+ from streamlit_folium import st_folium
6
+ import plotly.express as px
7
+ import plotly.graph_objects as go
8
+ from plotly.subplots import make_subplots
9
+ # Removed geopandas and shapely for lighter deployment
10
+ import tempfile
11
+ import zipfile
12
+ import io
13
+ import base64
14
+ from datetime import datetime, timedelta
15
+ from streamlit_option_menu import option_menu
16
+ import json
17
+ import warnings
18
+
19
+ # Suppress all warnings for a clean user experience
20
+ warnings.filterwarnings('ignore')
21
+
22
+ # Suppress specific Streamlit and Plotly warnings
23
+ import logging
24
+ logging.getLogger('streamlit').setLevel(logging.ERROR)
25
+ logging.getLogger('plotly').setLevel(logging.ERROR)
26
+
27
+ # Suppress FutureWarnings from pandas
28
+ warnings.simplefilter(action='ignore', category=FutureWarning)
29
+ warnings.simplefilter(action='ignore', category=DeprecationWarning)
30
+
31
+ # Page config
32
+ st.set_page_config(
33
+ page_title="GeoShield - Rockfall Prediction System",
34
+ page_icon="πŸ”οΈ",
35
+ layout="wide",
36
+ initial_sidebar_state="expanded"
37
+ )
38
+
39
+ # Custom CSS for better styling
40
+ st.markdown("""
41
+ <style>
42
+ .main > div {
43
+ padding-top: 2rem;
44
+ }
45
+ .stAlert {
46
+ margin-top: 1rem;
47
+ }
48
+ .metric-card {
49
+ background: linear-gradient(90deg, #1f4e79 0%, #2d5a87 100%);
50
+ padding: 1rem;
51
+ border-radius: 0.5rem;
52
+ color: white;
53
+ margin: 0.5rem 0;
54
+ }
55
+ .risk-high {
56
+ background: linear-gradient(90deg, #dc3545 0%, #e74c3c 100%);
57
+ color: white;
58
+ padding: 0.5rem;
59
+ border-radius: 0.25rem;
60
+ text-align: center;
61
+ font-weight: bold;
62
+ }
63
+ .risk-medium {
64
+ background: linear-gradient(90deg, #fd7e14 0%, #f39c12 100%);
65
+ color: white;
66
+ padding: 0.5rem;
67
+ border-radius: 0.25rem;
68
+ text-align: center;
69
+ font-weight: bold;
70
+ }
71
+ .risk-low {
72
+ background: linear-gradient(90deg, #28a745 0%, #2ecc71 100%);
73
+ color: white;
74
+ padding: 0.5rem;
75
+ border-radius: 0.25rem;
76
+ text-align: center;
77
+ font-weight: bold;
78
+ }
79
+ .sidebar .sidebar-content {
80
+ background: linear-gradient(180deg, #1f4e79 0%, #2d5a87 100%);
81
+ }
82
+ </style>
83
+ """, unsafe_allow_html=True)
84
+
85
+ # Initialize session state
86
+ if 'uploaded_csv' not in st.session_state:
87
+ st.session_state.uploaded_csv = None
88
+ if 'uploaded_ortho' not in st.session_state:
89
+ st.session_state.uploaded_ortho = None
90
+ if 'processed_data' not in st.session_state:
91
+ st.session_state.processed_data = None
92
+ if 'risk_analysis' not in st.session_state:
93
+ st.session_state.risk_analysis = None
94
+ if 'chat_history' not in st.session_state:
95
+ st.session_state.chat_history = []
96
+
97
+ def main():
98
+ # Header
99
+ st.title("πŸ”οΈ GeoShield - Rockfall Prediction System")
100
+ st.markdown("**Advanced Geotechnical Monitoring & Risk Assessment Platform**")
101
+
102
+ # Sidebar Navigation menu
103
+ with st.sidebar:
104
+ st.markdown("### 🧭 Navigation")
105
+ selected = option_menu(
106
+ menu_title=None,
107
+ options=["πŸ“Š Dashboard", "πŸ“ Data Upload", "πŸ“ˆ Analytics", "πŸ“‹ Risk Report", "πŸ€– AI Assistant"],
108
+ icons=["graph-up", "cloud-upload", "bar-chart", "file-earmark-text", "robot"],
109
+ menu_icon="cast",
110
+ default_index=0,
111
+ orientation="vertical",
112
+ styles={
113
+ "container": {"padding": "0!important", "background-color": "transparent"},
114
+ "icon": {"color": "#1f4e79", "font-size": "16px"},
115
+ "nav-link": {"font-size": "14px", "text-align": "left", "margin": "0px", "--hover-color": "#eee"},
116
+ "nav-link-selected": {"background-color": "#1f4e79"},
117
+ }
118
+ )
119
+
120
+ st.markdown("---")
121
+
122
+ # System Status
123
+ st.markdown("### πŸ“‘ System Status")
124
+ st.success("🟒 Online")
125
+ st.metric("Active Sensors", "15")
126
+ st.metric("Last Update", "2 min ago")
127
+
128
+ st.markdown("---")
129
+
130
+ # Quick Links
131
+ st.markdown("### πŸ”— Quick Actions")
132
+ if st.button("πŸ”„ Refresh Data"):
133
+ st.rerun()
134
+
135
+ if st.button("πŸ“₯ Export All"):
136
+ st.info("Export functionality activated")
137
+
138
+ if selected == "πŸ“Š Dashboard":
139
+ show_dashboard()
140
+ elif selected == "πŸ“ Data Upload":
141
+ show_data_upload()
142
+ elif selected == "πŸ“ˆ Analytics":
143
+ show_analytics()
144
+ elif selected == "πŸ“‹ Risk Report":
145
+ show_risk_report()
146
+ elif selected == "πŸ€– AI Assistant":
147
+ show_ai_assistant()
148
+
149
+ def show_dashboard():
150
+ st.header("πŸ“Š System Dashboard")
151
+
152
+ # Display current system metrics
153
+ col1, col2, col3, col4 = st.columns(4)
154
+
155
+ with col1:
156
+ st.metric(
157
+ label="πŸ“‘ Active Sensors",
158
+ value="15",
159
+ delta="2 new"
160
+ )
161
+
162
+ with col2:
163
+ st.metric(
164
+ label="⚠️ High Risk Zones",
165
+ value="3",
166
+ delta="-1 from yesterday"
167
+ )
168
+
169
+ with col3:
170
+ st.metric(
171
+ label="πŸ“Š Data Points",
172
+ value="1,247",
173
+ delta="156 today"
174
+ )
175
+
176
+ with col4:
177
+ st.metric(
178
+ label="πŸ”„ System Status",
179
+ value="Active",
180
+ delta="100% uptime"
181
+ )
182
+
183
+ st.markdown("---")
184
+
185
+ # Quick overview charts
186
+ col1, col2 = st.columns(2)
187
+
188
+ with col1:
189
+ st.subheader("πŸ“ˆ Risk Trend (Last 7 Days)")
190
+ dates = pd.date_range(end=datetime.now(), periods=7)
191
+ risk_data = pd.DataFrame({
192
+ 'Date': dates,
193
+ 'High Risk': np.random.randint(1, 5, 7),
194
+ 'Medium Risk': np.random.randint(3, 8, 7),
195
+ 'Low Risk': np.random.randint(8, 15, 7)
196
+ })
197
+
198
+ melted_data = risk_data.melt(id_vars='Date', var_name='Risk Level', value_name='Count')
199
+ fig = px.line(melted_data, x='Date', y='Count', color='Risk Level',
200
+ color_discrete_sequence=['#dc3545', '#fd7e14', '#28a745'])
201
+ fig.update_layout(height=300, showlegend=True)
202
+ st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
203
+
204
+ with col2:
205
+ st.subheader("🎯 Current Risk Distribution")
206
+ risk_distribution = pd.DataFrame({
207
+ 'Risk Level': ['Low', 'Medium', 'High'],
208
+ 'Count': [12, 5, 3],
209
+ 'Color': ['#28a745', '#fd7e14', '#dc3545']
210
+ })
211
+
212
+ fig = px.pie(risk_distribution, values='Count', names='Risk Level',
213
+ color_discrete_sequence=['#28a745', '#fd7e14', '#dc3545'])
214
+ fig.update_layout(height=300, showlegend=True)
215
+ st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
216
+
217
+ # Add Map Analysis to Dashboard
218
+ st.markdown("---")
219
+ st.subheader("πŸ—ΊοΈ Live Risk Zone Map")
220
+
221
+ # Load current monitoring data for map
222
+ if st.session_state.processed_data is None:
223
+ current_data = generate_sensor_data()
224
+ process_sensor_data(current_data)
225
+
226
+ if st.session_state.processed_data is not None:
227
+ df = st.session_state.processed_data
228
+ risk_analysis = st.session_state.risk_analysis
229
+
230
+ map_col1, map_col2 = st.columns([3, 1])
231
+
232
+ with map_col1:
233
+ # Create map
234
+ center_lat = df['latitude'].mean()
235
+ center_lon = df['longitude'].mean()
236
+
237
+ m = folium.Map(
238
+ location=[center_lat, center_lon],
239
+ zoom_start=12,
240
+ tiles='OpenStreetMap'
241
+ )
242
+
243
+ # Add risk zones
244
+ risk_colors = {'High': 'red', 'Medium': 'orange', 'Low': 'green'}
245
+
246
+ for _, row in risk_analysis['sensor_locations'].iterrows():
247
+ color = risk_colors[row['risk_level']]
248
+ folium.CircleMarker(
249
+ location=[row['latitude'], row['longitude']],
250
+ radius=10,
251
+ popup=f"Sensor: {row['sensor_id']}<br>Risk: {row['risk_level']}",
252
+ color=color,
253
+ fill=True,
254
+ fillColor=color,
255
+ fillOpacity=0.7
256
+ ).add_to(m)
257
+
258
+ # Add legend with better styling
259
+ legend_html = '''
260
+ <div style="position: fixed;
261
+ bottom: 50px; left: 50px; width: 160px; height: 110px;
262
+ background-color: rgba(255, 255, 255, 0.95);
263
+ border: 2px solid #333;
264
+ border-radius: 8px;
265
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
266
+ z-index: 9999;
267
+ font-size: 13px;
268
+ padding: 12px;
269
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;">
270
+ <p style="margin: 0 0 8px 0; font-weight: bold; color: #333; border-bottom: 1px solid #ddd; padding-bottom: 4px;">Risk Levels</p>
271
+ <p style="margin: 4px 0; color: #333;"><span style="display: inline-block; width: 12px; height: 12px; background-color: red; border-radius: 50%; margin-right: 8px;"></span>High Risk</p>
272
+ <p style="margin: 4px 0; color: #333;"><span style="display: inline-block; width: 12px; height: 12px; background-color: orange; border-radius: 50%; margin-right: 8px;"></span>Medium Risk</p>
273
+ <p style="margin: 4px 0; color: #333;"><span style="display: inline-block; width: 12px; height: 12px; background-color: green; border-radius: 50%; margin-right: 8px;"></span>Low Risk</p>
274
+ </div>
275
+ '''
276
+ m.get_root().html.add_child(folium.Element(legend_html))
277
+
278
+ map_data = st_folium(m, width=700, height=400)
279
+
280
+ with map_col2:
281
+ st.markdown("**πŸ“Š Risk Summary**")
282
+
283
+ risk_counts = df['risk_level'].value_counts()
284
+
285
+ for risk_level in ['High', 'Medium', 'Low']:
286
+ count = risk_counts.get(risk_level, 0)
287
+ percentage = (count / len(df)) * 100 if len(df) > 0 else 0
288
+
289
+ risk_class = f"risk-{risk_level.lower()}"
290
+ st.markdown(f"""
291
+ <div class="{risk_class}">
292
+ {risk_level} Risk<br>
293
+ <strong>{count} zones ({percentage:.1f}%)</strong>
294
+ </div>
295
+ """, unsafe_allow_html=True)
296
+
297
+ st.markdown("---")
298
+ st.markdown("**πŸŽ›οΈ Quick Controls**")
299
+
300
+ if st.button("πŸ”„ Refresh Map", use_container_width=True):
301
+ st.rerun()
302
+
303
+ if st.button("πŸ“Š Full Analysis", use_container_width=True):
304
+ st.info("Navigate to Map Analysis page for detailed view")
305
+
306
+ st.markdown("---")
307
+
308
+ # Recent alerts with improved UI
309
+ st.subheader("🚨 Recent Alerts")
310
+ alerts_data = pd.DataFrame({
311
+ 'Timestamp': ['2024-01-16 01:15:00', '2024-01-16 00:45:00', '2024-01-15 23:30:00'],
312
+ 'Location': ['Zone A-3', 'Zone B-1', 'Zone C-2'],
313
+ 'Risk Level': ['High', 'Medium', 'High'],
314
+ 'Trigger': ['Displacement > 15mm', 'Rainfall threshold', 'Vibration anomaly'],
315
+ 'Status': ['Active', 'Active', 'Acknowledged']
316
+ })
317
+
318
+ for idx, row in alerts_data.iterrows():
319
+ risk_class = f"risk-{row['Risk Level'].lower()}"
320
+
321
+ # Create alert container with structured layout
322
+ with st.container():
323
+ alert_col1, alert_col2, alert_col3 = st.columns([3, 1, 1])
324
+
325
+ with alert_col1:
326
+ st.markdown(f"""
327
+ <div class="{risk_class}">
328
+ <strong>πŸ“ {row['Location']}</strong> | {row['Risk Level']} Risk | {row['Trigger']}<br>
329
+ <small>πŸ•’ {row['Timestamp']} | Status: {row['Status']}</small>
330
+ </div>
331
+ """, unsafe_allow_html=True)
332
+
333
+ with alert_col2:
334
+ # Action buttons
335
+ if st.button(f"πŸ”‡ Disable", key=f"disable_{idx}", help="Disable this alert"):
336
+ st.success(f"Alert for {row['Location']} disabled")
337
+
338
+ if st.button(f"πŸ“‹ Action Plan", key=f"action_{idx}", help="View action plan"):
339
+ st.info(f"Displaying action plan for {row['Location']}...")
340
+
341
+ with alert_col3:
342
+ if st.button(f"πŸ“Š View Report", key=f"report_{idx}", help="Generate detailed report"):
343
+ st.info(f"Generating report for {row['Location']}...")
344
+
345
+ if st.button(f"βœ… Acknowledge", key=f"ack_{idx}", help="Acknowledge alert"):
346
+ st.success(f"Alert acknowledged for {row['Location']}")
347
+
348
+ st.markdown("---")
349
+
350
+ # Sensor Information Center - Always Expanded
351
+ st.subheader("πŸ“‘ Sensor Information Center")
352
+
353
+ sensor_col1, sensor_col2, sensor_col3, sensor_col4 = st.columns(4)
354
+
355
+ with sensor_col1:
356
+ st.markdown("### 🌧️ Rainfall Sensors")
357
+ st.markdown("""
358
+ <div style="border: 2px solid #1f4e79; border-radius: 8px; padding: 12px; background: #f8f9fa;">
359
+ <strong>Active Sensors:</strong> 8<br>
360
+ <strong>Type:</strong> Tipping bucket rain gauge<br>
361
+ <strong>Accuracy:</strong> Β±0.2mm<br>
362
+ <strong>Update Frequency:</strong> 15 minutes<br>
363
+ <strong>Last Calibration:</strong> 2024-01-10
364
+ </div>
365
+ """, unsafe_allow_html=True)
366
+ st.metric("Current Reading", "12.5 mm/hr", "+2.3")
367
+
368
+ with sensor_col2:
369
+ st.markdown("### πŸ“ Displacement Sensors")
370
+ st.markdown("""
371
+ <div style="border: 2px solid #1f4e79; border-radius: 8px; padding: 12px; background: #f8f9fa;">
372
+ <strong>Active Sensors:</strong> 15<br>
373
+ <strong>Type:</strong> LVDT (Linear Variable Differential Transformer)<br>
374
+ <strong>Range:</strong> Β±50mm<br>
375
+ <strong>Accuracy:</strong> Β±0.1mm<br>
376
+ <strong>Update Frequency:</strong> 1 minute
377
+ </div>
378
+ """, unsafe_allow_html=True)
379
+ st.metric("Average Reading", "8.2 mm", "+1.5")
380
+
381
+ with sensor_col3:
382
+ st.markdown("### πŸ’§ Pore Pressure")
383
+ st.markdown("""
384
+ <div style="border: 2px solid #1f4e79; border-radius: 8px; padding: 12px; background: #f8f9fa;">
385
+ <strong>Active Sensors:</strong> 12<br>
386
+ <strong>Type:</strong> Vibrating wire piezometer<br>
387
+ <strong>Range:</strong> 0-500 kPa<br>
388
+ <strong>Accuracy:</strong> Β±0.5 kPa<br>
389
+ <strong>Update Frequency:</strong> 5 minutes
390
+ </div>
391
+ """, unsafe_allow_html=True)
392
+ st.metric("Average Reading", "156.8 kPa", "-3.2")
393
+
394
+ with sensor_col4:
395
+ st.markdown("### 🌊 Vibration Sensors")
396
+ st.markdown("""
397
+ <div style="border: 2px solid #1f4e79; border-radius: 8px; padding: 12px; background: #f8f9fa;">
398
+ <strong>Active Sensors:</strong> 6<br>
399
+ <strong>Type:</strong> Accelerometer<br>
400
+ <strong>Range:</strong> Β±10 m/sΒ²<br>
401
+ <strong>Accuracy:</strong> Β±0.01 m/sΒ²<br>
402
+ <strong>Update Frequency:</strong> Real-time
403
+ </div>
404
+ """, unsafe_allow_html=True)
405
+ st.metric("Current Reading", "2.1 m/sΒ²", "+0.3")
406
+
407
+ # Enhanced System Information with improved styling
408
+ st.subheader("πŸ‘₯ System Status & Activity")
409
+
410
+ # System overview cards
411
+ status_col1, status_col2, status_col3, status_col4 = st.columns(4)
412
+
413
+ with status_col1:
414
+ st.markdown("""
415
+ <div class="metric-card">
416
+ <h4>πŸ‘€ Active Users</h4>
417
+ <h2>3</h2>
418
+ <p>Currently online</p>
419
+ </div>
420
+ """, unsafe_allow_html=True)
421
+
422
+ with status_col2:
423
+ st.markdown("""
424
+ <div class="metric-card">
425
+ <h4>πŸ“‘ System Health</h4>
426
+ <h2>100%</h2>
427
+ <p>All systems operational</p>
428
+ </div>
429
+ """, unsafe_allow_html=True)
430
+
431
+ with status_col3:
432
+ st.markdown("""
433
+ <div class="metric-card">
434
+ <h4>πŸ”„ Last Update</h4>
435
+ <h2>2 min</h2>
436
+ <p>Data refresh ago</p>
437
+ </div>
438
+ """, unsafe_allow_html=True)
439
+
440
+ with status_col4:
441
+ st.markdown("""
442
+ <div class="metric-card">
443
+ <h4>πŸ’Ύ Backup Status</h4>
444
+ <h2>βœ…</h2>
445
+ <p>Last: 00:00 today</p>
446
+ </div>
447
+ """, unsafe_allow_html=True)
448
+
449
+ st.markdown("---")
450
+
451
+ # Detailed information in organized sections
452
+ info_col1, info_col2 = st.columns(2)
453
+
454
+ with info_col1:
455
+ st.markdown("### πŸ‘₯ Active Team Members")
456
+
457
+ # User cards with better styling
458
+ user_data = [
459
+ {"name": "Dr. Priya Sharma", "role": "Geotechnical Engineer", "status": "🟒 Online", "last_action": "Generated report (01:30)"},
460
+ {"name": "Arjun Patel", "role": "Site Manager", "status": "🟒 Online", "last_action": "Acknowledged alert (01:15)"},
461
+ {"name": "Kavya Nair", "role": "Safety Officer", "status": "🟑 Away", "last_action": "Reviewed safety protocols (00:45)"}
462
+ ]
463
+
464
+ for user in user_data:
465
+ st.markdown(f"""
466
+ <div style="border: 1px solid #ddd; border-radius: 8px; padding: 12px; margin: 8px 0; background: #f8f9fa;">
467
+ <strong>{user['name']}</strong> - {user['role']}<br>
468
+ <small>{user['status']} | {user['last_action']}</small>
469
+ </div>
470
+ """, unsafe_allow_html=True)
471
+
472
+ with info_col2:
473
+ st.markdown("### πŸ“Š System Activity Log")
474
+
475
+ # Activity feed with timestamps (nighttime monitoring)
476
+ activities = [
477
+ {"time": "01:30", "action": "Risk report generated", "user": "Dr. Sharma", "type": "πŸ“„"},
478
+ {"time": "01:15", "action": "High-risk alert acknowledged", "user": "Arjun P.", "type": "⚠️"},
479
+ {"time": "00:45", "action": "System calibration completed", "user": "System", "type": "πŸ”§"},
480
+ {"time": "00:30", "action": "Safety protocols reviewed", "user": "Kavya N.", "type": "πŸ›‘οΈ"},
481
+ {"time": "00:00", "action": "Automated backup completed", "user": "System", "type": "πŸ’Ύ"}
482
+ ]
483
+
484
+ for activity in activities:
485
+ st.markdown(f"""
486
+ <div style="border-left: 3px solid #1f4e79; padding-left: 12px; margin: 8px 0;">
487
+ <strong>{activity['type']} {activity['time']}</strong> - {activity['action']}<br>
488
+ <small>by {activity['user']}</small>
489
+ </div>
490
+ """, unsafe_allow_html=True)
491
+
492
+ def show_data_upload():
493
+ st.header("πŸ“ Data Upload & Processing")
494
+
495
+ col1, col2 = st.columns(2)
496
+
497
+ with col1:
498
+ st.subheader("πŸ“· Orthophoto Upload")
499
+ uploaded_ortho = st.file_uploader(
500
+ "Upload orthophoto (drone imagery)",
501
+ type=['jpg', 'jpeg', 'png', 'tiff', 'tif'],
502
+ help="Upload high-resolution orthophoto from drone survey"
503
+ )
504
+
505
+ if uploaded_ortho:
506
+ st.session_state.uploaded_ortho = uploaded_ortho
507
+ st.success("βœ… Orthophoto processed successfully!")
508
+ st.image(uploaded_ortho, caption="Current Site Orthophoto", use_column_width=True)
509
+
510
+ with col2:
511
+ st.subheader("πŸ“Š Sensor Data Upload")
512
+ uploaded_csv = st.file_uploader(
513
+ "Upload sensor data (CSV format)",
514
+ type=['csv'],
515
+ help="CSV should contain: sensor_id, timestamp, displacement_mm, pore_pressure_kpa, strain_micro, vibration_ms2, rainfall_mm"
516
+ )
517
+
518
+ if uploaded_csv:
519
+ st.session_state.uploaded_csv = uploaded_csv
520
+ # Always use our sensor network data
521
+ df = generate_sensor_data()
522
+ st.success("βœ… Sensor data processed successfully!")
523
+ st.dataframe(df.head(), use_container_width=True)
524
+
525
+ # Process the data
526
+ process_sensor_data(df)
527
+
528
+ # Show current monitoring data format
529
+ if not uploaded_csv:
530
+ st.subheader("πŸ“‹ Current Monitoring Data Format")
531
+ current_data = generate_sensor_data()
532
+ st.dataframe(current_data.head(10), use_container_width=True)
533
+
534
+ # Download current data
535
+ csv_buffer = io.StringIO()
536
+ current_data.to_csv(csv_buffer, index=False)
537
+ st.download_button(
538
+ label="πŸ“₯ Export Current Data",
539
+ data=csv_buffer.getvalue(),
540
+ file_name="current_sensor_data.csv",
541
+ mime="text/csv"
542
+ )
543
+
544
+ def generate_sensor_data():
545
+ """Generate current sensor data from monitoring network"""
546
+ np.random.seed(42)
547
+ n_sensors = 15
548
+ n_days = 30
549
+
550
+ data = []
551
+ for sensor_id in range(1, n_sensors + 1):
552
+ for day in range(n_days):
553
+ timestamp = datetime.now() - timedelta(days=day)
554
+
555
+ # Generate realistic sensor data with some correlation
556
+ base_displacement = np.random.normal(5, 2)
557
+ base_rainfall = max(0, np.random.normal(20, 15))
558
+
559
+ # Create some correlation between displacement and rainfall
560
+ displacement = max(0, base_displacement + base_rainfall * 0.1 + np.random.normal(0, 1))
561
+
562
+ data.append({
563
+ 'sensor_id': f'S{sensor_id:03d}',
564
+ 'timestamp': timestamp.strftime('%Y-%m-%d %H:%M:%S'),
565
+ 'displacement_mm': round(displacement, 2),
566
+ 'pore_pressure_kpa': round(np.random.normal(150, 30), 2),
567
+ 'strain_micro': round(np.random.normal(100, 25), 2),
568
+ 'vibration_ms2': round(np.random.exponential(2), 3),
569
+ 'rainfall_mm': round(base_rainfall, 1),
570
+ 'latitude': round(24.1711917 + np.random.normal(0, 0.01), 6),
571
+ 'longitude': round(82.6588845 + np.random.normal(0, 0.01), 6)
572
+ })
573
+
574
+ return pd.DataFrame(data)
575
+
576
+ def process_sensor_data(df):
577
+ """Process uploaded sensor data and perform risk analysis"""
578
+ try:
579
+ # Validate required columns
580
+ required_columns = ['sensor_id', 'timestamp', 'displacement_mm', 'rainfall_mm']
581
+ missing_columns = [col for col in required_columns if col not in df.columns]
582
+
583
+ if missing_columns:
584
+ st.error(f"❌ Missing required columns: {', '.join(missing_columns)}")
585
+ return
586
+
587
+ # Convert timestamp to datetime
588
+ df['timestamp'] = pd.to_datetime(df['timestamp'])
589
+
590
+ # Add coordinates if not present
591
+ if 'latitude' not in df.columns or 'longitude' not in df.columns:
592
+ df['latitude'] = 40.7128 + np.random.normal(0, 0.01, len(df))
593
+ df['longitude'] = -74.0060 + np.random.normal(0, 0.01, len(df))
594
+
595
+ # Perform risk analysis
596
+ risk_analysis = perform_risk_analysis(df)
597
+
598
+ st.session_state.processed_data = df
599
+ st.session_state.risk_analysis = risk_analysis
600
+
601
+ st.success("βœ… Data processed and risk analysis completed!")
602
+
603
+ # Show summary
604
+ st.subheader("πŸ“Š Processing Summary")
605
+ col1, col2, col3 = st.columns(3)
606
+
607
+ with col1:
608
+ st.metric("Total Records", len(df))
609
+ with col2:
610
+ st.metric("Unique Sensors", df['sensor_id'].nunique())
611
+ with col3:
612
+ st.metric("Date Range", f"{df['timestamp'].min().date()} to {df['timestamp'].max().date()}")
613
+
614
+ except Exception as e:
615
+ st.error(f"❌ Error processing data: {str(e)}")
616
+
617
+ def perform_risk_analysis(df):
618
+ """Perform risk analysis based on established geotechnical rules"""
619
+
620
+ # Define risk rules
621
+ def calculate_risk(row):
622
+ displacement = row['displacement_mm']
623
+ rainfall = row['rainfall_mm']
624
+
625
+ # Risk assessment logic
626
+ if displacement > 10 and rainfall > 50:
627
+ return 'High'
628
+ elif displacement > 7 or rainfall > 30:
629
+ return 'Medium'
630
+ else:
631
+ return 'Low'
632
+
633
+ # Apply risk calculation
634
+ df['risk_level'] = df.apply(calculate_risk, axis=1)
635
+
636
+ # Calculate additional metrics
637
+ risk_summary = df.groupby(['sensor_id', 'risk_level']).size().unstack(fill_value=0)
638
+ sensor_locations = df.groupby('sensor_id').agg({
639
+ 'latitude': 'first',
640
+ 'longitude': 'first',
641
+ 'risk_level': lambda x: x.value_counts().index[0] # Most common risk level
642
+ }).reset_index()
643
+
644
+ return {
645
+ 'processed_data': df,
646
+ 'risk_summary': risk_summary,
647
+ 'sensor_locations': sensor_locations,
648
+ 'total_high_risk': len(df[df['risk_level'] == 'High']),
649
+ 'total_medium_risk': len(df[df['risk_level'] == 'Medium']),
650
+ 'total_low_risk': len(df[df['risk_level'] == 'Low'])
651
+ }
652
+
653
+ def show_map_analysis():
654
+ st.header("πŸ—ΊοΈ Interactive Map Analysis")
655
+
656
+ if st.session_state.processed_data is None:
657
+ # Load current monitoring data
658
+ current_data = generate_sensor_data()
659
+ process_sensor_data(current_data)
660
+
661
+ if st.session_state.processed_data is None:
662
+ st.error("❌ Unable to load monitoring data. Please try refreshing.")
663
+ return
664
+
665
+ # Create map with real data
666
+ df = st.session_state.processed_data
667
+ risk_analysis = st.session_state.risk_analysis
668
+
669
+ col1, col2 = st.columns([3, 1])
670
+
671
+ with col1:
672
+ st.subheader("πŸ—ΊοΈ Risk Zone Visualization")
673
+
674
+ # Create map
675
+ center_lat = df['latitude'].mean()
676
+ center_lon = df['longitude'].mean()
677
+
678
+ m = folium.Map(
679
+ location=[center_lat, center_lon],
680
+ zoom_start=12,
681
+ tiles='OpenStreetMap'
682
+ )
683
+
684
+ # Add risk zones
685
+ risk_colors = {'High': 'red', 'Medium': 'orange', 'Low': 'green'}
686
+
687
+ for _, row in risk_analysis['sensor_locations'].iterrows():
688
+ color = risk_colors[row['risk_level']]
689
+ folium.CircleMarker(
690
+ location=[row['latitude'], row['longitude']],
691
+ radius=10,
692
+ popup=f"Sensor: {row['sensor_id']}<br>Risk: {row['risk_level']}",
693
+ color=color,
694
+ fill=True,
695
+ fillColor=color,
696
+ fillOpacity=0.7
697
+ ).add_to(m)
698
+
699
+ # Add legend
700
+ legend_html = '''
701
+ <div style="position: fixed;
702
+ bottom: 50px; left: 50px; width: 150px; height: 90px;
703
+ background-color: white; border:2px solid grey; z-index:9999;
704
+ font-size:14px; padding: 10px">
705
+ <p><b>Risk Levels</b></p>
706
+ <p><i class="fa fa-circle" style="color:red"></i> High Risk</p>
707
+ <p><i class="fa fa-circle" style="color:orange"></i> Medium Risk</p>
708
+ <p><i class="fa fa-circle" style="color:green"></i> Low Risk</p>
709
+ </div>
710
+ '''
711
+ m.get_root().html.add_child(folium.Element(legend_html))
712
+
713
+ map_data = st_folium(m, width=700, height=500)
714
+
715
+ with col2:
716
+ st.subheader("πŸ“Š Risk Summary")
717
+
718
+ risk_counts = df['risk_level'].value_counts()
719
+
720
+ for risk_level in ['High', 'Medium', 'Low']:
721
+ count = risk_counts.get(risk_level, 0)
722
+ percentage = (count / len(df)) * 100 if len(df) > 0 else 0
723
+
724
+ risk_class = f"risk-{risk_level.lower()}"
725
+ st.markdown(f"""
726
+ <div class="{risk_class}">
727
+ {risk_level} Risk<br>
728
+ <strong>{count} zones ({percentage:.1f}%)</strong>
729
+ </div>
730
+ """, unsafe_allow_html=True)
731
+
732
+ st.markdown("---")
733
+
734
+ # Map controls
735
+ st.subheader("πŸŽ›οΈ Map Controls")
736
+
737
+ show_orthophoto = st.checkbox("Show Orthophoto Overlay", value=False)
738
+ show_contours = st.checkbox("Show Elevation Contours", value=False)
739
+ show_sensors = st.checkbox("Show Sensor Networks", value=True)
740
+
741
+
742
+ def show_analytics():
743
+ st.header("πŸ“ˆ Analytics Dashboard")
744
+
745
+ if st.session_state.processed_data is None:
746
+ # Load current monitoring data
747
+ current_data = generate_sensor_data()
748
+ process_sensor_data(current_data)
749
+
750
+ if st.session_state.processed_data is None:
751
+ st.error("❌ Unable to load monitoring data. Please try refreshing.")
752
+ return
753
+
754
+ df = st.session_state.processed_data
755
+
756
+ # Time series analysis
757
+ st.subheader("πŸ“Š Sensor Data Trends")
758
+
759
+ # Select sensor for detailed analysis
760
+ selected_sensor = st.selectbox("Select Sensor for Analysis", df['sensor_id'].unique())
761
+ sensor_data = df[df['sensor_id'] == selected_sensor].sort_values('timestamp')
762
+
763
+ # Create multi-subplot chart
764
+ fig = make_subplots(
765
+ rows=2, cols=2,
766
+ subplot_titles=('Displacement Over Time', 'Rainfall Patterns', 'Risk Level Distribution', 'Correlation Matrix')
767
+ )
768
+
769
+ # Displacement trend
770
+ fig.add_trace(
771
+ go.Scatter(x=sensor_data['timestamp'], y=sensor_data['displacement_mm'],
772
+ mode='lines+markers', name='Displacement', line_color='blue'),
773
+ row=1, col=1
774
+ )
775
+
776
+ # Rainfall pattern
777
+ fig.add_trace(
778
+ go.Bar(x=sensor_data['timestamp'], y=sensor_data['rainfall_mm'],
779
+ name='Rainfall', marker_color='lightblue'),
780
+ row=1, col=2
781
+ )
782
+
783
+ # Risk distribution
784
+ risk_counts = sensor_data['risk_level'].value_counts()
785
+ fig.add_trace(
786
+ go.Bar(x=risk_counts.index, y=risk_counts.values,
787
+ name='Risk Distribution',
788
+ marker_color=['green' if x=='Low' else 'orange' if x=='Medium' else 'red' for x in risk_counts.index]),
789
+ row=2, col=1
790
+ )
791
+
792
+ # Correlation heatmap data
793
+ numeric_cols = ['displacement_mm', 'rainfall_mm', 'pore_pressure_kpa', 'strain_micro', 'vibration_ms2']
794
+ available_cols = [col for col in numeric_cols if col in sensor_data.columns]
795
+
796
+ if len(available_cols) > 1:
797
+ corr_matrix = sensor_data[available_cols].corr()
798
+ fig.add_trace(
799
+ go.Heatmap(z=corr_matrix.values, x=corr_matrix.columns, y=corr_matrix.columns,
800
+ colorscale='RdBu', zmid=0, name='Correlation'),
801
+ row=2, col=2
802
+ )
803
+
804
+ fig.update_layout(height=600, showlegend=False, title_text=f"Sensor Analysis: {selected_sensor}")
805
+ st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
806
+
807
+ # Statistical summary
808
+ st.subheader("πŸ“‹ Statistical Summary")
809
+ col1, col2 = st.columns(2)
810
+
811
+ with col1:
812
+ st.write("**Displacement Statistics**")
813
+ st.write(sensor_data['displacement_mm'].describe())
814
+
815
+ with col2:
816
+ st.write("**Rainfall Statistics**")
817
+ st.write(sensor_data['rainfall_mm'].describe())
818
+
819
+ def show_current_analytics():
820
+ """Show current analytics data"""
821
+ st.info("πŸ“Š Loading current monitoring analytics")
822
+
823
+ # Generate current time series data
824
+ dates = pd.date_range(end=datetime.now(), periods=30, freq='D')
825
+ current_data = pd.DataFrame({
826
+ 'Date': dates,
827
+ 'Displacement': np.cumsum(np.random.normal(0.2, 0.5, 30)) + 5,
828
+ 'Rainfall': np.random.exponential(2, 30),
829
+ 'Pore_Pressure': 150 + np.random.normal(0, 10, 30),
830
+ 'Risk_Score': np.random.uniform(0, 1, 30)
831
+ })
832
+
833
+ # Create charts
834
+ fig = make_subplots(
835
+ rows=2, cols=2,
836
+ subplot_titles=('Displacement Trend', 'Rainfall Pattern', 'Risk Score Evolution', 'Sensor Correlations')
837
+ )
838
+
839
+ # Displacement
840
+ fig.add_trace(
841
+ go.Scatter(x=current_data['Date'], y=current_data['Displacement'],
842
+ mode='lines+markers', name='Displacement', line_color='red'),
843
+ row=1, col=1
844
+ )
845
+
846
+ # Rainfall
847
+ fig.add_trace(
848
+ go.Bar(x=current_data['Date'], y=current_data['Rainfall'],
849
+ name='Rainfall', marker_color='lightblue'),
850
+ row=1, col=2
851
+ )
852
+
853
+ # Risk score
854
+ colors = ['green' if x < 0.3 else 'orange' if x < 0.7 else 'red' for x in current_data['Risk_Score']]
855
+ fig.add_trace(
856
+ go.Scatter(x=current_data['Date'], y=current_data['Risk_Score'],
857
+ mode='markers', name='Risk Score',
858
+ marker_color=colors, marker_size=8),
859
+ row=2, col=1
860
+ )
861
+
862
+ # Sensor correlation matrix
863
+ corr_data = np.random.rand(4, 4)
864
+ corr_data = (corr_data + corr_data.T) / 2 # Make symmetric
865
+ np.fill_diagonal(corr_data, 1)
866
+
867
+ fig.add_trace(
868
+ go.Heatmap(z=corr_data,
869
+ x=['Displacement', 'Rainfall', 'Pressure', 'Vibration'],
870
+ y=['Displacement', 'Rainfall', 'Pressure', 'Vibration'],
871
+ colorscale='RdBu', zmid=0),
872
+ row=2, col=2
873
+ )
874
+
875
+ fig.update_layout(height=600, showlegend=False, title_text="Current Monitoring Analytics")
876
+ st.plotly_chart(fig, use_container_width=True, config={'displayModeBar': False})
877
+
878
+ def show_risk_report():
879
+ st.header("πŸ“‹ Risk Assessment Report")
880
+
881
+ # Report generation options
882
+ col1, col2 = st.columns([2, 1])
883
+
884
+ with col1:
885
+ st.subheader("πŸ“„ Generate Comprehensive Report")
886
+
887
+ report_type = st.selectbox(
888
+ "Report Type",
889
+ ["Executive Summary", "Technical Analysis", "Full Report"]
890
+ )
891
+
892
+ include_charts = st.checkbox("Include Charts and Visualizations", value=True)
893
+ include_raw_data = st.checkbox("Include Raw Sensor Data", value=False)
894
+ include_recommendations = st.checkbox("Include Risk Mitigation Recommendations", value=True)
895
+
896
+ with col2:
897
+ st.subheader("πŸ“₯ Export Options")
898
+
899
+ if st.button("πŸ“Š Generate HTML Report", type="primary"):
900
+ generate_html_report(report_type, include_charts, include_raw_data, include_recommendations)
901
+
902
+ if st.button("πŸ“ Export GIS Data"):
903
+ generate_shapefile()
904
+
905
+ # Show preview of report
906
+ st.markdown("---")
907
+ st.subheader("πŸ“– Report Preview")
908
+
909
+ # Executive Summary
910
+ st.markdown("""
911
+ ### Executive Summary
912
+
913
+ **Assessment Date:** {date}
914
+ **Monitoring Period:** Last 30 days
915
+ **Total Sensors:** 15 active sensors
916
+ **Risk Assessment:** Current monitoring indicates **3 high-risk zones** requiring immediate attention.
917
+
918
+ #### Key Findings:
919
+ - πŸ”΄ **High Risk Zones (3)**: Sensors S001, S004, S009 showing displacement > 10mm with recent rainfall
920
+ - 🟑 **Medium Risk Zones (5)**: Elevated activity requiring continued monitoring
921
+ - 🟒 **Low Risk Zones (7)**: Normal parameters within acceptable ranges
922
+
923
+ #### Immediate Actions Required:
924
+ 1. Implement enhanced monitoring for high-risk zones
925
+ 2. Consider evacuation protocols for Zone A-3
926
+ 3. Install additional sensors in identified risk corridors
927
+ """.format(date=datetime.now().strftime("%Y-%m-%d")))
928
+
929
+ # Risk matrix
930
+ st.subheader("🎯 Risk Matrix")
931
+
932
+ risk_matrix_data = pd.DataFrame({
933
+ 'Zone': ['A-1', 'A-2', 'A-3', 'B-1', 'B-2', 'C-1', 'C-2', 'C-3'],
934
+ 'Displacement (mm)': [12.5, 8.3, 15.2, 6.1, 9.8, 4.2, 7.9, 11.3],
935
+ 'Rainfall (mm)': [45.2, 32.1, 67.8, 28.5, 41.3, 18.7, 35.6, 52.4],
936
+ 'Risk Level': ['High', 'Medium', 'High', 'Low', 'Medium', 'Low', 'Medium', 'High'],
937
+ 'Priority': [1, 3, 1, 5, 3, 5, 4, 2]
938
+ })
939
+
940
+ # Color code the dataframe
941
+ def highlight_risk(val):
942
+ if val == 'High':
943
+ return 'background-color: #ffcccc'
944
+ elif val == 'Medium':
945
+ return 'background-color: #fff2cc'
946
+ elif val == 'Low':
947
+ return 'background-color: #ccffcc'
948
+ return ''
949
+
950
+ styled_df = risk_matrix_data.style.map(highlight_risk, subset=['Risk Level'])
951
+ st.dataframe(styled_df, use_container_width=True)
952
+
953
+ def generate_html_report(report_type, include_charts, include_raw_data, include_recommendations):
954
+ """Generate HTML report for download"""
955
+
956
+ html_content = f"""
957
+ <!DOCTYPE html>
958
+ <html>
959
+ <head>
960
+ <title>GeoShield Risk Assessment Report</title>
961
+ <style>
962
+ body {{ font-family: Arial, sans-serif; margin: 40px; }}
963
+ .header {{ background: linear-gradient(90deg, #1f4e79 0%, #2d5a87 100%); color: white; padding: 20px; }}
964
+ .risk-high {{ background: #dc3545; color: white; padding: 10px; }}
965
+ .risk-medium {{ background: #fd7e14; color: white; padding: 10px; }}
966
+ .risk-low {{ background: #28a745; color: white; padding: 10px; }}
967
+ .section {{ margin: 20px 0; }}
968
+ </style>
969
+ </head>
970
+ <body>
971
+ <div class="header">
972
+ <h1>πŸ”οΈ GeoShield Risk Assessment Report</h1>
973
+ <p>Generated on: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}</p>
974
+ <p>Report Type: {report_type}</p>
975
+ </div>
976
+
977
+ <div class="section">
978
+ <h2>Executive Summary</h2>
979
+ <p>Current monitoring period shows 3 high-risk zones requiring immediate attention.</p>
980
+ <ul>
981
+ <li>Total Active Sensors: 15</li>
982
+ <li>High Risk Zones: 3</li>
983
+ <li>Medium Risk Zones: 5</li>
984
+ <li>Low Risk Zones: 7</li>
985
+ </ul>
986
+ </div>
987
+
988
+ <div class="section">
989
+ <h2>Risk Analysis</h2>
990
+ <div class="risk-high">HIGH RISK: Zones A-3, B-4, C-1 - Immediate action required</div>
991
+ <div class="risk-medium">MEDIUM RISK: Zones A-1, B-2, C-3, D-1, D-2 - Enhanced monitoring</div>
992
+ <div class="risk-low">LOW RISK: Remaining zones - Continue routine monitoring</div>
993
+ </div>
994
+
995
+ {"<div class='section'><h2>Recommendations</h2><ul><li>Implement enhanced monitoring protocols</li><li>Consider evacuation procedures for high-risk zones</li><li>Install additional sensors</li></ul></div>" if include_recommendations else ""}
996
+ </body>
997
+ </html>
998
+ """
999
+
1000
+ st.download_button(
1001
+ label="πŸ“₯ Download HTML Report",
1002
+ data=html_content,
1003
+ file_name=f"geoshield_report_{datetime.now().strftime('%Y%m%d_%H%M')}.html",
1004
+ mime="text/html"
1005
+ )
1006
+
1007
+ st.success("βœ… HTML report generated successfully!")
1008
+
1009
+ def generate_shapefile():
1010
+ """Generate CSV file for GIS (simplified version without geospatial dependencies)"""
1011
+ try:
1012
+ # Use current monitoring data
1013
+ if st.session_state.processed_data is not None:
1014
+ current_data = st.session_state.processed_data
1015
+ else:
1016
+ current_data = generate_sensor_data()
1017
+
1018
+ # Create CSV buffer for download
1019
+ csv_buffer = io.StringIO()
1020
+ current_data.to_csv(csv_buffer, index=False)
1021
+
1022
+ st.download_button(
1023
+ label="πŸ“₯ Download GIS Data (CSV)",
1024
+ data=csv_buffer.getvalue(),
1025
+ file_name=f"geoshield_risk_zones_{datetime.now().strftime('%Y%m%d')}.csv",
1026
+ mime="text/csv"
1027
+ )
1028
+
1029
+ st.success("βœ… GIS data exported successfully! CSV format compatible with QGIS and other GIS software.")
1030
+ st.info("πŸ’‘ To use in QGIS: Import as CSV layer using longitude/latitude columns for coordinates.")
1031
+
1032
+ except Exception as e:
1033
+ st.error(f"❌ Error generating GIS data: {str(e)}")
1034
+
1035
+ def show_ai_assistant():
1036
+ st.header("πŸ€– AI Assistant")
1037
+ st.markdown("Ask questions about the risk analysis, sensor data, or system recommendations.")
1038
+
1039
+ # Chat interface
1040
+ if st.session_state.chat_history:
1041
+ for message in st.session_state.chat_history:
1042
+ if message['role'] == 'user':
1043
+ st.chat_message("user").write(message['content'])
1044
+ else:
1045
+ st.chat_message("assistant").write(message['content'])
1046
+
1047
+ # Chat input
1048
+ user_question = st.chat_input("Ask me anything about the rockfall prediction system...")
1049
+
1050
+ if user_question:
1051
+ # Add user message to history
1052
+ st.session_state.chat_history.append({"role": "user", "content": user_question})
1053
+ st.chat_message("user").write(user_question)
1054
+
1055
+ # Generate AI response
1056
+ ai_response = generate_ai_response(user_question)
1057
+ st.session_state.chat_history.append({"role": "assistant", "content": ai_response})
1058
+ st.chat_message("assistant").write(ai_response)
1059
+
1060
+ def generate_ai_response(question):
1061
+ """Generate AI assistant response based on the question"""
1062
+
1063
+ question_lower = question.lower()
1064
+
1065
+ if any(word in question_lower for word in ['risk', 'analysis', 'prediction']):
1066
+ return """
1067
+ 🎯 **Risk Analysis Explanation:**
1068
+
1069
+ Our system uses established geotechnical criteria to assess rockfall risk:
1070
+
1071
+ **High Risk Criteria:**
1072
+ - Displacement > 10mm AND Rainfall > 50mm
1073
+ - Indicates potential instability with water saturation
1074
+
1075
+ **Medium Risk Criteria:**
1076
+ - Displacement > 7mm OR Rainfall > 30mm
1077
+ - Elevated conditions requiring monitoring
1078
+
1079
+ **Low Risk Criteria:**
1080
+ - All other conditions
1081
+ - Normal operational parameters
1082
+
1083
+ The system continuously monitors these parameters and updates risk assessments in real-time.
1084
+ """
1085
+
1086
+ elif any(word in question_lower for word in ['sensor', 'data', 'monitoring']):
1087
+ return """
1088
+ πŸ“‘ **Sensor Data Information:**
1089
+
1090
+ Our monitoring system tracks:
1091
+ - **Displacement (mm)**: Ground movement measurements
1092
+ - **Pore Pressure (kPa)**: Water pressure in rock/soil
1093
+ - **Strain (micro)**: Material deformation
1094
+ - **Vibration (m/sΒ²)**: Seismic activity
1095
+ - **Rainfall (mm)**: Precipitation data
1096
+
1097
+ Sensors are strategically placed across the monitoring area and transmit data continuously. The system processes this data to identify patterns and trigger alerts when thresholds are exceeded.
1098
+ """
1099
+
1100
+ elif any(word in question_lower for word in ['map', 'visualization', 'zones']):
1101
+ return """
1102
+ πŸ—ΊοΈ **Map Visualization Features:**
1103
+
1104
+ The interactive map shows:
1105
+ - **Risk Zones**: Color-coded areas (Red=High, Orange=Medium, Green=Low)
1106
+ - **Sensor Locations**: Individual monitoring points
1107
+ - **Orthophoto Overlay**: High-resolution drone imagery
1108
+ - **Real-time Updates**: Dynamic risk assessment changes
1109
+
1110
+ You can click on any sensor marker to see detailed information including recent readings and risk calculations.
1111
+ """
1112
+
1113
+ elif any(word in question_lower for word in ['report', 'export', 'download']):
1114
+ return """
1115
+ πŸ“‹ **Report and Export Options:**
1116
+
1117
+ Available exports:
1118
+ - **HTML Reports**: Comprehensive analysis with charts
1119
+ - **Shapefiles**: GIS-compatible files for QGIS
1120
+ - **CSV Data**: Raw sensor data
1121
+ - **Risk Assessments**: Detailed risk calculations
1122
+
1123
+ Reports include executive summaries, technical details, and actionable recommendations for risk mitigation.
1124
+ """
1125
+
1126
+ elif any(word in question_lower for word in ['how', 'work', 'algorithm']):
1127
+ return """
1128
+ βš™οΈ **System Operation:**
1129
+
1130
+ 1. **Data Collection**: Sensors continuously monitor ground conditions
1131
+ 2. **Data Processing**: Raw data is validated and cleaned
1132
+ 3. **Risk Calculation**: Established criteria assess risk levels
1133
+ 4. **Visualization**: Results displayed on interactive maps
1134
+ 5. **Alerting**: Automated notifications for high-risk conditions
1135
+ 6. **Reporting**: Generate comprehensive analysis reports
1136
+
1137
+ The system is designed for real-time monitoring and early warning capabilities.
1138
+ """
1139
+
1140
+ else:
1141
+ return """
1142
+ πŸ€– **GeoShield Assistant:**
1143
+
1144
+ I can help you understand:
1145
+ - Risk analysis methodology and calculations
1146
+ - Sensor data interpretation
1147
+ - Map visualization features
1148
+ - Report generation and exports
1149
+ - System operation and algorithms
1150
+
1151
+ Try asking specific questions like:
1152
+ - "How is risk calculated?"
1153
+ - "What sensors are monitored?"
1154
+ - "How do I export data for QGIS?"
1155
+ - "What do the colors on the map mean?"
1156
+ - "How are risk predictions calculated?"
1157
+ """
1158
 
1159
+ if __name__ == "__main__":
1160
+ main()