Esmaeilkianii commited on
Commit
6c515f7
·
verified ·
1 Parent(s): 8d3f313

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +855 -244
app.py CHANGED
@@ -18,10 +18,12 @@ import os
18
  import json
19
  from PIL import Image
20
  import time
 
 
21
 
22
  # Set page configuration
23
  st.set_page_config(
24
- page_title="Smart Sugarcane Farm Monitoring",
25
  page_icon="🌱",
26
  layout="wide",
27
  initial_sidebar_state="collapsed"
@@ -30,19 +32,36 @@ st.set_page_config(
30
  # Custom CSS for styling
31
  st.markdown("""
32
  <style>
 
 
 
 
 
 
 
 
 
 
 
33
  .main {
34
- background-color: #1E2130;
35
  color: white;
 
 
36
  }
 
37
  .stApp {
38
- background-image: linear-gradient(to bottom, #1E2130, #2D3747);
39
  }
 
40
  .css-18e3th9 {
41
  padding-top: 0;
42
  }
 
43
  .css-1d391kg {
44
  padding-top: 1rem;
45
  }
 
46
  .stButton>button {
47
  background-color: #2EC4B6;
48
  color: white;
@@ -51,83 +70,118 @@ st.markdown("""
51
  padding: 10px 20px;
52
  transition: all 0.3s;
53
  }
 
54
  .stButton>button:hover {
55
  background-color: #1A9E8F;
56
  transform: scale(1.05);
57
  }
 
58
  .metric-card {
59
- background-color: rgba(30, 33, 48, 0.8);
60
  border-radius: 10px;
61
  padding: 15px;
62
  border: 1px solid #2EC4B6;
63
  text-align: center;
 
 
 
 
 
 
 
64
  }
 
65
  .metric-value {
66
- font-size: 24px;
67
  font-weight: bold;
68
  color: #2EC4B6;
 
69
  }
 
70
  .metric-label {
71
- font-size: 14px;
72
  color: #CCC;
 
73
  }
 
74
  .chart-container {
75
- background-color: rgba(30, 33, 48, 0.8);
76
  border-radius: 10px;
77
  padding: 15px;
78
  border: 1px solid #2EC4B6;
 
79
  }
 
80
  .table-container {
81
- background-color: rgba(30, 33, 48, 0.8);
82
  border-radius: 10px;
83
  padding: 15px;
84
  border: 1px solid #2EC4B6;
 
85
  }
 
86
  .compass {
87
  position: relative;
88
  width: 200px;
89
  height: 200px;
90
  margin: 0 auto;
 
91
  }
 
 
 
 
 
 
92
  .compass-img {
93
  width: 100%;
94
  height: 100%;
95
  }
 
96
  .stDataFrame {
97
- background-color: rgba(30, 33, 48, 0.8) !important;
98
  }
 
99
  div[data-testid="stDataFrameResizable"] {
100
- background-color: rgba(30, 33, 48, 0.8) !important;
101
  }
 
102
  div[data-testid="stDataFrameResizable"] > div {
103
- background-color: rgba(30, 33, 48, 0.8) !important;
104
  }
 
105
  div[data-testid="stDataFrameResizable"] td {
106
- background-color: rgba(30, 33, 48, 0.8) !important;
107
  color: white !important;
108
  }
 
109
  div[data-testid="stDataFrameResizable"] th {
110
  background-color: rgba(46, 196, 182, 0.3) !important;
111
  color: white !important;
112
  }
 
113
  .stTabs [data-baseweb="tab-list"] {
114
  gap: 10px;
115
  }
 
116
  .stTabs [data-baseweb="tab"] {
117
- background-color: rgba(30, 33, 48, 0.8);
118
  border-radius: 4px 4px 0px 0px;
119
  padding: 10px 20px;
120
  color: white;
121
  }
 
122
  .stTabs [aria-selected="true"] {
123
  background-color: #2EC4B6 !important;
124
  color: white !important;
125
  }
 
126
  .gauge-chart {
127
  margin: 0 auto;
128
  width: 200px;
129
  height: 200px;
130
  }
 
131
  @keyframes pulse {
132
  0% {
133
  box-shadow: 0 0 0 0 rgba(46, 196, 182, 0.7);
@@ -139,18 +193,357 @@ st.markdown("""
139
  box-shadow: 0 0 0 0 rgba(46, 196, 182, 0);
140
  }
141
  }
 
142
  .pulsing {
143
  animation: pulse 2s infinite;
144
  }
 
145
  .logo-container {
146
  text-align: center;
147
  margin-bottom: 20px;
148
  }
 
149
  .logo-text {
150
  font-size: 24px;
151
  font-weight: bold;
152
  color: #2EC4B6;
153
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  </style>
155
  """, unsafe_allow_html=True)
156
 
@@ -168,7 +561,7 @@ def initialize_ee():
168
  ee.Initialize(credentials)
169
  return True
170
  else:
171
- st.error("GEE credentials file not found. Please upload the credentials file.")
172
  return False
173
 
174
  # Load farm coordinates
@@ -176,16 +569,18 @@ def initialize_ee():
176
  def load_farm_data():
177
  try:
178
  farm_data = pd.read_csv('farm_coordinates.csv')
 
 
179
  return farm_data
180
  except Exception as e:
181
- st.error(f"Error loading farm coordinates: {e}")
182
  # Create sample data if file not found
183
  sample_data = pd.DataFrame({
184
- 'farm_name': [f'Farm_{i}' for i in range(1, 11)],
185
- 'age': np.random.randint(1, 6, 10),
186
- 'variety': np.random.choice(['Type A', 'Type B', 'Type C'], 10),
187
- 'longitude': np.random.uniform(51.0, 51.5, 10),
188
- 'latitude': np.random.uniform(32.0, 32.5, 10)
189
  })
190
  return sample_data
191
 
@@ -194,14 +589,17 @@ def load_farm_data():
194
  def load_day_schedule():
195
  try:
196
  schedule_data = pd.read_csv('پایگاه داده (1).csv')
 
 
 
197
  return schedule_data
198
  except Exception as e:
199
- st.error(f"Error loading day schedule: {e}")
200
  # Create sample data if file not found
201
- days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
202
  sample_data = pd.DataFrame({
203
- 'day': np.random.choice(days, 10),
204
- 'farm_name': [f'Farm_{i}' for i in range(1, 11)]
205
  })
206
  return sample_data
207
 
@@ -226,34 +624,32 @@ def calculate_indices(image):
226
  # NDRE = (NIR - Red Edge) / (NIR + Red Edge)
227
  ndre = image.normalizedDifference(['B8', 'B5']).rename('NDRE')
228
 
229
- # LAI calculation (simplified model)
230
- # LAI = 3.618 * EVI - 0.118
231
- nir = image.select('B8')
232
- red = image.select('B4')
233
- blue = image.select('B2')
234
- evi = image.expression(
235
- '2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))',
236
- {'NIR': nir, 'RED': red, 'BLUE': blue}
237
- ).rename('EVI')
238
- lai = evi.multiply(3.618).subtract(0.118).rename('LAI')
239
 
240
  # Chlorophyll index
 
 
241
  chlorophyll = image.expression(
242
  '(NIR / RED) - 1',
243
  {'NIR': nir, 'RED': red}
244
  ).rename('CHL')
245
 
246
  # Add all indices to the image
247
- return image.addBands([ndvi, ndre, lai, evi, chlorophyll])
248
 
249
  # Function to create LSTM model
250
  def create_lstm_model(input_shape):
251
  model = Sequential()
252
- model.add(LSTM(50, return_sequences=True, input_shape=input_shape))
253
  model.add(Dropout(0.2))
254
- model.add(LSTM(50, return_sequences=False))
255
  model.add(Dropout(0.2))
256
- model.add(Dense(25))
257
  model.add(Dense(3)) # Predict LAI, NDVI, NDRE
258
 
259
  model.compile(optimizer='adam', loss='mse')
@@ -323,7 +719,7 @@ def create_gauge_chart(value, title, min_val=0, max_val=1):
323
  gauge={
324
  'axis': {'range': [min_val, max_val], 'tickcolor': 'white'},
325
  'bar': {'color': "#2EC4B6"},
326
- 'bgcolor': "rgba(30, 33, 48, 0.8)",
327
  'borderwidth': 2,
328
  'bordercolor': "#2EC4B6",
329
  'steps': [
@@ -348,20 +744,22 @@ def create_gauge_chart(value, title, min_val=0, max_val=1):
348
  def create_line_chart(data, x_col, y_cols, title):
349
  fig = go.Figure()
350
 
351
- for col in y_cols:
 
 
352
  fig.add_trace(go.Scatter(
353
  x=data[x_col],
354
  y=data[col],
355
  mode='lines+markers',
356
  name=col,
357
- line=dict(width=3),
358
- marker=dict(size=8)
359
  ))
360
 
361
  fig.update_layout(
362
  title=title,
363
- xaxis_title='Date',
364
- yaxis_title='Value',
365
  legend=dict(
366
  orientation="h",
367
  yanchor="bottom",
@@ -370,7 +768,7 @@ def create_line_chart(data, x_col, y_cols, title):
370
  x=1
371
  ),
372
  template="plotly_dark",
373
- plot_bgcolor='rgba(30, 33, 48, 0.8)',
374
  paper_bgcolor='rgba(0,0,0,0)',
375
  font=dict(color='white'),
376
  margin=dict(l=20, r=20, t=50, b=20),
@@ -386,7 +784,10 @@ def create_bar_chart(data, x_col, y_col, title):
386
  fig.add_trace(go.Bar(
387
  x=data[x_col],
388
  y=data[y_col],
389
- marker_color='#2EC4B6'
 
 
 
390
  ))
391
 
392
  fig.update_layout(
@@ -394,7 +795,7 @@ def create_bar_chart(data, x_col, y_col, title):
394
  xaxis_title=x_col,
395
  yaxis_title=y_col,
396
  template="plotly_dark",
397
- plot_bgcolor='rgba(30, 33, 48, 0.8)',
398
  paper_bgcolor='rgba(0,0,0,0)',
399
  font=dict(color='white'),
400
  margin=dict(l=20, r=20, t=50, b=20),
@@ -421,7 +822,7 @@ def create_area_chart(data, x_col, y_col, title):
421
  xaxis_title=x_col,
422
  yaxis_title=y_col,
423
  template="plotly_dark",
424
- plot_bgcolor='rgba(30, 33, 48, 0.8)',
425
  paper_bgcolor='rgba(0,0,0,0)',
426
  font=dict(color='white'),
427
  margin=dict(l=20, r=20, t=50, b=20),
@@ -430,64 +831,118 @@ def create_area_chart(data, x_col, y_col, title):
430
 
431
  return fig
432
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  # Main application
434
  def main():
435
  # Initialize Earth Engine
436
  ee_initialized = initialize_ee()
437
  if not ee_initialized:
438
- st.warning("Earth Engine initialization failed. Some features may not work.")
439
 
440
  # Load data
441
  farm_data = load_farm_data()
442
  day_schedule = load_day_schedule()
443
 
444
  # Header with logo
445
- col1, col2, col3 = st.columns([1, 3, 1])
446
- with col1:
447
- st.markdown("""
448
- <div class="logo-container">
 
 
 
449
  <div style="font-size: 40px; color: #2EC4B6;">✦</div>
450
- <div class="logo-text">SMART<br>Sugarcane</div>
451
- <div style="color: #CCC; font-size: 16px;">FARM</div>
452
  </div>
453
- """, unsafe_allow_html=True)
454
-
455
- with col2:
456
- st.markdown("""
457
- <h1 style="text-align: center; color: #2EC4B6; margin-bottom: 20px;">
458
- <span style="margin-right: 10px;">��</span> Smart Sugarcane Farm Monitoring <span style="margin-left: 10px;">🌱</span>
459
- </h1>
460
- """, unsafe_allow_html=True)
461
 
462
  # Date selection
463
  today = datetime.now()
464
  start_date = today - timedelta(days=30)
465
  end_date = today
466
 
467
- col1, col2 = st.columns(2)
 
 
468
  with col1:
469
- start_date = st.date_input("Start Date", start_date)
470
  with col2:
471
- end_date = st.date_input("End Date", end_date)
 
 
 
 
472
 
473
- # Day selection
474
- days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
475
- selected_day = st.selectbox("Select Day", days)
476
 
477
  # Filter farms for selected day
478
- day_farms = day_schedule[day_schedule['day'] == selected_day]['farm_name'].tolist()
479
- selected_farms = farm_data[farm_data['farm_name'].isin(day_farms)]
480
 
481
  if selected_farms.empty:
482
- st.warning(f"No farms scheduled for {selected_day}. Please select another day.")
483
  else:
484
  # Create tabs for different sections
485
- tabs = st.tabs(["Dashboard", "Farm Analysis", "Growth Prediction", "Data Tables"])
486
 
487
  # Dashboard Tab
488
  with tabs[0]:
 
 
489
  # Main metrics row
490
- st.markdown("### Current Farm Metrics")
 
 
 
491
  metric_cols = st.columns(5)
492
 
493
  # Generate sample metrics for demonstration
@@ -501,7 +956,7 @@ def main():
501
  st.markdown(f"""
502
  <div class="metric-card">
503
  <div class="metric-value">{avg_ndvi}</div>
504
- <div class="metric-label">Avg. NDVI</div>
505
  </div>
506
  """, unsafe_allow_html=True)
507
 
@@ -509,7 +964,7 @@ def main():
509
  st.markdown(f"""
510
  <div class="metric-card">
511
  <div class="metric-value">{avg_lai}</div>
512
- <div class="metric-label">Avg. LAI</div>
513
  </div>
514
  """, unsafe_allow_html=True)
515
 
@@ -517,7 +972,7 @@ def main():
517
  st.markdown(f"""
518
  <div class="metric-card">
519
  <div class="metric-value">{avg_ndre}</div>
520
- <div class="metric-label">Avg. NDRE</div>
521
  </div>
522
  """, unsafe_allow_html=True)
523
 
@@ -525,7 +980,7 @@ def main():
525
  st.markdown(f"""
526
  <div class="metric-card">
527
  <div class="metric-value">{avg_chl}</div>
528
- <div class="metric-label">Chlorophyll</div>
529
  </div>
530
  """, unsafe_allow_html=True)
531
 
@@ -533,19 +988,25 @@ def main():
533
  st.markdown(f"""
534
  <div class="metric-card pulsing">
535
  <div class="metric-value">{avg_growth}%</div>
536
- <div class="metric-label">Growth Rate</div>
537
  </div>
538
  """, unsafe_allow_html=True)
539
 
 
 
540
  # Map and compass row
 
 
541
  map_col, compass_col = st.columns([3, 1])
542
 
543
  with map_col:
544
- st.markdown("### Farm Map (NDVI)")
 
 
545
 
546
  # Create a map centered at the mean of farm coordinates
547
- center_lat = selected_farms['latitude'].mean()
548
- center_lon = selected_farms['longitude'].mean()
549
 
550
  m = geemap.Map(center=[center_lat, center_lon], zoom=12)
551
 
@@ -554,7 +1015,7 @@ def main():
554
 
555
  # Create features for farms
556
  for _, farm in selected_farms.iterrows():
557
- point = ee.Geometry.Point([farm['longitude'], farm['latitude']])
558
  buffer = point.buffer(500) # 500m buffer around point
559
 
560
  # Get Sentinel imagery
@@ -575,16 +1036,16 @@ def main():
575
  'max': 1,
576
  'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']
577
  }
578
- m.addLayer(image_with_indices.select('NDVI'), ndvi_vis, f'NDVI - {farm["farm_name"]}')
579
 
580
  # Add farm marker with pulsing effect
581
  folium.Marker(
582
- location=[farm['latitude'], farm['longitude']],
583
  popup=f"""
584
- <div style='width: 200px'>
585
- <h4>{farm['farm_name']}</h4>
586
- <p>Age: {farm['age']} years</p>
587
- <p>Variety: {farm['variety']}</p>
588
  </div>
589
  """,
590
  icon=folium.Icon(color='green', icon='leaf')
@@ -594,20 +1055,30 @@ def main():
594
  folium_static(m)
595
 
596
  with compass_col:
597
- st.markdown("### Navigation")
598
- st.markdown("""
599
- <div class="compass">
600
- <img src="https://i.imgur.com/JSnB9ks.png" class="compass-img">
601
- </div>
602
- """, unsafe_allow_html=True)
603
 
604
  # Add a gauge chart for overall farm health
605
- st.markdown("### Farm Health")
606
- gauge_fig = create_gauge_chart(avg_growth/100, "Health Index", 0, 1)
 
 
 
607
  st.plotly_chart(gauge_fig, use_container_width=True)
608
 
 
 
609
  # Charts row
610
- st.markdown("### Farm Analytics")
 
 
 
 
 
611
  chart_col1, chart_col2 = st.columns(2)
612
 
613
  with chart_col1:
@@ -622,43 +1093,58 @@ def main():
622
  ndvi_values = np.clip(ndvi_values, 0, 1)
623
 
624
  ts_data = pd.DataFrame({
625
- 'Date': dates,
626
  'NDVI': ndvi_values,
627
  'NDRE': ndvi_values * 0.7 + np.random.normal(0, 0.05, size=len(dates)),
628
  'LAI': ndvi_values * 5 + np.random.normal(0, 0.3, size=len(dates))
629
  })
630
 
631
  # Create and display line chart
632
- line_fig = create_line_chart(ts_data, 'Date', ['NDVI', 'NDRE'], 'Vegetation Indices Over Time')
633
  st.plotly_chart(line_fig, use_container_width=True)
634
 
635
  with chart_col2:
636
  # Create sample area chart data
637
  area_data = pd.DataFrame({
638
- 'Date': dates,
639
- 'Growth': np.cumsum(np.random.normal(0.1, 0.03, size=len(dates)))
640
  })
641
 
642
  # Create and display area chart
643
- area_fig = create_area_chart(area_data, 'Date', 'Growth', 'Cumulative Growth')
644
  st.plotly_chart(area_fig, use_container_width=True)
645
 
 
 
646
  # Farm ranking row
647
- st.markdown("### Farm Rankings")
 
 
 
 
 
648
  rank_col1, rank_col2 = st.columns(2)
649
 
650
  with rank_col1:
651
- # Create sample farm ranking data
652
- farm_names = selected_farms['farm_name'].tolist()
653
- farm_ndvi = np.random.uniform(0.5, 0.9, size=len(farm_names))
 
 
 
 
 
 
 
 
654
 
655
  rank_data = pd.DataFrame({
656
- 'Farm': farm_names,
657
- 'NDVI': farm_ndvi
658
- }).sort_values('NDVI', ascending=False)
659
 
660
  # Create and display bar chart
661
- bar_fig = create_bar_chart(rank_data, 'Farm', 'NDVI', 'Farms by NDVI')
662
  st.plotly_chart(bar_fig, use_container_width=True)
663
 
664
  with rank_col2:
@@ -666,78 +1152,139 @@ def main():
666
  st.markdown('<div class="table-container">', unsafe_allow_html=True)
667
  st.dataframe(
668
  rank_data.reset_index(drop=True).style.background_gradient(
669
- cmap='Greens', subset=['NDVI']
670
  ),
671
  use_container_width=True
672
  )
673
  st.markdown('</div>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
 
675
  # Farm Analysis Tab
676
  with tabs[1]:
677
- st.markdown("## Detailed Farm Analysis")
 
 
 
 
678
 
679
  # Farm selection
680
- selected_farm = st.selectbox("Select Farm for Analysis", selected_farms['farm_name'].tolist())
681
- farm_row = selected_farms[selected_farms['farm_name'] == selected_farm].iloc[0]
682
 
683
  # Farm details
684
  st.markdown(f"""
685
  <div class="metric-card" style="margin-bottom: 20px;">
686
  <h3>{selected_farm}</h3>
687
- <p>Age: {farm_row['age']} years | Variety: {farm_row['variety']}</p>
688
- <p>Location: {farm_row['latitude']:.4f}, {farm_row['longitude']:.4f}</p>
689
  </div>
690
  """, unsafe_allow_html=True)
691
 
 
 
692
  # Create tabs for different analysis views
693
- analysis_tabs = st.tabs(["Vegetation Indices", "Growth Trends", "Environmental Factors"])
694
 
695
  # Vegetation Indices tab
696
  with analysis_tabs[0]:
 
 
697
  # Generate sample time series data for the selected farm
698
  dates = pd.date_range(start=start_date, end=end_date, freq='D')
699
 
700
  # Base values with some randomness
701
  base_ndvi = np.random.uniform(0.6, 0.8)
702
  base_ndre = np.random.uniform(0.4, 0.6)
703
- base_lai = np.random.uniform(3.0, 4.5)
704
 
705
  # Add daily variations
706
  ndvi_values = base_ndvi + np.random.normal(0, 0.05, size=len(dates))
707
  ndre_values = base_ndre + np.random.normal(0, 0.04, size=len(dates))
708
- lai_values = base_lai + np.random.normal(0, 0.3, size=len(dates))
709
 
710
  # Add trend
711
  trend = np.linspace(0, 0.15, len(dates))
712
  ndvi_values += trend
713
  ndre_values += trend * 0.8
714
- lai_values += trend * 2
715
 
716
  # Clip to valid ranges
717
  ndvi_values = np.clip(ndvi_values, 0, 1)
718
  ndre_values = np.clip(ndre_values, 0, 1)
719
- lai_values = np.clip(lai_values, 0, 5)
 
 
720
 
721
  # Create dataframe
722
  farm_ts_data = pd.DataFrame({
723
- 'Date': dates,
724
  'NDVI': ndvi_values,
725
  'NDRE': ndre_values,
726
  'LAI': lai_values
727
  })
728
 
729
  # Display charts
730
- st.markdown("### Vegetation Indices Over Time")
 
 
731
 
732
  # Create line chart for all indices
733
  vi_fig = create_line_chart(
734
- farm_ts_data, 'Date', ['NDVI', 'NDRE', 'LAI'],
735
- f'Vegetation Indices for {selected_farm}'
736
  )
737
  st.plotly_chart(vi_fig, use_container_width=True)
738
 
739
  # Display individual metrics with small charts
740
- st.markdown("### Current Vegetation Status")
 
 
 
741
  vi_cols = st.columns(3)
742
 
743
  with vi_cols[0]:
@@ -752,11 +1299,15 @@ def main():
752
 
753
  with vi_cols[2]:
754
  current_lai = lai_values[-1]
755
- lai_gauge = create_gauge_chart(current_lai, "LAI", 0, 5)
756
  st.plotly_chart(lai_gauge, use_container_width=True)
 
 
757
 
758
  # Growth Trends tab
759
  with analysis_tabs[1]:
 
 
760
  # Generate sample growth data
761
  growth_dates = pd.date_range(start=start_date, end=end_date, freq='D')
762
 
@@ -773,32 +1324,37 @@ def main():
773
 
774
  # Create dataframe
775
  growth_data = pd.DataFrame({
776
- 'Date': growth_dates,
777
- 'Daily Growth': growth_values,
778
- 'Cumulative Growth': cumulative_growth
779
  })
780
 
781
  # Display charts
782
- st.markdown("### Growth Patterns")
 
 
783
 
784
- growth_tabs = st.tabs(["Daily Growth", "Cumulative Growth"])
785
 
786
  with growth_tabs[0]:
787
  daily_fig = create_bar_chart(
788
- growth_data, 'Date', 'Daily Growth',
789
- f'Daily Growth Rate for {selected_farm}'
790
  )
791
  st.plotly_chart(daily_fig, use_container_width=True)
792
 
793
  with growth_tabs[1]:
794
  cumulative_fig = create_area_chart(
795
- growth_data, 'Date', 'Cumulative Growth',
796
- f'Cumulative Growth for {selected_farm}'
797
  )
798
  st.plotly_chart(cumulative_fig, use_container_width=True)
799
 
800
  # Growth statistics
801
- st.markdown("### Growth Statistics")
 
 
 
802
  stats_cols = st.columns(3)
803
 
804
  with stats_cols[0]:
@@ -806,7 +1362,7 @@ def main():
806
  st.markdown(f"""
807
  <div class="metric-card">
808
  <div class="metric-value">{avg_growth:.3f}</div>
809
- <div class="metric-label">Average Daily Growth</div>
810
  </div>
811
  """, unsafe_allow_html=True)
812
 
@@ -816,7 +1372,7 @@ def main():
816
  st.markdown(f"""
817
  <div class="metric-card">
818
  <div class="metric-value">{max_growth:.3f}</div>
819
- <div class="metric-label">Max Growth ({max_day})</div>
820
  </div>
821
  """, unsafe_allow_html=True)
822
 
@@ -825,12 +1381,16 @@ def main():
825
  st.markdown(f"""
826
  <div class="metric-card">
827
  <div class="metric-value">{total_growth:.2f}</div>
828
- <div class="metric-label">Total Growth</div>
829
  </div>
830
  """, unsafe_allow_html=True)
 
 
831
 
832
  # Environmental Factors tab
833
  with analysis_tabs[2]:
 
 
834
  # Generate sample environmental data
835
  env_dates = pd.date_range(start=start_date, end=end_date, freq='D')
836
 
@@ -850,52 +1410,56 @@ def main():
850
 
851
  # Create dataframe
852
  env_data = pd.DataFrame({
853
- 'Date': env_dates,
854
- 'Temperature (°C)': temp_values,
855
- 'Humidity (%)': humidity_values,
856
- 'Rainfall (mm)': rainfall_values
857
  })
858
 
859
  # Display charts
860
- st.markdown("### Environmental Conditions")
 
 
861
 
862
- env_tabs = st.tabs(["Temperature", "Humidity", "Rainfall"])
863
 
864
  with env_tabs[0]:
865
  temp_fig = create_line_chart(
866
- env_data, 'Date', ['Temperature (°C)'],
867
- f'Temperature for {selected_farm}'
868
  )
869
  st.plotly_chart(temp_fig, use_container_width=True)
870
 
871
  with env_tabs[1]:
872
  humidity_fig = create_line_chart(
873
- env_data, 'Date', ['Humidity (%)'],
874
- f'Humidity for {selected_farm}'
875
  )
876
  st.plotly_chart(humidity_fig, use_container_width=True)
877
 
878
  with env_tabs[2]:
879
  rainfall_fig = create_bar_chart(
880
- env_data, 'Date', 'Rainfall (mm)',
881
- f'Rainfall for {selected_farm}'
882
  )
883
  st.plotly_chart(rainfall_fig, use_container_width=True)
884
 
885
  # Correlation analysis
886
- st.markdown("### Environmental Impact on Growth")
 
 
887
 
888
  # Merge environmental and growth data
889
  merged_data = pd.merge(
890
  env_data,
891
- growth_data[['Date', 'Daily Growth']],
892
- on='Date'
893
  )
894
 
895
  # Calculate correlations
896
- corr_temp = np.corrcoef(merged_data['Temperature (°C)'], merged_data['Daily Growth'])[0, 1]
897
- corr_humidity = np.corrcoef(merged_data['Humidity (%)'], merged_data['Daily Growth'])[0, 1]
898
- corr_rainfall = np.corrcoef(merged_data['Rainfall (mm)'], merged_data['Daily Growth'])[0, 1]
899
 
900
  # Display correlation metrics
901
  corr_cols = st.columns(3)
@@ -904,7 +1468,7 @@ def main():
904
  st.markdown(f"""
905
  <div class="metric-card">
906
  <div class="metric-value">{corr_temp:.2f}</div>
907
- <div class="metric-label">Temperature Correlation</div>
908
  </div>
909
  """, unsafe_allow_html=True)
910
 
@@ -912,7 +1476,7 @@ def main():
912
  st.markdown(f"""
913
  <div class="metric-card">
914
  <div class="metric-value">{corr_humidity:.2f}</div>
915
- <div class="metric-label">Humidity Correlation</div>
916
  </div>
917
  """, unsafe_allow_html=True)
918
 
@@ -920,16 +1484,24 @@ def main():
920
  st.markdown(f"""
921
  <div class="metric-card">
922
  <div class="metric-value">{corr_rainfall:.2f}</div>
923
- <div class="metric-label">Rainfall Correlation</div>
924
  </div>
925
  """, unsafe_allow_html=True)
 
 
926
 
927
  # Growth Prediction Tab
928
  with tabs[2]:
929
- st.markdown("## Growth Prediction with LSTM")
 
 
 
 
930
 
931
  # Farm selection for prediction
932
- pred_farm = st.selectbox("Select Farm for Prediction", selected_farms['farm_name'].tolist(), key="pred_farm")
 
 
933
 
934
  # Generate sample historical data for LSTM
935
  hist_dates = pd.date_range(start=start_date - timedelta(days=60), end=end_date, freq='D')
@@ -956,57 +1528,67 @@ def main():
956
  rainfall[rain_days] = np.random.exponential(5, size=len(rain_days))
957
 
958
  # Age (constant for a farm)
959
- farm_age = selected_farms[selected_farms['farm_name'] == pred_farm]['age'].values[0]
960
  age = np.ones(len(hist_dates)) * farm_age
961
 
962
  # Variety (encoded as 0, 1, 2)
963
- variety_map = {'Type A': 0, 'Type B': 1, 'Type C': 2}
964
- farm_variety = selected_farms[selected_farms['farm_name'] == pred_farm]['variety'].values[0]
965
  variety_encoded = np.ones(len(hist_dates)) * variety_map.get(farm_variety, 0)
966
 
967
  # Target variables with relationship to features
968
- # LAI increases with rainfall and temperature within optimal range
969
- lai_base = 0.01 * temp + 0.02 * rainfall - 0.005 * np.abs(temp - 28)
970
- lai = 3 + lai_base + 0.05 * np.arange(len(hist_dates)) + np.random.normal(0, 0.2, len(hist_dates))
971
- lai = np.clip(lai, 0, 7)
972
-
973
- # NDVI follows similar pattern but scaled
974
- ndvi = 0.5 + 0.07 * lai / 7 + np.random.normal(0, 0.03, len(hist_dates))
975
  ndvi = np.clip(ndvi, 0, 1)
976
 
977
  # NDRE correlated with NDVI but lower values
978
  ndre = 0.7 * ndvi + np.random.normal(0, 0.02, len(hist_dates))
979
  ndre = np.clip(ndre, 0, 1)
980
 
 
 
 
981
  # Create dataframe
982
  hist_data = pd.DataFrame({
983
- 'Date': hist_dates,
984
- 'Temperature': temp,
985
- 'Humidity': humidity,
986
- 'Rainfall': rainfall,
987
- 'Age': age,
988
- 'Variety': variety_encoded,
989
  'LAI': lai,
990
  'NDVI': ndvi,
991
  'NDRE': ndre
992
  })
993
 
994
  # Display historical data
995
- st.markdown("### Historical Data")
 
 
 
 
 
996
  hist_fig = create_line_chart(
997
- hist_data, 'Date', ['LAI', 'NDVI', 'NDRE'],
998
- f'Historical Vegetation Indices for {pred_farm}'
999
  )
1000
  st.plotly_chart(hist_fig, use_container_width=True)
1001
 
 
 
1002
  # LSTM Model Training
1003
- st.markdown("### LSTM Model Training")
 
 
 
 
1004
 
1005
  # Button to trigger training
1006
- if st.button("Train LSTM Model and Predict"):
1007
- with st.spinner("Training LSTM model..."):
1008
  # Prepare data for LSTM
1009
- features = hist_data[['Temperature', 'Humidity', 'Rainfall', 'Age', 'Variety']].values
1010
  targets = hist_data[['LAI', 'NDVI', 'NDRE']].values
1011
 
1012
  # Combine features and targets
@@ -1026,7 +1608,7 @@ def main():
1026
 
1027
  # Create prediction dataframe
1028
  pred_data = pd.DataFrame({
1029
- 'Date': pred_dates,
1030
  'LAI': predictions[:, 0],
1031
  'NDVI': predictions[:, 1],
1032
  'NDRE': predictions[:, 2]
@@ -1034,35 +1616,37 @@ def main():
1034
 
1035
  # Combine historical and prediction data
1036
  combined_data = pd.concat([
1037
- hist_data[['Date', 'LAI', 'NDVI', 'NDRE']].tail(30), # Last 30 days of historical data
1038
  pred_data
1039
  ])
1040
 
1041
  # Display prediction results
1042
- st.markdown("### Prediction Results")
 
 
1043
 
1044
  # Create line chart with historical and predicted values
1045
  pred_fig = go.Figure()
1046
 
1047
  # Historical data (solid lines)
1048
- for col, color in zip(['LAI', 'NDVI', 'NDRE'], ['#1f77b4', '#ff7f0e', '#2ca02c']):
1049
- hist_part = combined_data[combined_data['Date'] <= end_date]
1050
  pred_fig.add_trace(go.Scatter(
1051
- x=hist_part['Date'],
1052
  y=hist_part[col],
1053
  mode='lines',
1054
- name=f'Historical {col}',
1055
  line=dict(color=color)
1056
  ))
1057
 
1058
  # Predicted data (dashed lines)
1059
- for col, color in zip(['LAI', 'NDVI', 'NDRE'], ['#1f77b4', '#ff7f0e', '#2ca02c']):
1060
- pred_part = combined_data[combined_data['Date'] > end_date]
1061
  pred_fig.add_trace(go.Scatter(
1062
- x=pred_part['Date'],
1063
  y=pred_part[col],
1064
  mode='lines+markers',
1065
- name=f'Predicted {col}',
1066
  line=dict(color=color, dash='dash'),
1067
  marker=dict(size=8)
1068
  ))
@@ -1073,14 +1657,14 @@ def main():
1073
  line_width=2,
1074
  line_dash="dash",
1075
  line_color="red",
1076
- annotation_text="Prediction Start",
1077
  annotation_position="top right"
1078
  )
1079
 
1080
  pred_fig.update_layout(
1081
- title=f'7-Day Growth Prediction for {pred_farm}',
1082
- xaxis_title='Date',
1083
- yaxis_title='Value',
1084
  legend=dict(
1085
  orientation="h",
1086
  yanchor="bottom",
@@ -1089,7 +1673,7 @@ def main():
1089
  x=1
1090
  ),
1091
  template="plotly_dark",
1092
- plot_bgcolor='rgba(30, 33, 48, 0.8)',
1093
  paper_bgcolor='rgba(0,0,0,0)',
1094
  font=dict(color='white'),
1095
  height=500
@@ -1098,7 +1682,10 @@ def main():
1098
  st.plotly_chart(pred_fig, use_container_width=True)
1099
 
1100
  # Display prediction table
1101
- st.markdown("### Predicted Values")
 
 
 
1102
  st.dataframe(
1103
  pred_data.style.background_gradient(
1104
  cmap='Greens', subset=['LAI', 'NDVI', 'NDRE']
@@ -1107,7 +1694,9 @@ def main():
1107
  )
1108
 
1109
  # Growth summary
1110
- st.markdown("### Growth Summary")
 
 
1111
 
1112
  # Calculate growth rates
1113
  lai_growth = (pred_data['LAI'].iloc[-1] - hist_data['LAI'].iloc[-1]) / hist_data['LAI'].iloc[-1] * 100
@@ -1120,7 +1709,7 @@ def main():
1120
  st.markdown(f"""
1121
  <div class="metric-card">
1122
  <div class="metric-value">{lai_growth:.1f}%</div>
1123
- <div class="metric-label">LAI Growth (7 days)</div>
1124
  </div>
1125
  """, unsafe_allow_html=True)
1126
 
@@ -1128,7 +1717,7 @@ def main():
1128
  st.markdown(f"""
1129
  <div class="metric-card">
1130
  <div class="metric-value">{ndvi_growth:.1f}%</div>
1131
- <div class="metric-label">NDVI Growth (7 days)</div>
1132
  </div>
1133
  """, unsafe_allow_html=True)
1134
 
@@ -1136,22 +1725,31 @@ def main():
1136
  st.markdown(f"""
1137
  <div class="metric-card">
1138
  <div class="metric-value">{ndre_growth:.1f}%</div>
1139
- <div class="metric-label">NDRE Growth (7 days)</div>
1140
  </div>
1141
  """, unsafe_allow_html=True)
1142
  else:
1143
- st.info("Click the button above to train the LSTM model and generate predictions.")
 
 
1144
 
1145
  # Data Tables Tab
1146
  with tabs[3]:
1147
- st.markdown("## Farm Data Tables")
 
 
 
 
1148
 
1149
  # Create tabs for different data tables
1150
- data_tabs = st.tabs(["Farm Information", "Vegetation Indices", "Growth Rankings"])
1151
 
1152
  # Farm Information tab
1153
  with data_tabs[0]:
1154
- st.markdown("### Farm Details")
 
 
 
1155
  st.dataframe(
1156
  selected_farms,
1157
  use_container_width=True
@@ -1159,26 +1757,30 @@ def main():
1159
 
1160
  # Vegetation Indices tab
1161
  with data_tabs[1]:
1162
- st.markdown("### Vegetation Indices by Farm")
 
 
1163
 
1164
  # Generate sample vegetation indices for all farms
1165
  vi_data = []
1166
 
1167
  for _, farm in selected_farms.iterrows():
1168
  # Generate random indices with some correlation to farm age
1169
- farm_age = farm['age']
1170
  base_ndvi = min(0.5 + 0.08 * farm_age + np.random.normal(0, 0.05), 0.95)
1171
  base_ndre = base_ndvi * 0.7 + np.random.normal(0, 0.03)
1172
- base_lai = base_ndvi * 6 + np.random.normal(0, 0.3)
 
 
1173
 
1174
  vi_data.append({
1175
- 'Farm': farm['farm_name'],
1176
- 'Age': farm_age,
1177
- 'Variety': farm['variety'],
1178
  'NDVI': round(base_ndvi, 2),
1179
  'NDRE': round(base_ndre, 2),
1180
  'LAI': round(base_lai, 1),
1181
- 'Chlorophyll Index': round(base_ndvi * 4 + np.random.normal(0, 0.2), 1)
1182
  })
1183
 
1184
  vi_df = pd.DataFrame(vi_data)
@@ -1186,16 +1788,18 @@ def main():
1186
  # Display the data
1187
  st.dataframe(
1188
  vi_df.style.background_gradient(
1189
- cmap='Greens', subset=['NDVI', 'NDRE', 'LAI', 'Chlorophyll Index']
1190
  ),
1191
  use_container_width=True
1192
  )
1193
 
1194
  # Growth Rankings tab
1195
  with data_tabs[2]:
1196
- st.markdown("### Farm Growth Rankings")
 
 
1197
 
1198
- # Generate sample growth data for all farms
1199
  growth_data = []
1200
 
1201
  for _, farm in selected_farms.iterrows():
@@ -1203,25 +1807,25 @@ def main():
1203
  base_growth = np.random.uniform(2.0, 5.0)
1204
 
1205
  # Adjust based on farm age (younger farms grow faster)
1206
- age_factor = max(1.0, 6 - farm['age']) / 3
1207
 
1208
  growth_data.append({
1209
- 'Farm': farm['farm_name'],
1210
- 'Age': farm['age'],
1211
- 'Variety': farm['variety'],
1212
- 'Growth Rate (%)': round(base_growth * age_factor, 1),
1213
- 'Health Index': round(np.random.uniform(0.7, 1.0), 2),
1214
- 'Stress Level': round(np.random.uniform(0.0, 0.3), 2)
1215
  })
1216
 
1217
- growth_df = pd.DataFrame(growth_data).sort_values('Growth Rate (%)', ascending=False)
1218
 
1219
  # Display the data
1220
  st.dataframe(
1221
  growth_df.style.background_gradient(
1222
- cmap='Greens', subset=['Growth Rate (%)', 'Health Index']
1223
  ).background_gradient(
1224
- cmap='Reds_r', subset=['Stress Level']
1225
  ),
1226
  use_container_width=True
1227
  )
@@ -1230,34 +1834,41 @@ def main():
1230
  col1, col2 = st.columns(2)
1231
 
1232
  with col1:
1233
- st.markdown("### Top Performers")
 
 
 
1234
  top_farms = growth_df.head(3)
1235
 
1236
  for i, (_, farm) in enumerate(top_farms.iterrows()):
1237
  st.markdown(f"""
1238
  <div class="metric-card" style="margin-bottom: 10px;">
1239
- <h4>#{i+1}: {farm['Farm']}</h4>
1240
- <div class="metric-value">{farm['Growth Rate (%)']}%</div>
1241
- <div class="metric-label">Growth Rate</div>
1242
- <p>Health Index: {farm['Health Index']} | Stress: {farm['Stress Level']}</p>
1243
  </div>
1244
  """, unsafe_allow_html=True)
1245
 
1246
  with col2:
1247
- st.markdown("### Needs Attention")
 
 
 
1248
  bottom_farms = growth_df.tail(3).iloc[::-1]
1249
 
1250
  for i, (_, farm) in enumerate(bottom_farms.iterrows()):
1251
  st.markdown(f"""
1252
  <div class="metric-card" style="margin-bottom: 10px; border-color: #ff6b6b;">
1253
- <h4>#{i+1}: {farm['Farm']}</h4>
1254
- <div class="metric-value" style="color: #ff6b6b;">{farm['Growth Rate (%)']}%</div>
1255
- <div class="metric-label">Growth Rate</div>
1256
- <p>Health Index: {farm['Health Index']} | Stress: {farm['Stress Level']}</p>
1257
  </div>
1258
  """, unsafe_allow_html=True)
 
 
1259
 
1260
  # Run the app
1261
  if __name__ == "__main__":
1262
- main()
1263
-
 
18
  import json
19
  from PIL import Image
20
  import time
21
+ import base64
22
+ from io import BytesIO
23
 
24
  # Set page configuration
25
  st.set_page_config(
26
+ page_title="سامانه هوشمند مانیتورینگ مزارع نیشکر",
27
  page_icon="🌱",
28
  layout="wide",
29
  initial_sidebar_state="collapsed"
 
32
  # Custom CSS for styling
33
  st.markdown("""
34
  <style>
35
+ @font-face {
36
+ font-family: 'Vazir';
37
+ src: url('https://cdn.jsdelivr.net/gh/rastikerdar/[email protected]/dist/Vazir.woff2') format('woff2');
38
+ font-weight: normal;
39
+ font-style: normal;
40
+ }
41
+
42
+ * {
43
+ font-family: 'Vazir', sans-serif !important;
44
+ }
45
+
46
  .main {
47
+ background-color: #0F1923;
48
  color: white;
49
+ direction: rtl;
50
+ text-align: right;
51
  }
52
+
53
  .stApp {
54
+ background-image: linear-gradient(to bottom, #0F1923, #1A2634);
55
  }
56
+
57
  .css-18e3th9 {
58
  padding-top: 0;
59
  }
60
+
61
  .css-1d391kg {
62
  padding-top: 1rem;
63
  }
64
+
65
  .stButton>button {
66
  background-color: #2EC4B6;
67
  color: white;
 
70
  padding: 10px 20px;
71
  transition: all 0.3s;
72
  }
73
+
74
  .stButton>button:hover {
75
  background-color: #1A9E8F;
76
  transform: scale(1.05);
77
  }
78
+
79
  .metric-card {
80
+ background-color: rgba(15, 25, 35, 0.8);
81
  border-radius: 10px;
82
  padding: 15px;
83
  border: 1px solid #2EC4B6;
84
  text-align: center;
85
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
86
+ transition: all 0.3s ease;
87
+ }
88
+
89
+ .metric-card:hover {
90
+ transform: translateY(-5px);
91
+ box-shadow: 0 8px 15px rgba(0, 0, 0, 0.2);
92
  }
93
+
94
  .metric-value {
95
+ font-size: 28px;
96
  font-weight: bold;
97
  color: #2EC4B6;
98
+ text-shadow: 0 0 10px rgba(46, 196, 182, 0.5);
99
  }
100
+
101
  .metric-label {
102
+ font-size: 16px;
103
  color: #CCC;
104
+ margin-top: 5px;
105
  }
106
+
107
  .chart-container {
108
+ background-color: rgba(15, 25, 35, 0.8);
109
  border-radius: 10px;
110
  padding: 15px;
111
  border: 1px solid #2EC4B6;
112
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
113
  }
114
+
115
  .table-container {
116
+ background-color: rgba(15, 25, 35, 0.8);
117
  border-radius: 10px;
118
  padding: 15px;
119
  border: 1px solid #2EC4B6;
120
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
121
  }
122
+
123
  .compass {
124
  position: relative;
125
  width: 200px;
126
  height: 200px;
127
  margin: 0 auto;
128
+ animation: rotate 60s linear infinite;
129
  }
130
+
131
+ @keyframes rotate {
132
+ from { transform: rotate(0deg); }
133
+ to { transform: rotate(360deg); }
134
+ }
135
+
136
  .compass-img {
137
  width: 100%;
138
  height: 100%;
139
  }
140
+
141
  .stDataFrame {
142
+ background-color: rgba(15, 25, 35, 0.8) !important;
143
  }
144
+
145
  div[data-testid="stDataFrameResizable"] {
146
+ background-color: rgba(15, 25, 35, 0.8) !important;
147
  }
148
+
149
  div[data-testid="stDataFrameResizable"] > div {
150
+ background-color: rgba(15, 25, 35, 0.8) !important;
151
  }
152
+
153
  div[data-testid="stDataFrameResizable"] td {
154
+ background-color: rgba(15, 25, 35, 0.8) !important;
155
  color: white !important;
156
  }
157
+
158
  div[data-testid="stDataFrameResizable"] th {
159
  background-color: rgba(46, 196, 182, 0.3) !important;
160
  color: white !important;
161
  }
162
+
163
  .stTabs [data-baseweb="tab-list"] {
164
  gap: 10px;
165
  }
166
+
167
  .stTabs [data-baseweb="tab"] {
168
+ background-color: rgba(15, 25, 35, 0.8);
169
  border-radius: 4px 4px 0px 0px;
170
  padding: 10px 20px;
171
  color: white;
172
  }
173
+
174
  .stTabs [aria-selected="true"] {
175
  background-color: #2EC4B6 !important;
176
  color: white !important;
177
  }
178
+
179
  .gauge-chart {
180
  margin: 0 auto;
181
  width: 200px;
182
  height: 200px;
183
  }
184
+
185
  @keyframes pulse {
186
  0% {
187
  box-shadow: 0 0 0 0 rgba(46, 196, 182, 0.7);
 
193
  box-shadow: 0 0 0 0 rgba(46, 196, 182, 0);
194
  }
195
  }
196
+
197
  .pulsing {
198
  animation: pulse 2s infinite;
199
  }
200
+
201
  .logo-container {
202
  text-align: center;
203
  margin-bottom: 20px;
204
  }
205
+
206
  .logo-text {
207
  font-size: 24px;
208
  font-weight: bold;
209
  color: #2EC4B6;
210
  }
211
+
212
+ .dashboard-container {
213
+ background-color: rgba(15, 25, 35, 0.7);
214
+ border-radius: 15px;
215
+ padding: 20px;
216
+ border: 1px solid #2EC4B6;
217
+ box-shadow: 0 0 20px rgba(46, 196, 182, 0.2);
218
+ margin-bottom: 20px;
219
+ }
220
+
221
+ .dashboard-header {
222
+ display: flex;
223
+ justify-content: space-between;
224
+ align-items: center;
225
+ margin-bottom: 20px;
226
+ border-bottom: 1px solid rgba(46, 196, 182, 0.3);
227
+ padding-bottom: 10px;
228
+ }
229
+
230
+ .dashboard-title {
231
+ color: #2EC4B6;
232
+ font-size: 24px;
233
+ font-weight: bold;
234
+ margin: 0;
235
+ }
236
+
237
+ .dashboard-subtitle {
238
+ color: #CCC;
239
+ font-size: 14px;
240
+ margin: 0;
241
+ }
242
+
243
+ .circular-button {
244
+ width: 60px;
245
+ height: 60px;
246
+ border-radius: 50%;
247
+ background-color: rgba(46, 196, 182, 0.2);
248
+ display: flex;
249
+ justify-content: center;
250
+ align-items: center;
251
+ color: #2EC4B6;
252
+ font-size: 24px;
253
+ border: 2px solid #2EC4B6;
254
+ cursor: pointer;
255
+ transition: all 0.3s;
256
+ margin: 0 auto;
257
+ box-shadow: 0 0 10px rgba(46, 196, 182, 0.5);
258
+ }
259
+
260
+ .circular-button:hover {
261
+ background-color: rgba(46, 196, 182, 0.4);
262
+ transform: scale(1.1);
263
+ }
264
+
265
+ .circular-value {
266
+ font-size: 18px;
267
+ font-weight: bold;
268
+ color: #2EC4B6;
269
+ text-align: center;
270
+ margin-top: 10px;
271
+ }
272
+
273
+ .circular-label {
274
+ font-size: 14px;
275
+ color: #CCC;
276
+ text-align: center;
277
+ }
278
+
279
+ .blinking {
280
+ animation: blink 1.5s infinite;
281
+ }
282
+
283
+ @keyframes blink {
284
+ 0% { opacity: 1; }
285
+ 50% { opacity: 0.3; }
286
+ 100% { opacity: 1; }
287
+ }
288
+
289
+ .rotating {
290
+ animation: rotate 10s linear infinite;
291
+ }
292
+
293
+ .glowing {
294
+ box-shadow: 0 0 10px #2EC4B6, 0 0 20px #2EC4B6, 0 0 30px #2EC4B6;
295
+ animation: glow 2s infinite alternate;
296
+ }
297
+
298
+ @keyframes glow {
299
+ from { box-shadow: 0 0 10px #2EC4B6, 0 0 20px #2EC4B6; }
300
+ to { box-shadow: 0 0 20px #2EC4B6, 0 0 30px #2EC4B6, 0 0 40px #2EC4B6; }
301
+ }
302
+
303
+ .stSelectbox [data-baseweb=select] {
304
+ background-color: rgba(15, 25, 35, 0.8);
305
+ border-color: #2EC4B6;
306
+ }
307
+
308
+ .stSelectbox [data-baseweb=select] > div {
309
+ background-color: rgba(15, 25, 35, 0.8);
310
+ color: white;
311
+ }
312
+
313
+ .stDateInput > div > div {
314
+ background-color: rgba(15, 25, 35, 0.8);
315
+ border-color: #2EC4B6;
316
+ color: white;
317
+ }
318
+
319
+ .stDateInput input {
320
+ color: white;
321
+ }
322
+
323
+ /* Custom scrollbar */
324
+ ::-webkit-scrollbar {
325
+ width: 8px;
326
+ height: 8px;
327
+ }
328
+
329
+ ::-webkit-scrollbar-track {
330
+ background: rgba(15, 25, 35, 0.8);
331
+ }
332
+
333
+ ::-webkit-scrollbar-thumb {
334
+ background: #2EC4B6;
335
+ border-radius: 4px;
336
+ }
337
+
338
+ ::-webkit-scrollbar-thumb:hover {
339
+ background: #1A9E8F;
340
+ }
341
+
342
+ /* Animated buttons */
343
+ .animated-button {
344
+ background-color: rgba(46, 196, 182, 0.2);
345
+ color: #2EC4B6;
346
+ border: 2px solid #2EC4B6;
347
+ border-radius: 10px;
348
+ padding: 10px 15px;
349
+ margin: 5px;
350
+ transition: all 0.3s;
351
+ cursor: pointer;
352
+ text-align: center;
353
+ font-weight: bold;
354
+ box-shadow: 0 0 10px rgba(46, 196, 182, 0.3);
355
+ }
356
+
357
+ .animated-button:hover {
358
+ background-color: rgba(46, 196, 182, 0.4);
359
+ transform: translateY(-3px);
360
+ box-shadow: 0 5px 15px rgba(46, 196, 182, 0.5);
361
+ }
362
+
363
+ /* Tooltip styling */
364
+ .tooltip {
365
+ position: relative;
366
+ display: inline-block;
367
+ }
368
+
369
+ .tooltip .tooltiptext {
370
+ visibility: hidden;
371
+ width: 120px;
372
+ background-color: rgba(15, 25, 35, 0.9);
373
+ color: #fff;
374
+ text-align: center;
375
+ border-radius: 6px;
376
+ padding: 5px;
377
+ position: absolute;
378
+ z-index: 1;
379
+ bottom: 125%;
380
+ left: 50%;
381
+ margin-left: -60px;
382
+ opacity: 0;
383
+ transition: opacity 0.3s;
384
+ border: 1px solid #2EC4B6;
385
+ }
386
+
387
+ .tooltip:hover .tooltiptext {
388
+ visibility: visible;
389
+ opacity: 1;
390
+ }
391
+
392
+ /* Progress bar styling */
393
+ .stProgress > div > div > div {
394
+ background-color: #2EC4B6;
395
+ }
396
+
397
+ /* Header styling */
398
+ .header-container {
399
+ display: flex;
400
+ justify-content: space-between;
401
+ align-items: center;
402
+ padding: 10px 20px;
403
+ background-color: rgba(15, 25, 35, 0.8);
404
+ border-radius: 10px;
405
+ margin-bottom: 20px;
406
+ border-bottom: 2px solid #2EC4B6;
407
+ }
408
+
409
+ .header-title {
410
+ color: #2EC4B6;
411
+ font-size: 28px;
412
+ font-weight: bold;
413
+ margin: 0;
414
+ text-shadow: 0 0 10px rgba(46, 196, 182, 0.5);
415
+ }
416
+
417
+ .header-subtitle {
418
+ color: #CCC;
419
+ font-size: 16px;
420
+ margin: 0;
421
+ }
422
+
423
+ /* Card styling */
424
+ .card {
425
+ background-color: rgba(15, 25, 35, 0.8);
426
+ border-radius: 10px;
427
+ padding: 15px;
428
+ border: 1px solid #2EC4B6;
429
+ margin-bottom: 15px;
430
+ transition: all 0.3s;
431
+ }
432
+
433
+ .card:hover {
434
+ transform: translateY(-5px);
435
+ box-shadow: 0 5px 15px rgba(46, 196, 182, 0.3);
436
+ }
437
+
438
+ .card-title {
439
+ color: #2EC4B6;
440
+ font-size: 18px;
441
+ font-weight: bold;
442
+ margin-bottom: 10px;
443
+ border-bottom: 1px solid rgba(46, 196, 182, 0.3);
444
+ padding-bottom: 5px;
445
+ }
446
+
447
+ /* Weather icon styling */
448
+ .weather-icon {
449
+ font-size: 36px;
450
+ color: #2EC4B6;
451
+ text-align: center;
452
+ margin-bottom: 10px;
453
+ }
454
+
455
+ /* Farm status indicators */
456
+ .status-indicator {
457
+ width: 15px;
458
+ height: 15px;
459
+ border-radius: 50%;
460
+ display: inline-block;
461
+ margin-right: 5px;
462
+ }
463
+
464
+ .status-good {
465
+ background-color: #4CAF50;
466
+ box-shadow: 0 0 5px #4CAF50;
467
+ }
468
+
469
+ .status-warning {
470
+ background-color: #FFC107;
471
+ box-shadow: 0 0 5px #FFC107;
472
+ }
473
+
474
+ .status-alert {
475
+ background-color: #F44336;
476
+ box-shadow: 0 0 5px #F44336;
477
+ }
478
+
479
+ /* Custom radio buttons */
480
+ .stRadio [data-testid="stMarkdownContainer"] > div {
481
+ display: flex;
482
+ flex-direction: row;
483
+ }
484
+
485
+ .stRadio label {
486
+ background-color: rgba(15, 25, 35, 0.8);
487
+ border: 1px solid #2EC4B6;
488
+ border-radius: 5px;
489
+ padding: 10px 15px;
490
+ margin: 0 5px;
491
+ transition: all 0.3s;
492
+ }
493
+
494
+ .stRadio label:hover {
495
+ background-color: rgba(46, 196, 182, 0.2);
496
+ }
497
+
498
+ .stRadio label[data-checked="true"] {
499
+ background-color: #2EC4B6;
500
+ color: white;
501
+ }
502
+
503
+ /* Animated loading spinner */
504
+ @keyframes spinner {
505
+ 0% { transform: rotate(0deg); }
506
+ 100% { transform: rotate(360deg); }
507
+ }
508
+
509
+ .loading-spinner {
510
+ width: 40px;
511
+ height: 40px;
512
+ border: 4px solid rgba(46, 196, 182, 0.3);
513
+ border-radius: 50%;
514
+ border-top: 4px solid #2EC4B6;
515
+ animation: spinner 1s linear infinite;
516
+ margin: 20px auto;
517
+ }
518
+
519
+ /* Custom slider */
520
+ .stSlider [data-baseweb="slider"] {
521
+ margin-top: 10px;
522
+ }
523
+
524
+ .stSlider [data-baseweb="slider"] [data-testid="stThumbValue"] {
525
+ background-color: #2EC4B6;
526
+ color: white;
527
+ }
528
+
529
+ .stSlider [data-baseweb="slider"] [data-testid="stTrack"] > div {
530
+ background-color: #2EC4B6;
531
+ }
532
+
533
+ /* Responsive design adjustments */
534
+ @media (max-width: 768px) {
535
+ .metric-value {
536
+ font-size: 20px;
537
+ }
538
+
539
+ .metric-label {
540
+ font-size: 12px;
541
+ }
542
+
543
+ .dashboard-title {
544
+ font-size: 20px;
545
+ }
546
+ }
547
  </style>
548
  """, unsafe_allow_html=True)
549
 
 
561
  ee.Initialize(credentials)
562
  return True
563
  else:
564
+ st.error("فایل اعتبارنامه GEE یافت نشد. لطفاً فایل اعتبارنامه را آپلود کنید.")
565
  return False
566
 
567
  # Load farm coordinates
 
569
  def load_farm_data():
570
  try:
571
  farm_data = pd.read_csv('farm_coordinates.csv')
572
+ # Rename columns to Persian
573
+ farm_data.columns = ['نام مزرعه', 'سن', 'تنوع', 'طول جغرافیایی', 'عرض جغرافیایی']
574
  return farm_data
575
  except Exception as e:
576
+ st.error(f"خطا در بارگذاری مختصات مزارع: {e}")
577
  # Create sample data if file not found
578
  sample_data = pd.DataFrame({
579
+ 'نام مزرعه': [f'مزرعه_{i}' for i in range(1, 11)],
580
+ 'سن': np.random.randint(1, 6, 10),
581
+ 'تنوع': np.random.choice(['نوع A', 'نوع B', 'نوع C'], 10),
582
+ 'طول جغرافیایی': np.random.uniform(51.0, 51.5, 10),
583
+ 'عرض جغرافیایی': np.random.uniform(32.0, 32.5, 10)
584
  })
585
  return sample_data
586
 
 
589
  def load_day_schedule():
590
  try:
591
  schedule_data = pd.read_csv('پایگاه داده (1).csv')
592
+ # Ensure the column names are in Persian
593
+ if 'day' in schedule_data.columns and 'farm_name' in schedule_data.columns:
594
+ schedule_data.columns = ['روز', 'نام مزرعه']
595
  return schedule_data
596
  except Exception as e:
597
+ st.error(f"خطا در بارگذاری برنامه روزانه: {e}")
598
  # Create sample data if file not found
599
+ days = ['دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنج‌شنبه', 'جمعه', 'شنبه', 'یکشنبه']
600
  sample_data = pd.DataFrame({
601
+ 'روز': np.random.choice(days, 10),
602
+ 'نام مزرعه': [f'مزرعه_{i}' for i in range(1, 11)]
603
  })
604
  return sample_data
605
 
 
624
  # NDRE = (NIR - Red Edge) / (NIR + Red Edge)
625
  ndre = image.normalizedDifference(['B8', 'B5']).rename('NDRE')
626
 
627
+ # LAI calculation (more realistic model based on NDVI)
628
+ # LAI = 0.57 * exp(2.33 * NDVI)
629
+ lai = ndvi.expression(
630
+ '0.57 * exp(2.33 * NDVI)',
631
+ {'NDVI': ndvi}
632
+ ).rename('LAI')
 
 
 
 
633
 
634
  # Chlorophyll index
635
+ nir = image.select('B8')
636
+ red = image.select('B4')
637
  chlorophyll = image.expression(
638
  '(NIR / RED) - 1',
639
  {'NIR': nir, 'RED': red}
640
  ).rename('CHL')
641
 
642
  # Add all indices to the image
643
+ return image.addBands([ndvi, ndre, lai, chlorophyll])
644
 
645
  # Function to create LSTM model
646
  def create_lstm_model(input_shape):
647
  model = Sequential()
648
+ model.add(LSTM(64, return_sequences=True, input_shape=input_shape))
649
  model.add(Dropout(0.2))
650
+ model.add(LSTM(64, return_sequences=False))
651
  model.add(Dropout(0.2))
652
+ model.add(Dense(32))
653
  model.add(Dense(3)) # Predict LAI, NDVI, NDRE
654
 
655
  model.compile(optimizer='adam', loss='mse')
 
719
  gauge={
720
  'axis': {'range': [min_val, max_val], 'tickcolor': 'white'},
721
  'bar': {'color': "#2EC4B6"},
722
+ 'bgcolor': "rgba(15, 25, 35, 0.8)",
723
  'borderwidth': 2,
724
  'bordercolor': "#2EC4B6",
725
  'steps': [
 
744
  def create_line_chart(data, x_col, y_cols, title):
745
  fig = go.Figure()
746
 
747
+ colors = ['#2EC4B6', '#FF9F1C', '#E71D36', '#4361EE', '#7209B7']
748
+
749
+ for i, col in enumerate(y_cols):
750
  fig.add_trace(go.Scatter(
751
  x=data[x_col],
752
  y=data[col],
753
  mode='lines+markers',
754
  name=col,
755
+ line=dict(width=3, color=colors[i % len(colors)]),
756
+ marker=dict(size=8, color=colors[i % len(colors)])
757
  ))
758
 
759
  fig.update_layout(
760
  title=title,
761
+ xaxis_title='تاریخ',
762
+ yaxis_title='مقدار',
763
  legend=dict(
764
  orientation="h",
765
  yanchor="bottom",
 
768
  x=1
769
  ),
770
  template="plotly_dark",
771
+ plot_bgcolor='rgba(15, 25, 35, 0.8)',
772
  paper_bgcolor='rgba(0,0,0,0)',
773
  font=dict(color='white'),
774
  margin=dict(l=20, r=20, t=50, b=20),
 
784
  fig.add_trace(go.Bar(
785
  x=data[x_col],
786
  y=data[y_col],
787
+ marker_color='#2EC4B6',
788
+ marker_line_color='#1A9E8F',
789
+ marker_line_width=1.5,
790
+ opacity=0.8
791
  ))
792
 
793
  fig.update_layout(
 
795
  xaxis_title=x_col,
796
  yaxis_title=y_col,
797
  template="plotly_dark",
798
+ plot_bgcolor='rgba(15, 25, 35, 0.8)',
799
  paper_bgcolor='rgba(0,0,0,0)',
800
  font=dict(color='white'),
801
  margin=dict(l=20, r=20, t=50, b=20),
 
822
  xaxis_title=x_col,
823
  yaxis_title=y_col,
824
  template="plotly_dark",
825
+ plot_bgcolor='rgba(15, 25, 35, 0.8)',
826
  paper_bgcolor='rgba(0,0,0,0)',
827
  font=dict(color='white'),
828
  margin=dict(l=20, r=20, t=50, b=20),
 
831
 
832
  return fig
833
 
834
+ # Create a compass image
835
+ def get_compass_image():
836
+ # Create a base64 encoded compass image
837
+ compass_html = f"""
838
+ <div style="width:200px; height:200px; margin:0 auto; position:relative;">
839
+ <svg width="200" height="200" viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg" class="rotating">
840
+ <circle cx="100" cy="100" r="90" fill="rgba(15, 25, 35, 0.8)" stroke="#2EC4B6" stroke-width="2"/>
841
+ <circle cx="100" cy="100" r="5" fill="#2EC4B6"/>
842
+ <path d="M100,10 L100,30" stroke="#2EC4B6" stroke-width="2" transform="rotate(0 100 100)"/>
843
+ <path d="M100,10 L100,30" stroke="#2EC4B6" stroke-width="2" transform="rotate(90 100 100)"/>
844
+ <path d="M100,10 L100,30" stroke="#2EC4B6" stroke-width="2" transform="rotate(180 100 100)"/>
845
+ <path d="M100,10 L100,30" stroke="#2EC4B6" stroke-width="2" transform="rotate(270 100 100)"/>
846
+ <path d="M100,10 L100,20" stroke="#2EC4B6" stroke-width="1" transform="rotate(30 100 100)"/>
847
+ <path d="M100,10 L100,20" stroke="#2EC4B6" stroke-width="1" transform="rotate(60 100 100)"/>
848
+ <path d="M100,10 L100,20" stroke="#2EC4B6" stroke-width="1" transform="rotate(120 100 100)"/>
849
+ <path d="M100,10 L100,20" stroke="#2EC4B6" stroke-width="1" transform="rotate(150 100 100)"/>
850
+ <path d="M100,10 L100,20" stroke="#2EC4B6" stroke-width="1" transform="rotate(210 100 100)"/>
851
+ <path d="M100,10 L100,20" stroke="#2EC4B6" stroke-width="1" transform="rotate(240 100 100)"/>
852
+ <path d="M100,10 L100,20" stroke="#2EC4B6" stroke-width="1" transform="rotate(300 100 100)"/>
853
+ <path d="M100,10 L100,20" stroke="#2EC4B6" stroke-width="1" transform="rotate(330 100 100)"/>
854
+ <text x="100" y="45" text-anchor="middle" fill="#2EC4B6" font-size="12">N</text>
855
+ <text x="155" y="105" text-anchor="middle" fill="#2EC4B6" font-size="12">E</text>
856
+ <text x="100" y="165" text-anchor="middle" fill="#2EC4B6" font-size="12">S</text>
857
+ <text x="45" y="105" text-anchor="middle" fill="#2EC4B6" font-size="12">W</text>
858
+ <path d="M100,40 L90,90 L100,80 L110,90 Z" fill="#E71D36" stroke="#2EC4B6" stroke-width="1"/>
859
+ <path d="M100,160 L90,110 L100,120 L110,110 Z" fill="#2EC4B6" stroke="#2EC4B6" stroke-width="1"/>
860
+ </svg>
861
+ </div>
862
+ """
863
+ return compass_html
864
+
865
+ # Create a circular metric
866
+ def create_circular_metric(value, label, icon, min_val=0, max_val=100):
867
+ # Calculate percentage for the circular progress
868
+ percentage = min(max(value / max_val, 0), 1) * 100
869
+
870
+ # Create the HTML for the circular metric
871
+ html = f"""
872
+ <div style="text-align: center; margin-bottom: 20px;">
873
+ <div class="circular-button" style="background: conic-gradient(#2EC4B6 {percentage}%, rgba(15, 25, 35, 0.8) 0%);">
874
+ {icon}
875
+ </div>
876
+ <div class="circular-value">{value}</div>
877
+ <div class="circular-label">{label}</div>
878
+ </div>
879
+ """
880
+ return html
881
+
882
  # Main application
883
  def main():
884
  # Initialize Earth Engine
885
  ee_initialized = initialize_ee()
886
  if not ee_initialized:
887
+ st.warning("مقداردهی اولیه Earth Engine با مشکل مواجه شد. برخی از ویژگی‌ها ممکن است کار نکنند.")
888
 
889
  # Load data
890
  farm_data = load_farm_data()
891
  day_schedule = load_day_schedule()
892
 
893
  # Header with logo
894
+ st.markdown("""
895
+ <div class="header-container">
896
+ <div>
897
+ <h1 class="header-title">سامانه هوشمند مانیتورینگ مزارع نیشکر</h1>
898
+ <p class="header-subtitle">پایش و پیش‌بینی رشد با استفاده از تصاویر ماهواره‌ای و هوش مصنوعی</p>
899
+ </div>
900
+ <div style="text-align: center;">
901
  <div style="font-size: 40px; color: #2EC4B6;">✦</div>
902
+ <div style="font-size: 18px; font-weight: bold; color: #2EC4B6;">SMART<br>Sugarcane</div>
903
+ <div style="color: #CCC; font-size: 14px;">FARM</div>
904
  </div>
905
+ </div>
906
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
907
 
908
  # Date selection
909
  today = datetime.now()
910
  start_date = today - timedelta(days=30)
911
  end_date = today
912
 
913
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
914
+
915
+ col1, col2, col3 = st.columns([2, 1, 2])
916
  with col1:
917
+ start_date = st.date_input("تاریخ شروع", start_date)
918
  with col2:
919
+ # Day selection
920
+ days_persian = ['دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنج‌شنبه', 'جمعه', 'شنبه', 'یکشنبه']
921
+ selected_day = st.selectbox("انتخاب روز", days_persian)
922
+ with col3:
923
+ end_date = st.date_input("تاریخ پایان", end_date)
924
 
925
+ st.markdown('</div>', unsafe_allow_html=True)
 
 
926
 
927
  # Filter farms for selected day
928
+ day_farms = day_schedule[day_schedule['روز'] == selected_day]['نام مزرعه'].tolist()
929
+ selected_farms = farm_data[farm_data['نام مزرعه'].isin(day_farms)]
930
 
931
  if selected_farms.empty:
932
+ st.warning(f"هیچ مزرعه‌ای برای روز {selected_day} برنامه‌ریزی نشده است. لطفاً روز دیگری را انتخاب کنید.")
933
  else:
934
  # Create tabs for different sections
935
+ tabs = st.tabs(["داشبورد", "تحلیل مزرعه", "پیش‌بینی رشد", "جداول داده"])
936
 
937
  # Dashboard Tab
938
  with tabs[0]:
939
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
940
+
941
  # Main metrics row
942
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
943
+ st.markdown('<h2 class="dashboard-title">شاخص‌های فعلی مزارع</h2>', unsafe_allow_html=True)
944
+ st.markdown('</div>', unsafe_allow_html=True)
945
+
946
  metric_cols = st.columns(5)
947
 
948
  # Generate sample metrics for demonstration
 
956
  st.markdown(f"""
957
  <div class="metric-card">
958
  <div class="metric-value">{avg_ndvi}</div>
959
+ <div class="metric-label">میانگین NDVI</div>
960
  </div>
961
  """, unsafe_allow_html=True)
962
 
 
964
  st.markdown(f"""
965
  <div class="metric-card">
966
  <div class="metric-value">{avg_lai}</div>
967
+ <div class="metric-label">میانگین LAI</div>
968
  </div>
969
  """, unsafe_allow_html=True)
970
 
 
972
  st.markdown(f"""
973
  <div class="metric-card">
974
  <div class="metric-value">{avg_ndre}</div>
975
+ <div class="metric-label">میانگین NDRE</div>
976
  </div>
977
  """, unsafe_allow_html=True)
978
 
 
980
  st.markdown(f"""
981
  <div class="metric-card">
982
  <div class="metric-value">{avg_chl}</div>
983
+ <div class="metric-label">شاخص کلروفیل</div>
984
  </div>
985
  """, unsafe_allow_html=True)
986
 
 
988
  st.markdown(f"""
989
  <div class="metric-card pulsing">
990
  <div class="metric-value">{avg_growth}%</div>
991
+ <div class="metric-label">نرخ رشد</div>
992
  </div>
993
  """, unsafe_allow_html=True)
994
 
995
+ st.markdown('</div>', unsafe_allow_html=True)
996
+
997
  # Map and compass row
998
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
999
+
1000
  map_col, compass_col = st.columns([3, 1])
1001
 
1002
  with map_col:
1003
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1004
+ st.markdown('<h2 class="dashboard-title">نقشه مزارع (NDVI)</h2>', unsafe_allow_html=True)
1005
+ st.markdown('</div>', unsafe_allow_html=True)
1006
 
1007
  # Create a map centered at the mean of farm coordinates
1008
+ center_lat = selected_farms['عرض جغرافیایی'].mean()
1009
+ center_lon = selected_farms['طول جغرافیایی'].mean()
1010
 
1011
  m = geemap.Map(center=[center_lat, center_lon], zoom=12)
1012
 
 
1015
 
1016
  # Create features for farms
1017
  for _, farm in selected_farms.iterrows():
1018
+ point = ee.Geometry.Point([farm['طول جغرافیایی'], farm['عرض جغرافیایی']])
1019
  buffer = point.buffer(500) # 500m buffer around point
1020
 
1021
  # Get Sentinel imagery
 
1036
  'max': 1,
1037
  'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850']
1038
  }
1039
+ m.addLayer(image_with_indices.select('NDVI'), ndvi_vis, f'NDVI - {farm["نام مزرعه"]}')
1040
 
1041
  # Add farm marker with pulsing effect
1042
  folium.Marker(
1043
+ location=[farm['عرض جغرافیایی'], farm['طول جغرافیایی']],
1044
  popup=f"""
1045
+ <div style='width: 200px; direction: rtl; text-align: right;'>
1046
+ <h4>{farm['نام مزرعه']}</h4>
1047
+ <p>سن: {farm['سن']} سال</p>
1048
+ <p>تنوع: {farm['تنوع']}</p>
1049
  </div>
1050
  """,
1051
  icon=folium.Icon(color='green', icon='leaf')
 
1055
  folium_static(m)
1056
 
1057
  with compass_col:
1058
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1059
+ st.markdown('<h2 class="dashboard-title">ناوبری</h2>', unsafe_allow_html=True)
1060
+ st.markdown('</div>', unsafe_allow_html=True)
1061
+
1062
+ # Display compass
1063
+ st.markdown(get_compass_image(), unsafe_allow_html=True)
1064
 
1065
  # Add a gauge chart for overall farm health
1066
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1067
+ st.markdown('<h2 class="dashboard-title">سلامت مزرعه</h2>', unsafe_allow_html=True)
1068
+ st.markdown('</div>', unsafe_allow_html=True)
1069
+
1070
+ gauge_fig = create_gauge_chart(avg_growth/100, "شاخص سلامت", 0, 1)
1071
  st.plotly_chart(gauge_fig, use_container_width=True)
1072
 
1073
+ st.markdown('</div>', unsafe_allow_html=True)
1074
+
1075
  # Charts row
1076
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1077
+
1078
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1079
+ st.markdown('<h2 class="dashboard-title">تحلیل مزارع</h2>', unsafe_allow_html=True)
1080
+ st.markdown('</div>', unsafe_allow_html=True)
1081
+
1082
  chart_col1, chart_col2 = st.columns(2)
1083
 
1084
  with chart_col1:
 
1093
  ndvi_values = np.clip(ndvi_values, 0, 1)
1094
 
1095
  ts_data = pd.DataFrame({
1096
+ 'تاریخ': dates,
1097
  'NDVI': ndvi_values,
1098
  'NDRE': ndvi_values * 0.7 + np.random.normal(0, 0.05, size=len(dates)),
1099
  'LAI': ndvi_values * 5 + np.random.normal(0, 0.3, size=len(dates))
1100
  })
1101
 
1102
  # Create and display line chart
1103
+ line_fig = create_line_chart(ts_data, 'تاریخ', ['NDVI', 'NDRE'], 'شاخص‌های گیاهی در طول زمان')
1104
  st.plotly_chart(line_fig, use_container_width=True)
1105
 
1106
  with chart_col2:
1107
  # Create sample area chart data
1108
  area_data = pd.DataFrame({
1109
+ 'تاریخ': dates,
1110
+ 'رشد': np.cumsum(np.random.normal(0.1, 0.03, size=len(dates)))
1111
  })
1112
 
1113
  # Create and display area chart
1114
+ area_fig = create_area_chart(area_data, 'تاریخ', 'رشد', 'رشد تجمعی')
1115
  st.plotly_chart(area_fig, use_container_width=True)
1116
 
1117
+ st.markdown('</div>', unsafe_allow_html=True)
1118
+
1119
  # Farm ranking row
1120
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1121
+
1122
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1123
+ st.markdown('<h2 class="dashboard-title">رتبه‌بندی مزارع</h2>', unsafe_allow_html=True)
1124
+ st.markdown('</div>', unsafe_allow_html=True)
1125
+
1126
  rank_col1, rank_col2 = st.columns(2)
1127
 
1128
  with rank_col1:
1129
+ # Create sample farm ranking data for the last 7 days
1130
+ farm_names = selected_farms['نام مزرعه'].tolist()
1131
+
1132
+ # Calculate LAI for each farm based on realistic formula
1133
+ farm_lai_values = []
1134
+ for _, farm in selected_farms.iterrows():
1135
+ # Base NDVI with some randomness related to farm age
1136
+ base_ndvi = min(0.5 + 0.08 * farm['سن'] + np.random.normal(0, 0.05), 0.95)
1137
+ # LAI calculation: LAI = 0.57 * exp(2.33 * NDVI)
1138
+ lai_value = 0.57 * np.exp(2.33 * base_ndvi)
1139
+ farm_lai_values.append(round(lai_value, 2))
1140
 
1141
  rank_data = pd.DataFrame({
1142
+ 'مزرعه': farm_names,
1143
+ 'شاخص سطح برگ': farm_lai_values
1144
+ }).sort_values('شاخص سطح برگ', ascending=False)
1145
 
1146
  # Create and display bar chart
1147
+ bar_fig = create_bar_chart(rank_data, 'مزرعه', 'شاخص سطح برگ', 'مزارع بر اساس شاخص سطح برگ')
1148
  st.plotly_chart(bar_fig, use_container_width=True)
1149
 
1150
  with rank_col2:
 
1152
  st.markdown('<div class="table-container">', unsafe_allow_html=True)
1153
  st.dataframe(
1154
  rank_data.reset_index(drop=True).style.background_gradient(
1155
+ cmap='Greens', subset=['شاخص سطح برگ']
1156
  ),
1157
  use_container_width=True
1158
  )
1159
  st.markdown('</div>', unsafe_allow_html=True)
1160
+
1161
+ st.markdown('</div>', unsafe_allow_html=True)
1162
+
1163
+ # Circular metrics row
1164
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1165
+
1166
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1167
+ st.markdown('<h2 class="dashboard-title">شاخص‌های کلیدی</h2>', unsafe_allow_html=True)
1168
+ st.markdown('</div>', unsafe_allow_html=True)
1169
+
1170
+ circ_cols = st.columns(4)
1171
+
1172
+ with circ_cols[0]:
1173
+ st.markdown(create_circular_metric(
1174
+ round(np.random.uniform(20, 30), 1),
1175
+ "دما (°C)",
1176
+ "🌡️",
1177
+ 0,
1178
+ 50
1179
+ ), unsafe_allow_html=True)
1180
+
1181
+ with circ_cols[1]:
1182
+ st.markdown(create_circular_metric(
1183
+ round(np.random.uniform(40, 80), 1),
1184
+ "رطوبت (%)",
1185
+ "💧",
1186
+ 0,
1187
+ 100
1188
+ ), unsafe_allow_html=True)
1189
+
1190
+ with circ_cols[2]:
1191
+ st.markdown(create_circular_metric(
1192
+ round(np.random.uniform(3, 8), 1),
1193
+ "بریکس",
1194
+ "🍯",
1195
+ 0,
1196
+ 15
1197
+ ), unsafe_allow_html=True)
1198
+
1199
+ with circ_cols[3]:
1200
+ st.markdown(create_circular_metric(
1201
+ round(np.random.uniform(70, 95), 1),
1202
+ "خلوص (%)",
1203
+ "✨",
1204
+ 0,
1205
+ 100
1206
+ ), unsafe_allow_html=True)
1207
+
1208
+ st.markdown('</div>', unsafe_allow_html=True)
1209
 
1210
  # Farm Analysis Tab
1211
  with tabs[1]:
1212
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1213
+
1214
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1215
+ st.markdown('<h2 class="dashboard-title">تحلیل تفصیلی مزرعه</h2>', unsafe_allow_html=True)
1216
+ st.markdown('</div>', unsafe_allow_html=True)
1217
 
1218
  # Farm selection
1219
+ selected_farm = st.selectbox("انتخاب مزرعه برای تحلیل", selected_farms['نام مزرعه'].tolist())
1220
+ farm_row = selected_farms[selected_farms['نام مزرعه'] == selected_farm].iloc[0]
1221
 
1222
  # Farm details
1223
  st.markdown(f"""
1224
  <div class="metric-card" style="margin-bottom: 20px;">
1225
  <h3>{selected_farm}</h3>
1226
+ <p>سن: {farm_row['سن']} سال | تنوع: {farm_row['تنوع']}</p>
1227
+ <p>موقعیت: {farm_row['عرض جغرافیایی']:.4f}, {farm_row['طول جغرافیایی']:.4f}</p>
1228
  </div>
1229
  """, unsafe_allow_html=True)
1230
 
1231
+ st.markdown('</div>', unsafe_allow_html=True)
1232
+
1233
  # Create tabs for different analysis views
1234
+ analysis_tabs = st.tabs(["شاخص‌های گیاهی", "روند رشد", "عوامل محیطی"])
1235
 
1236
  # Vegetation Indices tab
1237
  with analysis_tabs[0]:
1238
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1239
+
1240
  # Generate sample time series data for the selected farm
1241
  dates = pd.date_range(start=start_date, end=end_date, freq='D')
1242
 
1243
  # Base values with some randomness
1244
  base_ndvi = np.random.uniform(0.6, 0.8)
1245
  base_ndre = np.random.uniform(0.4, 0.6)
 
1246
 
1247
  # Add daily variations
1248
  ndvi_values = base_ndvi + np.random.normal(0, 0.05, size=len(dates))
1249
  ndre_values = base_ndre + np.random.normal(0, 0.04, size=len(dates))
 
1250
 
1251
  # Add trend
1252
  trend = np.linspace(0, 0.15, len(dates))
1253
  ndvi_values += trend
1254
  ndre_values += trend * 0.8
 
1255
 
1256
  # Clip to valid ranges
1257
  ndvi_values = np.clip(ndvi_values, 0, 1)
1258
  ndre_values = np.clip(ndre_values, 0, 1)
1259
+
1260
+ # Calculate LAI using the realistic formula: LAI = 0.57 * exp(2.33 * NDVI)
1261
+ lai_values = 0.57 * np.exp(2.33 * ndvi_values)
1262
 
1263
  # Create dataframe
1264
  farm_ts_data = pd.DataFrame({
1265
+ 'تاریخ': dates,
1266
  'NDVI': ndvi_values,
1267
  'NDRE': ndre_values,
1268
  'LAI': lai_values
1269
  })
1270
 
1271
  # Display charts
1272
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1273
+ st.markdown('<h2 class="dashboard-title">شاخص‌های گیاهی در طول زمان</h2>', unsafe_allow_html=True)
1274
+ st.markdown('</div>', unsafe_allow_html=True)
1275
 
1276
  # Create line chart for all indices
1277
  vi_fig = create_line_chart(
1278
+ farm_ts_data, 'تاریخ', ['NDVI', 'NDRE', 'LAI'],
1279
+ f'شاخص‌های گیاهی برای {selected_farm}'
1280
  )
1281
  st.plotly_chart(vi_fig, use_container_width=True)
1282
 
1283
  # Display individual metrics with small charts
1284
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1285
+ st.markdown('<h2 class="dashboard-title">وضعیت فعلی گیاه</h2>', unsafe_allow_html=True)
1286
+ st.markdown('</div>', unsafe_allow_html=True)
1287
+
1288
  vi_cols = st.columns(3)
1289
 
1290
  with vi_cols[0]:
 
1299
 
1300
  with vi_cols[2]:
1301
  current_lai = lai_values[-1]
1302
+ lai_gauge = create_gauge_chart(current_lai, "LAI", 0, 7)
1303
  st.plotly_chart(lai_gauge, use_container_width=True)
1304
+
1305
+ st.markdown('</div>', unsafe_allow_html=True)
1306
 
1307
  # Growth Trends tab
1308
  with analysis_tabs[1]:
1309
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1310
+
1311
  # Generate sample growth data
1312
  growth_dates = pd.date_range(start=start_date, end=end_date, freq='D')
1313
 
 
1324
 
1325
  # Create dataframe
1326
  growth_data = pd.DataFrame({
1327
+ 'تاریخ': growth_dates,
1328
+ 'رشد روزانه': growth_values,
1329
+ 'رشد تجمعی': cumulative_growth
1330
  })
1331
 
1332
  # Display charts
1333
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1334
+ st.markdown('<h2 class="dashboard-title">الگوهای رشد</h2>', unsafe_allow_html=True)
1335
+ st.markdown('</div>', unsafe_allow_html=True)
1336
 
1337
+ growth_tabs = st.tabs(["رشد روزانه", "رشد تجمعی"])
1338
 
1339
  with growth_tabs[0]:
1340
  daily_fig = create_bar_chart(
1341
+ growth_data, 'تاریخ', 'رشد روزانه',
1342
+ f'نرخ رشد روزانه برای {selected_farm}'
1343
  )
1344
  st.plotly_chart(daily_fig, use_container_width=True)
1345
 
1346
  with growth_tabs[1]:
1347
  cumulative_fig = create_area_chart(
1348
+ growth_data, 'تاریخ', 'رشد تجمعی',
1349
+ f'رشد تجمعی برای {selected_farm}'
1350
  )
1351
  st.plotly_chart(cumulative_fig, use_container_width=True)
1352
 
1353
  # Growth statistics
1354
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1355
+ st.markdown('<h2 class="dashboard-title">آمار رشد</h2>', unsafe_allow_html=True)
1356
+ st.markdown('</div>', unsafe_allow_html=True)
1357
+
1358
  stats_cols = st.columns(3)
1359
 
1360
  with stats_cols[0]:
 
1362
  st.markdown(f"""
1363
  <div class="metric-card">
1364
  <div class="metric-value">{avg_growth:.3f}</div>
1365
+ <div class="metric-label">میانگین رشد روزانه</div>
1366
  </div>
1367
  """, unsafe_allow_html=True)
1368
 
 
1372
  st.markdown(f"""
1373
  <div class="metric-card">
1374
  <div class="metric-value">{max_growth:.3f}</div>
1375
+ <div class="metric-label">حداکثر رشد ({max_day})</div>
1376
  </div>
1377
  """, unsafe_allow_html=True)
1378
 
 
1381
  st.markdown(f"""
1382
  <div class="metric-card">
1383
  <div class="metric-value">{total_growth:.2f}</div>
1384
+ <div class="metric-label">رشد کل</div>
1385
  </div>
1386
  """, unsafe_allow_html=True)
1387
+
1388
+ st.markdown('</div>', unsafe_allow_html=True)
1389
 
1390
  # Environmental Factors tab
1391
  with analysis_tabs[2]:
1392
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1393
+
1394
  # Generate sample environmental data
1395
  env_dates = pd.date_range(start=start_date, end=end_date, freq='D')
1396
 
 
1410
 
1411
  # Create dataframe
1412
  env_data = pd.DataFrame({
1413
+ 'تاریخ': env_dates,
1414
+ 'دما (°C)': temp_values,
1415
+ 'رطوبت (%)': humidity_values,
1416
+ 'بارندگی (mm)': rainfall_values
1417
  })
1418
 
1419
  # Display charts
1420
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1421
+ st.markdown('<h2 class="dashboard-title">شرایط محیطی</h2>', unsafe_allow_html=True)
1422
+ st.markdown('</div>', unsafe_allow_html=True)
1423
 
1424
+ env_tabs = st.tabs(["دما", "رطوبت", "بارندگی"])
1425
 
1426
  with env_tabs[0]:
1427
  temp_fig = create_line_chart(
1428
+ env_data, 'تاریخ', ['دما (°C)'],
1429
+ f'دما برای {selected_farm}'
1430
  )
1431
  st.plotly_chart(temp_fig, use_container_width=True)
1432
 
1433
  with env_tabs[1]:
1434
  humidity_fig = create_line_chart(
1435
+ env_data, 'تاریخ', ['رطوبت (%)'],
1436
+ f'رطوبت برای {selected_farm}'
1437
  )
1438
  st.plotly_chart(humidity_fig, use_container_width=True)
1439
 
1440
  with env_tabs[2]:
1441
  rainfall_fig = create_bar_chart(
1442
+ env_data, 'تاریخ', 'بارندگی (mm)',
1443
+ f'بارندگی برای {selected_farm}'
1444
  )
1445
  st.plotly_chart(rainfall_fig, use_container_width=True)
1446
 
1447
  # Correlation analysis
1448
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1449
+ st.markdown('<h2 class="dashboard-title">تأثیر محیط بر رشد</h2>', unsafe_allow_html=True)
1450
+ st.markdown('</div>', unsafe_allow_html=True)
1451
 
1452
  # Merge environmental and growth data
1453
  merged_data = pd.merge(
1454
  env_data,
1455
+ growth_data[['تاریخ', 'رشد روزانه']],
1456
+ on='تاریخ'
1457
  )
1458
 
1459
  # Calculate correlations
1460
+ corr_temp = np.corrcoef(merged_data['دما (°C)'], merged_data['رشد روزانه'])[0, 1]
1461
+ corr_humidity = np.corrcoef(merged_data['رطوبت (%)'], merged_data['رشد روزانه'])[0, 1]
1462
+ corr_rainfall = np.corrcoef(merged_data['بارندگی (mm)'], merged_data['رشد روزانه'])[0, 1]
1463
 
1464
  # Display correlation metrics
1465
  corr_cols = st.columns(3)
 
1468
  st.markdown(f"""
1469
  <div class="metric-card">
1470
  <div class="metric-value">{corr_temp:.2f}</div>
1471
+ <div class="metric-label">همبستگی دما</div>
1472
  </div>
1473
  """, unsafe_allow_html=True)
1474
 
 
1476
  st.markdown(f"""
1477
  <div class="metric-card">
1478
  <div class="metric-value">{corr_humidity:.2f}</div>
1479
+ <div class="metric-label">همبستگی رطوبت</div>
1480
  </div>
1481
  """, unsafe_allow_html=True)
1482
 
 
1484
  st.markdown(f"""
1485
  <div class="metric-card">
1486
  <div class="metric-value">{corr_rainfall:.2f}</div>
1487
+ <div class="metric-label">همبستگی بارندگی</div>
1488
  </div>
1489
  """, unsafe_allow_html=True)
1490
+
1491
+ st.markdown('</div>', unsafe_allow_html=True)
1492
 
1493
  # Growth Prediction Tab
1494
  with tabs[2]:
1495
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1496
+
1497
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1498
+ st.markdown('<h2 class="dashboard-title">پیش‌ب��نی رشد با LSTM</h2>', unsafe_allow_html=True)
1499
+ st.markdown('</div>', unsafe_allow_html=True)
1500
 
1501
  # Farm selection for prediction
1502
+ pred_farm = st.selectbox("انتخاب مزرعه برای پیش‌بینی", selected_farms['نام مزرعه'].tolist(), key="pred_farm")
1503
+
1504
+ st.markdown('</div>', unsafe_allow_html=True)
1505
 
1506
  # Generate sample historical data for LSTM
1507
  hist_dates = pd.date_range(start=start_date - timedelta(days=60), end=end_date, freq='D')
 
1528
  rainfall[rain_days] = np.random.exponential(5, size=len(rain_days))
1529
 
1530
  # Age (constant for a farm)
1531
+ farm_age = selected_farms[selected_farms['نام مزرعه'] == pred_farm]['سن'].values[0]
1532
  age = np.ones(len(hist_dates)) * farm_age
1533
 
1534
  # Variety (encoded as 0, 1, 2)
1535
+ variety_map = {'نوع A': 0, 'نوع B': 1, 'نوع C': 2}
1536
+ farm_variety = selected_farms[selected_farms['نام مزرعه'] == pred_farm]['تنوع'].values[0]
1537
  variety_encoded = np.ones(len(hist_dates)) * variety_map.get(farm_variety, 0)
1538
 
1539
  # Target variables with relationship to features
1540
+ # NDVI increases with rainfall and temperature within optimal range
1541
+ ndvi_base = 0.01 * (temp - 15) / 15 + 0.02 * rainfall / 5 - 0.005 * np.abs(temp - 28) / 10
1542
+ ndvi = 0.6 + ndvi_base + 0.001 * np.arange(len(hist_dates)) + np.random.normal(0, 0.02, len(hist_dates))
 
 
 
 
1543
  ndvi = np.clip(ndvi, 0, 1)
1544
 
1545
  # NDRE correlated with NDVI but lower values
1546
  ndre = 0.7 * ndvi + np.random.normal(0, 0.02, len(hist_dates))
1547
  ndre = np.clip(ndre, 0, 1)
1548
 
1549
+ # LAI calculation using the realistic formula: LAI = 0.57 * exp(2.33 * NDVI)
1550
+ lai = 0.57 * np.exp(2.33 * ndvi)
1551
+
1552
  # Create dataframe
1553
  hist_data = pd.DataFrame({
1554
+ 'تاریخ': hist_dates,
1555
+ 'دما': temp,
1556
+ 'رطوبت': humidity,
1557
+ 'بارندگی': rainfall,
1558
+ 'سن': age,
1559
+ 'تنوع': variety_encoded,
1560
  'LAI': lai,
1561
  'NDVI': ndvi,
1562
  'NDRE': ndre
1563
  })
1564
 
1565
  # Display historical data
1566
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1567
+
1568
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1569
+ st.markdown('<h2 class="dashboard-title">داده‌های تاریخی</h2>', unsafe_allow_html=True)
1570
+ st.markdown('</div>', unsafe_allow_html=True)
1571
+
1572
  hist_fig = create_line_chart(
1573
+ hist_data, 'تاریخ', ['LAI', 'NDVI', 'NDRE'],
1574
+ f'شاخص‌های گیاهی تاریخی برای {pred_farm}'
1575
  )
1576
  st.plotly_chart(hist_fig, use_container_width=True)
1577
 
1578
+ st.markdown('</div>', unsafe_allow_html=True)
1579
+
1580
  # LSTM Model Training
1581
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1582
+
1583
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1584
+ st.markdown('<h2 class="dashboard-title">آموزش مدل LSTM</h2>', unsafe_allow_html=True)
1585
+ st.markdown('</div>', unsafe_allow_html=True)
1586
 
1587
  # Button to trigger training
1588
+ if st.button("آموزش مدل LSTM و پیش‌بینی"):
1589
+ with st.spinner("در حال آموزش مدل LSTM..."):
1590
  # Prepare data for LSTM
1591
+ features = hist_data[['دما', 'رطوبت', 'بارندگی', 'سن', 'تنوع']].values
1592
  targets = hist_data[['LAI', 'NDVI', 'NDRE']].values
1593
 
1594
  # Combine features and targets
 
1608
 
1609
  # Create prediction dataframe
1610
  pred_data = pd.DataFrame({
1611
+ 'تاریخ': pred_dates,
1612
  'LAI': predictions[:, 0],
1613
  'NDVI': predictions[:, 1],
1614
  'NDRE': predictions[:, 2]
 
1616
 
1617
  # Combine historical and prediction data
1618
  combined_data = pd.concat([
1619
+ hist_data[['تاریخ', 'LAI', 'NDVI', 'NDRE']].tail(30), # Last 30 days of historical data
1620
  pred_data
1621
  ])
1622
 
1623
  # Display prediction results
1624
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1625
+ st.markdown('<h2 class="dashboard-title">نتایج پیش‌بینی</h2>', unsafe_allow_html=True)
1626
+ st.markdown('</div>', unsafe_allow_html=True)
1627
 
1628
  # Create line chart with historical and predicted values
1629
  pred_fig = go.Figure()
1630
 
1631
  # Historical data (solid lines)
1632
+ for col, color in zip(['LAI', 'NDVI', 'NDRE'], ['#2EC4B6', '#FF9F1C', '#E71D36']):
1633
+ hist_part = combined_data[combined_data['تاریخ'] <= end_date]
1634
  pred_fig.add_trace(go.Scatter(
1635
+ x=hist_part['تاریخ'],
1636
  y=hist_part[col],
1637
  mode='lines',
1638
+ name=f'تاریخی {col}',
1639
  line=dict(color=color)
1640
  ))
1641
 
1642
  # Predicted data (dashed lines)
1643
+ for col, color in zip(['LAI', 'NDVI', 'NDRE'], ['#2EC4B6', '#FF9F1C', '#E71D36']):
1644
+ pred_part = combined_data[combined_data['تاریخ'] > end_date]
1645
  pred_fig.add_trace(go.Scatter(
1646
+ x=pred_part['تاریخ'],
1647
  y=pred_part[col],
1648
  mode='lines+markers',
1649
+ name=f'پیش‌بینی {col}',
1650
  line=dict(color=color, dash='dash'),
1651
  marker=dict(size=8)
1652
  ))
 
1657
  line_width=2,
1658
  line_dash="dash",
1659
  line_color="red",
1660
+ annotation_text="شروع پیش‌بینی",
1661
  annotation_position="top right"
1662
  )
1663
 
1664
  pred_fig.update_layout(
1665
+ title=f'پیش‌بینی رشد 7 روزه برای {pred_farm}',
1666
+ xaxis_title='تاریخ',
1667
+ yaxis_title='مقدار',
1668
  legend=dict(
1669
  orientation="h",
1670
  yanchor="bottom",
 
1673
  x=1
1674
  ),
1675
  template="plotly_dark",
1676
+ plot_bgcolor='rgba(15, 25, 35, 0.8)',
1677
  paper_bgcolor='rgba(0,0,0,0)',
1678
  font=dict(color='white'),
1679
  height=500
 
1682
  st.plotly_chart(pred_fig, use_container_width=True)
1683
 
1684
  # Display prediction table
1685
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1686
+ st.markdown('<h2 class="dashboard-title">مقادیر پیش‌بینی شده</h2>', unsafe_allow_html=True)
1687
+ st.markdown('</div>', unsafe_allow_html=True)
1688
+
1689
  st.dataframe(
1690
  pred_data.style.background_gradient(
1691
  cmap='Greens', subset=['LAI', 'NDVI', 'NDRE']
 
1694
  )
1695
 
1696
  # Growth summary
1697
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1698
+ st.markdown('<h2 class="dashboard-title">خلاصه رشد</h2>', unsafe_allow_html=True)
1699
+ st.markdown('</div>', unsafe_allow_html=True)
1700
 
1701
  # Calculate growth rates
1702
  lai_growth = (pred_data['LAI'].iloc[-1] - hist_data['LAI'].iloc[-1]) / hist_data['LAI'].iloc[-1] * 100
 
1709
  st.markdown(f"""
1710
  <div class="metric-card">
1711
  <div class="metric-value">{lai_growth:.1f}%</div>
1712
+ <div class="metric-label">رشد LAI (7 روز)</div>
1713
  </div>
1714
  """, unsafe_allow_html=True)
1715
 
 
1717
  st.markdown(f"""
1718
  <div class="metric-card">
1719
  <div class="metric-value">{ndvi_growth:.1f}%</div>
1720
+ <div class="metric-label">رشد NDVI (7 روز)</div>
1721
  </div>
1722
  """, unsafe_allow_html=True)
1723
 
 
1725
  st.markdown(f"""
1726
  <div class="metric-card">
1727
  <div class="metric-value">{ndre_growth:.1f}%</div>
1728
+ <div class="metric-label">رشد NDRE (7 روز)</div>
1729
  </div>
1730
  """, unsafe_allow_html=True)
1731
  else:
1732
+ st.info("برای آموزش مدل LSTM و تولید پیش‌بینی‌ها، روی دکمه بالا کلیک کنید.")
1733
+
1734
+ st.markdown('</div>', unsafe_allow_html=True)
1735
 
1736
  # Data Tables Tab
1737
  with tabs[3]:
1738
+ st.markdown('<div class="dashboard-container">', unsafe_allow_html=True)
1739
+
1740
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1741
+ st.markdown('<h2 class="dashboard-title">جداول داده مزارع</h2>', unsafe_allow_html=True)
1742
+ st.markdown('</div>', unsafe_allow_html=True)
1743
 
1744
  # Create tabs for different data tables
1745
+ data_tabs = st.tabs(["اطلاعات مزرعه", "شاخص‌های گیاهی", "رتبه‌بندی رشد"])
1746
 
1747
  # Farm Information tab
1748
  with data_tabs[0]:
1749
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1750
+ st.markdown('<h2 class="dashboard-title">جزئیات مزرعه</h2>', unsafe_allow_html=True)
1751
+ st.markdown('</div>', unsafe_allow_html=True)
1752
+
1753
  st.dataframe(
1754
  selected_farms,
1755
  use_container_width=True
 
1757
 
1758
  # Vegetation Indices tab
1759
  with data_tabs[1]:
1760
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1761
+ st.markdown('<h2 class="dashboard-title">شاخص‌های گیاهی بر اساس مزرعه</h2>', unsafe_allow_html=True)
1762
+ st.markdown('</div>', unsafe_allow_html=True)
1763
 
1764
  # Generate sample vegetation indices for all farms
1765
  vi_data = []
1766
 
1767
  for _, farm in selected_farms.iterrows():
1768
  # Generate random indices with some correlation to farm age
1769
+ farm_age = farm['سن']
1770
  base_ndvi = min(0.5 + 0.08 * farm_age + np.random.normal(0, 0.05), 0.95)
1771
  base_ndre = base_ndvi * 0.7 + np.random.normal(0, 0.03)
1772
+
1773
+ # Calculate LAI using the realistic formula: LAI = 0.57 * exp(2.33 * NDVI)
1774
+ base_lai = 0.57 * np.exp(2.33 * base_ndvi)
1775
 
1776
  vi_data.append({
1777
+ 'مزرعه': farm['نام مزرعه'],
1778
+ 'سن': farm_age,
1779
+ 'تنوع': farm['تنوع'],
1780
  'NDVI': round(base_ndvi, 2),
1781
  'NDRE': round(base_ndre, 2),
1782
  'LAI': round(base_lai, 1),
1783
+ 'شاخص کلروفیل': round(base_ndvi * 4 + np.random.normal(0, 0.2), 1)
1784
  })
1785
 
1786
  vi_df = pd.DataFrame(vi_data)
 
1788
  # Display the data
1789
  st.dataframe(
1790
  vi_df.style.background_gradient(
1791
+ cmap='Greens', subset=['NDVI', 'NDRE', 'LAI', 'شاخص کلروفیل']
1792
  ),
1793
  use_container_width=True
1794
  )
1795
 
1796
  # Growth Rankings tab
1797
  with data_tabs[2]:
1798
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1799
+ st.markdown('<h2 class="dashboard-title">رتبه‌بندی رشد مزارع</h2>', unsafe_allow_html=True)
1800
+ st.markdown('</div>', unsafe_allow_html=True)
1801
 
1802
+ # Generate sample growth data for all farms for the last 7 days
1803
  growth_data = []
1804
 
1805
  for _, farm in selected_farms.iterrows():
 
1807
  base_growth = np.random.uniform(2.0, 5.0)
1808
 
1809
  # Adjust based on farm age (younger farms grow faster)
1810
+ age_factor = max(1.0, 6 - farm['سن']) / 3
1811
 
1812
  growth_data.append({
1813
+ 'مزرعه': farm['نام مزرعه'],
1814
+ 'سن': farm['سن'],
1815
+ 'تنوع': farm['تنوع'],
1816
+ 'نرخ رشد (%)': round(base_growth * age_factor, 1),
1817
+ 'شاخص سلامت': round(np.random.uniform(0.7, 1.0), 2),
1818
+ 'سطح استرس': round(np.random.uniform(0.0, 0.3), 2)
1819
  })
1820
 
1821
+ growth_df = pd.DataFrame(growth_data).sort_values('نرخ رشد (%)', ascending=False)
1822
 
1823
  # Display the data
1824
  st.dataframe(
1825
  growth_df.style.background_gradient(
1826
+ cmap='Greens', subset=['نرخ رشد (%)', 'شاخص سلامت']
1827
  ).background_gradient(
1828
+ cmap='Reds_r', subset=['سطح استرس']
1829
  ),
1830
  use_container_width=True
1831
  )
 
1834
  col1, col2 = st.columns(2)
1835
 
1836
  with col1:
1837
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1838
+ st.markdown('<h2 class="dashboard-title">بهترین عملکردها</h2>', unsafe_allow_html=True)
1839
+ st.markdown('</div>', unsafe_allow_html=True)
1840
+
1841
  top_farms = growth_df.head(3)
1842
 
1843
  for i, (_, farm) in enumerate(top_farms.iterrows()):
1844
  st.markdown(f"""
1845
  <div class="metric-card" style="margin-bottom: 10px;">
1846
+ <h4>#{i+1}: {farm['مزرعه']}</h4>
1847
+ <div class="metric-value">{farm['نرخ رشد (%)']}%</div>
1848
+ <div class="metric-label">نرخ رشد</div>
1849
+ <p>شاخص سلامت: {farm['شاخص سلامت']} | استرس: {farm['سطح استرس']}</p>
1850
  </div>
1851
  """, unsafe_allow_html=True)
1852
 
1853
  with col2:
1854
+ st.markdown('<div class="dashboard-header">', unsafe_allow_html=True)
1855
+ st.markdown('<h2 class="dashboard-title">نیازمند توجه</h2>', unsafe_allow_html=True)
1856
+ st.markdown('</div>', unsafe_allow_html=True)
1857
+
1858
  bottom_farms = growth_df.tail(3).iloc[::-1]
1859
 
1860
  for i, (_, farm) in enumerate(bottom_farms.iterrows()):
1861
  st.markdown(f"""
1862
  <div class="metric-card" style="margin-bottom: 10px; border-color: #ff6b6b;">
1863
+ <h4>#{i+1}: {farm['مزرعه']}</h4>
1864
+ <div class="metric-value" style="color: #ff6b6b;">{farm['نرخ رشد (%)']}%</div>
1865
+ <div class="metric-label">نرخ رشد</div>
1866
+ <p>شاخص سلامت: {farm['شاخص سلامت']} | استرس: {farm['سطح استرس']}</p>
1867
  </div>
1868
  """, unsafe_allow_html=True)
1869
+
1870
+ st.markdown('</div>', unsafe_allow_html=True)
1871
 
1872
  # Run the app
1873
  if __name__ == "__main__":
1874
+ main()