Esmaeilkianii commited on
Commit
09c0794
·
verified ·
1 Parent(s): 661ab86

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +143 -1372
app.py CHANGED
@@ -37,7 +37,7 @@ st.set_page_config(
37
  initial_sidebar_state="expanded"
38
  )
39
 
40
- # Custom CSS with modern green design and animations
41
  st.markdown("""
42
  <style>
43
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;200;300;400;500;600;700;800;900&display=swap');
@@ -53,7 +53,7 @@ st.markdown("""
53
 
54
  /* Header styling */
55
  .main-header {
56
- background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
57
  padding: 1.5rem;
58
  border-radius: 12px;
59
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
@@ -64,24 +64,8 @@ st.markdown("""
64
  }
65
 
66
  @keyframes header-glow {
67
- 0% {
68
- box-shadow: 0 8px 32px rgba(26, 135, 84, 0.1);
69
- }
70
- 100% {
71
- box-shadow: 0 8px 32px rgba(26, 135, 84, 0.3);
72
- }
73
- }
74
-
75
- .main-header::before {
76
- content: '';
77
- position: absolute;
78
- top: -50%;
79
- left: -50%;
80
- width: 200%;
81
- height: 200%;
82
- background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0) 70%);
83
- transform: rotate(30deg);
84
- z-index: 0;
85
  }
86
 
87
  .main-header h1 {
@@ -99,6 +83,35 @@ st.markdown("""
99
  z-index: 1;
100
  }
101
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  /* Navigation menu styling */
103
  .st-emotion-cache-1lcbz7b {
104
  background-color: transparent !important;
@@ -119,323 +132,11 @@ st.markdown("""
119
  }
120
 
121
  .st-emotion-cache-1lcbz7b .st-emotion-cache-1j7d69d[data-selected="true"] {
122
- background-color: #1a8754 !important;
123
  color: white !important;
124
  font-weight: 600 !important;
125
  }
126
 
127
- .st-emotion-cache-1lcbz7b .st-emotion-cache-1j7d69d .st-emotion-cache-1m5q2i0 {
128
- color: #1a8754 !important;
129
- font-size: 18px !important;
130
- }
131
-
132
- /* Metric card styling */
133
- .metric-card {
134
- background: white;
135
- border-radius: 12px;
136
- padding: 1.5rem;
137
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
138
- transition: all 0.3s ease;
139
- text-align: center;
140
- }
141
-
142
- .metric-card:hover {
143
- transform: translateY(-5px);
144
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
145
- }
146
-
147
- .metric-card .metric-value {
148
- font-size: 2.5rem;
149
- font-weight: 700;
150
- color: #1a8754;
151
- margin-bottom: 0.5rem;
152
- }
153
-
154
- .metric-card .metric-label {
155
- font-size: 1rem;
156
- color: #6c757d;
157
- }
158
-
159
- /* Map container styling */
160
- .map-container {
161
- border-radius: 12px;
162
- overflow: hidden;
163
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
164
- }
165
-
166
- /* Tabs styling */
167
- .stTabs [data-baseweb="tab-list"] {
168
- gap: 8px;
169
- }
170
-
171
- .stTabs [data-baseweb="tab"] {
172
- border-radius: 4px 4px 0px 0px;
173
- padding: 10px 16px;
174
- background-color: #f8f9fa;
175
- }
176
-
177
- .stTabs [aria-selected="true"] {
178
- background-color: #1a8754 !important;
179
- color: white !important;
180
- }
181
-
182
- /* Sidebar styling */
183
- [data-testid="stSidebar"] {
184
- background-color: #ffffff;
185
- border-right: 1px solid #e9ecef;
186
- }
187
-
188
- /* Animations */
189
- @keyframes fadeIn {
190
- 0% { opacity: 0; transform: translateY(20px); }
191
- 100% { opacity: 1; transform: translateY(0); }
192
- }
193
-
194
- .animate-fadeIn {
195
- animation: fadeIn 0.5s ease forwards;
196
- }
197
-
198
- /* Loading animation */
199
- .loading-spinner {
200
- display: flex;
201
- justify-content: center;
202
- align-items: center;
203
- height: 100px;
204
- }
205
-
206
- .loading-spinner::after {
207
- content: "";
208
- width: 40px;
209
- height: 40px;
210
- border: 4px solid #f3f3f3;
211
- border-top: 4px solid #1a8754;
212
- border-radius: 50%;
213
- animation: spin 1s linear infinite;
214
- }
215
-
216
- @keyframes spin {
217
- 0% { transform: rotate(0deg); }
218
- 100% { transform: rotate(360deg); }
219
- }
220
-
221
- /* RTL Support */
222
- .rtl {
223
- direction: rtl;
224
- text-align: right;
225
- }
226
-
227
- /* Custom scrollbar */
228
- ::-webkit-scrollbar {
229
- width: 8px;
230
- height: 8px;
231
- }
232
-
233
- ::-webkit-scrollbar-track {
234
- background: #f1f1f1;
235
- border-radius: 10px;
236
- }
237
-
238
- ::-webkit-scrollbar-thumb {
239
- background: #1a8754;
240
- border-radius: 10px;
241
- }
242
-
243
- ::-webkit-scrollbar-thumb:hover {
244
- background: #115740;
245
- }
246
-
247
- /* Tooltip styling */
248
- .tooltip {
249
- position: relative;
250
- display: inline-block;
251
- }
252
-
253
- .tooltip .tooltiptext {
254
- visibility: hidden;
255
- width: 120px;
256
- background-color: #555;
257
- color: #fff;
258
- text-align: center;
259
- border-radius: 6px;
260
- padding: 5px;
261
- position: absolute;
262
- z-index: 1;
263
- bottom: 125%;
264
- left: 50%;
265
- margin-left: -60px;
266
- opacity: 0;
267
- transition: opacity 0.3s;
268
- }
269
-
270
- .tooltip:hover .tooltiptext {
271
- visibility: visible;
272
- opacity: 1;
273
- }
274
-
275
- /* Data table styling */
276
- .dataframe {
277
- border-collapse: collapse;
278
- width: 100%;
279
- border-radius: 8px;
280
- overflow: hidden;
281
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
282
- }
283
-
284
- .dataframe th {
285
- background-color: #1a8754;
286
- color: white;
287
- padding: 12px;
288
- text-align: right;
289
- }
290
-
291
- .dataframe td {
292
- padding: 10px 12px;
293
- border-bottom: 1px solid #e9ecef;
294
- }
295
-
296
- .dataframe tr:nth-child(even) {
297
- background-color: #f8f9fa;
298
- }
299
-
300
- .dataframe tr:hover {
301
- background-color: #e9ecef;
302
- }
303
-
304
- /* Progress bar styling */
305
- .stProgress > div > div > div > div {
306
- background-color: #1a8754;
307
- }
308
-
309
- /* Notification styling */
310
- .notification {
311
- background-color: #d1e7dd;
312
- color: #0f5132;
313
- padding: 1rem;
314
- border-radius: 8px;
315
- margin-bottom: 1rem;
316
- display: flex;
317
- align-items: center;
318
- animation: slideIn 0.5s ease;
319
- }
320
-
321
- @keyframes slideIn {
322
- 0% { transform: translateX(100%); opacity: 0; }
323
- 100% { transform: translateX(0); opacity: 1; }
324
- }
325
-
326
- .notification-icon {
327
- margin-right: 0.5rem;
328
- font-size: 1.2rem;
329
- }
330
-
331
- /* Custom select box */
332
- .custom-select {
333
- background-color: white;
334
- border-radius: 8px;
335
- padding: 0.5rem;
336
- border: 1px solid #ced4da;
337
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
338
- }
339
-
340
- /* Glassmorphism effect */
341
- .glass-card {
342
- background: rgba(255, 255, 255, 0.7);
343
- backdrop-filter: blur(10px);
344
- -webkit-backdrop-filter: blur(10px);
345
- border-radius: 12px;
346
- border: 1px solid rgba(255, 255, 255, 0.3);
347
- padding: 1.5rem;
348
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
349
- }
350
-
351
- /* Neumorphism effect */
352
- .neumorphic-card {
353
- background: #f0f0f3;
354
- border-radius: 12px;
355
- box-shadow: 10px 10px 20px #d1d1d4, -10px -10px 20px #ffffff;
356
- padding: 1.5rem;
357
- }
358
-
359
- /* Gradient text */
360
- .gradient-text {
361
- background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
362
- -webkit-background-clip: text;
363
- -webkit-text-fill-color: transparent;
364
- font-weight: 700;
365
- }
366
-
367
- /* Pulsing animation */
368
- @keyframes pulse {
369
- 0% { transform: scale(1); }
370
- 50% { transform: scale(1.05); }
371
- 100% { transform: scale(1); }
372
- }
373
-
374
- .pulse-animation {
375
- animation: pulse 2s infinite;
376
- }
377
-
378
- /* Custom radio buttons */
379
- .stRadio > div {
380
- display: flex;
381
- gap: 10px;
382
- }
383
-
384
- .stRadio label {
385
- cursor: pointer;
386
- background-color: #f8f9fa;
387
- padding: 0.5rem 1rem;
388
- border-radius: 50px;
389
- transition: all 0.3s ease;
390
- }
391
-
392
- .stRadio label:hover {
393
- background-color: #e9ecef;
394
- }
395
-
396
- /* Hide default radio button */
397
- .stRadio input {
398
- display: none;
399
- }
400
-
401
- /* Custom checked state */
402
- .stRadio input:checked + label {
403
- background-color: #1a8754;
404
- color: white;
405
- }
406
-
407
- .stSelectbox, .stNumberInput {
408
- background-color: #f0f2f6;
409
- border-radius: 10px;
410
- padding: 10px;
411
- margin: 10px 0;
412
- }
413
-
414
- .custom-card {
415
- background-color: white;
416
- padding: 20px;
417
- border-radius: 15px;
418
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
419
- margin: 10px 0;
420
- }
421
-
422
- .metric-container {
423
- display: flex;
424
- justify-content: space-between;
425
- flex-wrap: wrap;
426
- }
427
-
428
- .metric-card {
429
- background-color: #1a8754;
430
- color: white;
431
- padding: 15px;
432
- border-radius: 10px;
433
- margin: 5px;
434
- flex: 1;
435
- min-width: 200px;
436
- text-align: center;
437
- }
438
-
439
  /* Button styling */
440
  .stButton>button {
441
  border-radius: 50px;
@@ -443,6 +144,8 @@ st.markdown("""
443
  font-weight: 600;
444
  transition: all 0.3s ease;
445
  border: none;
 
 
446
  }
447
 
448
  .stButton>button:hover {
@@ -450,28 +153,16 @@ st.markdown("""
450
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
451
  }
452
 
453
- .primary-btn {
454
- background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
455
- color: white;
456
- }
457
-
458
- .secondary-btn {
459
- background: white;
460
- color: #1a8754;
461
- border: 1px solid #1a8754 !important;
462
- }
463
-
464
  /* Footer styling */
465
  footer {
466
  position: fixed;
467
  left: 0;
468
  bottom: 0;
469
  width: 100%;
470
- background-color: #1a8754;
471
  color: white;
472
  text-align: center;
473
  padding: 10px 0;
474
- font-family: 'Vazirmatn', sans-serif;
475
  }
476
  </style>
477
  """, unsafe_allow_html=True)
@@ -481,39 +172,27 @@ st.markdown("""
481
  def load_farm_data():
482
  try:
483
  df = pd.read_csv("کراپ لاگ کلی (1).csv")
484
- # Rename columns for consistency with the program
485
  df.rename(columns={
486
- 'سال': 'Year',
487
- 'هفته': 'Week',
488
- 'مزرعه': 'Farm_ID',
489
- 'کانال': 'Channel',
490
- 'اداره': 'Administration',
491
- 'مساحت': 'Area',
492
- 'مساحت زیر مجموعه': 'SubArea',
493
- 'رقم': 'Variety',
494
- 'سن': 'Age',
495
- 'ایستگاه 1': 'Station1',
496
- 'ایستگاه 2': 'Station2',
497
- 'ایستگاه 3': 'Station3',
498
- 'ایستگاه 4': 'Station4',
499
- 'ایستگاه 5': 'Station5',
500
- 'ارتفاع هفته جاری مزرعه': 'CurrentHeight',
501
- 'ارتفاع هفته گذشته مزرعه': 'PreviousHeight',
502
- 'رشد هفته جاری': 'CurrentGrowth',
503
- 'رشد هفته گذشته': 'PreviousGrowth',
504
- 'نیتروژن فعلی': 'CurrentNitrogen',
505
- 'نیتروژن استاندارد فعلی': 'StandardNitrogen',
506
- 'نیتروژن قبلی': 'PreviousNitrogen',
507
- 'نیتروژن استاندارد قبلی': 'PreviousStandardNitrogen',
508
- 'رطوبت غلاف فعلی': 'CurrentMoisture',
509
- 'رطوبت استاندارد فعلی': 'StandardMoisture',
510
- 'رطوبت غلاف قبلی': 'PreviousMoisture',
511
- 'رطوبت استاندارد قبلی': 'PreviousStandardMoisture',
512
- 'چاهک 1': 'Well1',
513
- 'تاریخ قرائت': 'Well1Date',
514
- 'چاهک 2': 'Well2',
515
- 'تاریخ قرائت.1': 'Well2Date'
516
  }, inplace=True)
 
 
 
 
 
 
 
517
  return df
518
  except Exception as e:
519
  st.error(f"خطا در بارگذاری داده‌های مزارع: {e}")
@@ -524,9 +203,7 @@ def load_coordinates_data():
524
  try:
525
  coords_df = pd.read_csv("farm_coordinates.csv")
526
  coords_df.rename(columns={
527
- 'مزرعه': 'Farm_ID',
528
- 'عرض جغرافیایی': 'Latitude',
529
- 'طول جغرافیایی': 'Longitude'
530
  }, inplace=True)
531
  return coords_df
532
  except Exception as e:
@@ -537,10 +214,7 @@ def load_coordinates_data():
537
  def load_day_data():
538
  try:
539
  day_df = pd.read_csv("پایگاه داده (1).csv")
540
- day_df.rename(columns={
541
- 'مزرعه': 'Farm_ID',
542
- 'روز': 'Day'
543
- }, inplace=True)
544
  return day_df
545
  except Exception as e:
546
  st.error(f"خطا در بارگذاری داده‌های روزهای هفته: {e}")
@@ -554,7 +228,7 @@ def load_lottie_url(url: str):
554
  return None
555
  return r.json()
556
 
557
- # Initialize Earth Engine (unchanged for now, but can be used with real data)
558
  @st.cache_resource
559
  def initialize_earth_engine():
560
  try:
@@ -601,22 +275,9 @@ def create_ee_map(farm_id, date_str, layer_type="NDVI"):
601
  if layer_type == "NDVI":
602
  index = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
603
  viz_params = {'min': -0.2, 'max': 0.8, 'palette': ['#ff0000', '#ff4500', '#ffd700', '#32cd32', '#006400']}
604
- legend_title = 'شاخص پوشش گیاهی (NDVI)'
605
  elif layer_type == "NDMI":
606
  index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
607
  viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
608
- legend_title = 'شاخص رطوبت (NDMI)'
609
- elif layer_type == "EVI":
610
- nir = s2.select('B8')
611
- red = s2.select('B4')
612
- blue = s2.select('B2')
613
- index = nir.subtract(red).multiply(2.5).divide(nir.add(red.multiply(6)).subtract(blue.multiply(7.5)).add(1)).rename('EVI')
614
- viz_params = {'min': 0, 'max': 1, 'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#4caf50']}
615
- legend_title = 'شاخص پیشرفته گیاهی (EVI)'
616
- elif layer_type == "NDWI":
617
- index = s2.normalizedDifference(['B3', 'B8']).rename('NDWI')
618
- viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#00008b', '#00b7eb', '#add8e6', '#fdae61', '#d73027']}
619
- legend_title = 'شاخص آب (NDWI)'
620
  map_id_dict = ee.Image(index).getMapId(viz_params)
621
  folium.TileLayer(
622
  tiles=map_id_dict['tile_fetcher'].url_format,
@@ -625,72 +286,13 @@ def create_ee_map(farm_id, date_str, layer_type="NDVI"):
625
  overlay=True,
626
  control=True
627
  ).add_to(m)
628
- folium.Marker(
629
- [lat, lon],
630
- popup=f'مزرعه {farm_id}',
631
- tooltip=f'مزرعه {farm_id}',
632
- icon=folium.Icon(color='green', icon='leaf')
633
- ).add_to(m)
634
- folium.Circle(
635
- [lat, lon],
636
- radius=1500,
637
- color='green',
638
- fill=True,
639
- fill_color='green',
640
- fill_opacity=0.1
641
- ).add_to(m)
642
  folium.LayerControl().add_to(m)
643
- legend_html = '''
644
- <div style="position: fixed;
645
- bottom: 50px; right: 50px;
646
- border: 2px solid grey; z-index: 9999;
647
- background-color: white;
648
- padding: 10px;
649
- border-radius: 5px;
650
- direction: rtl;
651
- font-family: 'Vazirmatn', sans-serif;">
652
- <div style="font-size: 14px; font-weight: bold; margin-bottom: 5px;">''' + legend_title + '''</div>
653
- <div style="display: flex; align-items: center; margin-bottom: 5px;">
654
- <div style="background: ''' + viz_params['palette'][0] + '''; width: 20px; height: 20px; margin-left: 5px;"></div>
655
- <span>کم</span>
656
- </div>
657
- <div style="display: flex; align-items: center; margin-bottom: 5px;">
658
- <div style="background: ''' + viz_params['palette'][2] + '''; width: 20px; height: 20px; margin-left: 5px;"></div>
659
- <span>متوسط</span>
660
- </div>
661
- <div style="display: flex; align-items: center;">
662
- <div style="background: ''' + viz_params['palette'][-1] + '''; width: 20px; height: 20px; margin-left: 5px;"></div>
663
- <span>زیاد</span>
664
- </div>
665
- </div>
666
- '''
667
- m.get_root().html.add_child(folium.Element(legend_html))
668
  return m
669
  except Exception as e:
670
  st.error(f"خطا در ایجاد نقشه: {e}")
671
  return None
672
 
673
- # Calculate real farm stats
674
- def calculate_farm_stats(farm_id, layer_type="NDVI"):
675
- farm_data = farm_df[farm_df['Farm_ID'] == farm_id]
676
- if layer_type == "NDVI":
677
- stats = {
678
- 'mean': farm_data['CurrentHeight'].mean() if not farm_data.empty else 0,
679
- 'min': farm_data['CurrentHeight'].min() if not farm_data.empty else 0,
680
- 'max': farm_data['CurrentHeight'].max() if not farm_data.empty else 0,
681
- 'std_dev': farm_data['CurrentHeight'].std() if not farm_data.empty else 0,
682
- 'histogram_data': farm_data['CurrentHeight'].values if not farm_data.empty else np.array([])
683
- }
684
- elif layer_type == "NDMI":
685
- stats = {
686
- 'mean': farm_data['CurrentMoisture'].mean() if not farm_data.empty else 0,
687
- 'min': farm_data['CurrentMoisture'].min() if not farm_data.empty else 0,
688
- 'max': farm_data['CurrentMoisture'].max() if not farm_data.empty else 0,
689
- 'std_dev': farm_data['CurrentMoisture'].std() if not farm_data.empty else 0,
690
- 'histogram_data': farm_data['CurrentMoisture'].values if not farm_data.empty else np.array([])
691
- }
692
- return stats
693
-
694
  # Generate real growth data
695
  def generate_real_growth_data(selected_variety="all", selected_age="all"):
696
  filtered_farms = farm_df
@@ -699,37 +301,9 @@ def generate_real_growth_data(selected_variety="all", selected_age="all"):
699
  if selected_age != "all":
700
  filtered_farms = filtered_farms[filtered_farms['Age'] == selected_age]
701
 
702
- farm_growth_data = []
703
  weeks = filtered_farms['Week'].unique()
704
- for farm_id in filtered_farms['Farm_ID'].unique():
705
- farm_data = filtered_farms[filtered_farms['Farm_ID'] == farm_id]
706
- growth_data = {
707
- 'farm_id': farm_id,
708
- 'variety': farm_data['Variety'].iloc[0] if not farm_data.empty else 'Unknown',
709
- 'age': farm_data['Age'].iloc[0] if not farm_data.empty else 'Unknown',
710
- 'weeks': weeks,
711
- 'heights': [farm_data[farm_data['Week'] == week]['CurrentHeight'].mean() if not farm_data[farm_data['Week'] == week].empty else 0 for week in weeks]
712
- }
713
- farm_growth_data.append(growth_data)
714
-
715
- if farm_growth_data:
716
- avg_heights = []
717
- for week in weeks:
718
- week_heights = [farm['heights'][list(weeks).index(week)] for farm in farm_growth_data if farm['heights'][list(weeks).index(week)] > 0]
719
- avg_heights.append(round(sum(week_heights) / len(week_heights)) if week_heights else 0)
720
-
721
- avg_growth_data = {
722
- 'farm_id': 'میانگین',
723
- 'variety': 'همه',
724
- 'age': 'همه',
725
- 'weeks': weeks,
726
- 'heights': avg_heights
727
- }
728
- return {'individual': farm_growth_data, 'average': avg_growth_data}
729
- return {
730
- 'individual': [],
731
- 'average': {'farm_id': 'میانگین', 'variety': 'همه', 'age': 'همه', 'weeks': weeks, 'heights': [0] * len(weeks)}
732
- }
733
 
734
  # Initialize Earth Engine and load data
735
  ee_initialized = initialize_earth_engine()
@@ -749,10 +323,10 @@ if 'heights_df' not in st.session_state:
749
  # Main header
750
  st.markdown('<div class="main-header">', unsafe_allow_html=True)
751
  st.markdown('<h1>سامانه هوشمند پایش مزارع نیشکر دهخدا</h1>', unsafe_allow_html=True)
752
- st.markdown('<p>پلتفرم جامع مدیریت، پایش و تحلیل داده‌های مزارع نیشکر با استفاده از تصاویر ماهواره‌ای و هوش مصنوعی</p>', unsafe_allow_html=True)
753
  st.markdown('</div>', unsafe_allow_html=True)
754
 
755
- # Create a modern navigation menu
756
  selected = option_menu(
757
  menu_title=None,
758
  options=["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل داده‌ها", "گزارش‌گیری", "تنظیمات"],
@@ -762,942 +336,139 @@ selected = option_menu(
762
  orientation="horizontal",
763
  styles={
764
  "container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "20px"},
765
- "icon": {"color": "#1a8754", "font-size": "18px"},
766
  "nav-link": {"font-size": "16px", "text-align": "center", "margin":"0px", "--hover-color": "#e9f7ef", "border-radius": "10px"},
767
- "nav-link-selected": {"background-color": "#1a8754", "color": "white", "font-weight": "600"},
768
  }
769
  )
770
 
771
  # Dashboard
772
  if selected == "داشبورد":
773
- # Dashboard metrics
774
- col1, col2, col3, col4 = st.columns(4)
775
-
776
- with col1:
777
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
778
- st.markdown(f'<div class="metric-value">{len(farm_df["Farm_ID"].unique())}</div>', unsafe_allow_html=True)
779
- st.markdown('<div class="metric-label">تعداد مز��رع</div>', unsafe_allow_html=True)
780
- st.markdown('</div>', unsafe_allow_html=True)
781
-
782
- with col2:
783
- active_farms = int(len(farm_df["Farm_ID"].unique()) * 0.85)
784
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
785
- st.markdown(f'<div class="metric-value">{active_farms}</div>', unsafe_allow_html=True)
786
- st.markdown('<div class="metric-label">مزارع فعال</div>', unsafe_allow_html=True)
787
- st.markdown('</div>', unsafe_allow_html=True)
788
 
789
- with col3:
790
- avg_height = farm_df['CurrentHeight'].mean()
791
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
792
- st.markdown(f'<div class="metric-value">{avg_height:.1f} cm</div>', unsafe_allow_html=True)
793
- st.markdown('<div class="metric-label">میانگین ارتفاع</div>', unsafe_allow_html=True)
794
- st.markdown('</div>', unsafe_allow_html=True)
795
-
796
- with col4:
797
- avg_moisture = farm_df['CurrentMoisture'].mean()
798
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
799
- st.markdown(f'<div class="metric-value">{avg_moisture:.1f}%</div>', unsafe_allow_html=True)
800
- st.markdown('<div class="metric-label">میانگین رطوبت</div>', unsafe_allow_html=True)
801
- st.markdown('</div>', unsafe_allow_html=True)
802
 
803
- # Dashboard tabs
804
- tab1, tab2, tab3, tab4 = st.tabs(["نمای کلی", "نقشه مزارع", "نمودارها", "داده‌ها"])
 
 
 
 
 
805
 
806
- with tab1:
807
- st.markdown("### توزیع واریته‌ها و سن محصول")
808
-
809
- col1, col2 = st.columns(2)
810
-
811
- with col1:
812
- variety_counts = farm_df['Variety'].value_counts().reset_index()
813
- variety_counts.columns = ['Variety', 'Count']
814
- fig = px.pie(
815
- variety_counts,
816
- values='Count',
817
- names='Variety',
818
- title='توزیع واریته‌ها',
819
- color_discrete_sequence=px.colors.sequential.Greens_r
820
- )
821
- fig.update_traces(textposition='inside', textinfo='percent+label')
822
- fig.update_layout(
823
- font=dict(family="Vazirmatn"),
824
- legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5)
825
- )
826
- st.plotly_chart(fig, use_container_width=True)
827
-
828
- with col2:
829
- age_counts = farm_df['Age'].value_counts().reset_index()
830
- age_counts.columns = ['Age', 'Count']
831
- fig = px.pie(
832
- age_counts,
833
- values='Count',
834
- names='Age',
835
- title='توزیع سن محصول',
836
- color_discrete_sequence=px.colors.sequential.Blues_r
837
- )
838
- fig.update_traces(textposition='inside', textinfo='percent+label')
839
- fig.update_layout(
840
- font=dict(family="Vazirmatn"),
841
- legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5)
842
- )
843
- st.plotly_chart(fig, use_container_width=True)
844
-
845
- st.markdown("### اطلاعات کلی مزارع")
846
-
847
- total_area = farm_df['Area'].sum()
848
-
849
- col1, col2, col3 = st.columns(3)
850
- col1.metric("تعداد کل مزارع", f"{len(farm_df['Farm_ID'].unique())}")
851
- col2.metric("مساحت کل (هکتار)", f"{total_area:.2f}")
852
- col3.metric("تعداد کانال‌ها", f"{farm_df['Channel'].nunique()}")
853
-
854
- st.markdown('<hr style="height:2px;border:none;color:#1a8754;background-color:#1a8754;margin:30px 0;">', unsafe_allow_html=True)
855
-
856
- st_lottie(lottie_farm, height=300, key="farm_animation")
857
 
858
- with tab2:
859
- st.markdown("### نقشه مزارع")
860
-
861
- if coordinates_df is not None and not coordinates_df.empty:
862
- m = folium.Map(location=[31.45, 48.72], zoom_start=12, tiles='CartoDB positron')
863
- for _, farm in coordinates_df.iterrows():
864
- lat = farm['Latitude']
865
- lon = farm['Longitude']
866
- name = farm['Farm_ID']
867
- farm_info = farm_df[farm_df['Farm_ID'] == name]
868
- if not farm_info.empty:
869
- variety = farm_info['Variety'].iloc[0]
870
- age = farm_info['Age'].iloc[0]
871
- area = farm_info['Area'].iloc[0]
872
- popup_text = f"""
873
- <div style="direction: rtl; text-align: right; font-family: 'Vazirmatn', sans-serif;">
874
- <h4>مزرعه {name}</h4>
875
- <p>واریته: {variety}</p>
876
- <p>سن: {age}</p>
877
- <p>مساحت: {area} هکتار</p>
878
- </div>
879
- """
880
- else:
881
- popup_text = f"<div style='direction: rtl;'>مزرعه {name}</div>"
882
- folium.Marker(
883
- [lat, lon],
884
- popup=folium.Popup(popup_text, max_width=300),
885
- tooltip=f"مزرعه {name}",
886
- icon=folium.Icon(color='green', icon='leaf')
887
- ).add_to(m)
888
- st.markdown('<div class="map-container">', unsafe_allow_html=True)
889
- folium_static(m, width=1000, height=600)
890
- st.markdown('</div>', unsafe_allow_html=True)
891
- else:
892
- st.warning("داده‌های مختصات در دسترس نیست.")
893
 
894
- with tab3:
895
- st.markdown("### نمودار رشد هفتگی")
896
-
897
- col1, col2 = st.columns(2)
898
- with col1:
899
- selected_variety = st.selectbox(
900
- "انتخاب واریته",
901
- ["all"] + list(farm_df['Variety'].unique()),
902
- format_func=lambda x: "همه واریته‌ها" if x == "all" else x
903
- )
904
-
905
- with col2:
906
- selected_age = st.selectbox(
907
- "انتخاب سن",
908
- ["all"] + list(farm_df['Age'].unique()),
909
- format_func=lambda x: "همه سنین" if x == "all" else x
910
- )
911
-
912
- growth_data = generate_real_growth_data(selected_variety, selected_age)
913
-
914
- chart_tab1, chart_tab2 = st.tabs(["میانگین رشد", "رشد مزارع فردی"])
915
-
916
- with chart_tab1:
917
- avg_data = growth_data['average']
918
- fig = go.Figure()
919
- fig.add_trace(go.Scatter(
920
- x=avg_data['weeks'],
921
- y=avg_data['heights'],
922
- mode='lines+markers',
923
- name='میانگین رشد',
924
- line=dict(color='#1a8754', width=3),
925
- marker=dict(size=8, color='#1a8754')
926
- ))
927
- fig.update_layout(
928
- title='میانگین رشد هفتگی',
929
- xaxis_title='هفته',
930
- yaxis_title='ارتفاع (سانتی‌متر)',
931
- font=dict(family='Vazirmatn', size=14),
932
- hovermode='x unified',
933
- template='plotly_white',
934
- height=500
935
- )
936
- st.plotly_chart(fig, use_container_width=True)
937
-
938
- with chart_tab2:
939
- if growth_data['individual']:
940
- fig = go.Figure()
941
- colors = ['#1a8754', '#1976d2', '#e65100', '#9c27b0', '#d32f2f']
942
- for i, farm_data in enumerate(growth_data['individual'][:5]):
943
- fig.add_trace(go.Scatter(
944
- x=farm_data['weeks'],
945
- y=farm_data['heights'],
946
- mode='lines+markers',
947
- name=f"مزرعه {farm_data['farm_id']}",
948
- line=dict(color=colors[i % len(colors)], width=2),
949
- marker=dict(size=6, color=colors[i % len(colors)])
950
- ))
951
- fig.update_layout(
952
- title='رشد هفتگی مزارع فردی',
953
- xaxis_title='هفته',
954
- yaxis_title='ارتفاع (سانتی‌متر)',
955
- font=dict(family='Vazirmatn', size=14),
956
- hovermode='x unified',
957
- template='plotly_white',
958
- height=500
959
- )
960
- st.plotly_chart(fig, use_container_width=True)
961
- else:
962
- st.warning("داده‌ای برای نمایش وجود ندارد.")
963
 
964
- with tab4:
965
- st.markdown("### داده‌های مزارع")
966
-
967
- search_term = st.text_input("جستجو در داده‌ها", placeholder="نام مزرعه، واریته، سن و...")
968
-
969
- if search_term:
970
- filtered_df = farm_df[
971
- farm_df['Farm_ID'].astype(str).str.contains(search_term) |
972
- farm_df['Variety'].astype(str).str.contains(search_term) |
973
- farm_df['Age'].astype(str).str.contains(search_term) |
974
- farm_df['Channel'].astype(str).str.contains(search_term)
975
- ]
976
- else:
977
- filtered_df = farm_df
978
-
979
- if not filtered_df.empty:
980
- csv = filtered_df.to_csv(index=False).encode('utf-8')
981
- st.download_button(
982
- label="دانلود داده‌ها (CSV)",
983
- data=csv,
984
- file_name="farm_data.csv",
985
- mime="text/csv",
986
- )
987
- st.dataframe(
988
- filtered_df,
989
- use_container_width=True,
990
- height=400,
991
- hide_index=True
992
- )
993
- st.info(f"نمایش {len(filtered_df)} مزرعه از {len(farm_df)} مزرعه")
994
- else:
995
- st.warning("هیچ داده‌ای یافت نشد.")
996
 
997
  # Map Page
998
  elif selected == "نقشه مزارع":
999
  st.markdown("## نقشه مزارع با شاخص‌های ماهواره‌ای")
1000
-
1001
- col1, col2 = st.columns([1, 3])
1002
-
1003
- with col1:
1004
- st.markdown('<div class="glass-card">', unsafe_allow_html=True)
1005
- st.markdown("### تنظیمات نقشه")
1006
-
1007
- selected_farm = st.selectbox(
1008
- "انتخاب مزرعه",
1009
- options=coordinates_df['Farm_ID'].tolist(),
1010
- index=0,
1011
- format_func=lambda x: f"مزرعه {x}"
1012
- )
1013
-
1014
- selected_date = st.date_input(
1015
- "انتخاب تاریخ",
1016
- value=datetime.now(),
1017
- format="YYYY-MM-DD"
1018
- )
1019
-
1020
- selected_layer = st.selectbox(
1021
- "انتخاب شاخص",
1022
- options=["NDVI", "NDMI", "EVI", "NDWI"],
1023
- format_func=lambda x: {
1024
- "NDVI": "شاخص پوشش گیاهی (NDVI)",
1025
- "NDMI": "شاخص رطوبت (NDMI)",
1026
- "EVI": "شاخص پیشرفته گیاهی (EVI)",
1027
- "NDWI": "شاخص آب (NDWI)"
1028
- }[x]
1029
- )
1030
-
1031
- generate_map = st.button(
1032
- "تولید نقشه",
1033
- type="primary",
1034
- use_container_width=True
1035
- )
1036
-
1037
- st.markdown('<hr style="margin: 20px 0;">', unsafe_allow_html=True)
1038
-
1039
- st.markdown("### راهنمای شاخص‌ها")
1040
-
1041
- with st.expander("شاخص پوشش گیاهی (NDVI)", expanded=selected_layer == "NDVI"):
1042
- st.markdown("""
1043
- **شاخص تفاضل نرمال‌شده پوشش گیاهی (NDVI)** معیاری برای سنجش سلامت و تراکم پوشش گیاهی است.
1044
-
1045
- - **مقادیر بالا (0.6 تا 1.0)**: پوشش گیاهی متراکم و سالم
1046
- - **مقادیر متوسط (0.2 تا 0.6)**: پوشش گیاهی متوسط
1047
- - **مقادیر پایین (-1.0 تا 0.2)**: پوشش گیاهی کم یا خاک لخت
1048
-
1049
- فرمول: NDVI = (NIR - RED) / (NIR + RED)
1050
- """)
1051
-
1052
- with st.expander("شاخص رطوبت (NDMI)", expanded=selected_layer == "NDMI"):
1053
- st.markdown("""
1054
- **شاخص تفاضل نرمال‌شده رطوبت (NDMI)** برای ارزیابی محتوای رطوبت گیاهان استفاده می‌شود.
1055
-
1056
- - **مقادیر بالا (0.4 تا 1.0)**: محتوای رطوبت بالا
1057
- - **مقادیر متوسط (0.0 تا 0.4)**: محتوای رطوبت متوسط
1058
- - **مقادیر پایین (-1.0 تا 0.0)**: محتوای رطوبت کم
1059
-
1060
- فرمول: NDMI = (NIR - SWIR) / (NIR + SWIR)
1061
- """)
1062
-
1063
- with st.expander("شاخص پیشرفته گیاهی (EVI)", expanded=selected_layer == "EVI"):
1064
- st.markdown("""
1065
- **شاخص پیشرفته پوشش گیاهی (EVI)** نسخه بهبودیافته NDVI است که حساسیت کمتری به اثرات خاک و اتمسفر دارد.
1066
-
1067
- - **مقادیر بالا (0.4 تا 1.0)**: پوشش گیاهی متراکم و سالم
1068
- - **مقادیر متوسط (0.2 تا 0.4)**: پوشش گیاهی متوسط
1069
- - **مقادیر پایین (0.0 تا 0.2)**: پوشش گیاهی کم
1070
-
1071
- فرمول: EVI = 2.5 * ((NIR - RED) / (NIR + 6*RED - 7.5*BLUE + 1))
1072
- """)
1073
-
1074
- with st.expander("شاخص آب (NDWI)", expanded=selected_layer == "NDWI"):
1075
- st.markdown("""
1076
- **شاخص تفاضل نرمال‌شده آب (NDWI)** برای شناسایی پهنه‌های آبی و ارزیابی محتوای آب در گیاهان استفاده می‌شود.
1077
-
1078
- - **مقادیر بالا (0.3 تا 1.0)**: پهنه‌های آبی
1079
- - **مقادیر متوسط (0.0 تا 0.3)**: محتوای آب متوسط
1080
- - **مقادیر پایین (-1.0 تا 0.0)**: محتوای آب کم یا خاک خشک
1081
-
1082
- فرمول: NDWI = (GREEN - NIR) / (GREEN + NIR)
1083
- """)
1084
-
1085
- st.markdown('</div>', unsafe_allow_html=True)
1086
-
1087
- with col2:
1088
- map_tab, stats_tab = st.tabs(["نقشه", "آمار و تحلیل"])
1089
-
1090
- with map_tab:
1091
- st.markdown('<div class="map-container">', unsafe_allow_html=True)
1092
- if generate_map or 'last_map' not in st.session_state:
1093
- with st.spinner('در حال تولید نقشه...'):
1094
- m = create_ee_map(
1095
- selected_farm,
1096
- selected_date.strftime('%Y-%m-%d'),
1097
- selected_layer
1098
- )
1099
- if m:
1100
- st.session_state.last_map = m
1101
- folium_static(m, width=800, height=600)
1102
- st.success(f"نقشه {selected_layer} برای مزرعه {selected_farm} با موفقیت تولید شد.")
1103
- else:
1104
- st.error("خطا در تولید نقشه. لطفاً دوباره تلاش کنید.")
1105
- elif 'last_map' in st.session_state:
1106
- folium_static(st.session_state.last_map, width=800, height=600)
1107
- st.markdown('</div>', unsafe_allow_html=True)
1108
- st.info("""
1109
- **نکته:** این نقشه بر اساس تصاویر ماهواره‌ای Sentinel-2 تولید شده است.
1110
- برای دقت بیشتر، تاریخی را انتخاب کنید که ابرناکی کمتری داشته باشد.
1111
- """)
1112
-
1113
- with stats_tab:
1114
- if 'last_map' in st.session_state:
1115
- stats = calculate_farm_stats(selected_farm, selected_layer)
1116
-
1117
- col1, col2, col3, col4 = st.columns(4)
1118
-
1119
- with col1:
1120
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1121
- st.markdown(f'<div class="metric-value">{stats["mean"]:.2f}</div>', unsafe_allow_html=True)
1122
- st.markdown(f'<div class="metric-label">میانگین {selected_layer}</div>', unsafe_allow_html=True)
1123
- st.markdown('</div>', unsafe_allow_html=True)
1124
-
1125
- with col2:
1126
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1127
- st.markdown(f'<div class="metric-value">{stats["max"]:.2f}</div>', unsafe_allow_html=True)
1128
- st.markdown(f'<div class="metric-label">حداکثر {selected_layer}</div>', unsafe_allow_html=True)
1129
- st.markdown('</div>', unsafe_allow_html=True)
1130
-
1131
- with col3:
1132
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1133
- st.markdown(f'<div class="metric-value">{stats["min"]:.2f}</div>', unsafe_allow_html=True)
1134
- st.markdown(f'<div class="metric-label">حداقل {selected_layer}</div>', unsafe_allow_html=True)
1135
- st.markdown('</div>', unsafe_allow_html=True)
1136
-
1137
- with col4:
1138
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1139
- st.markdown(f'<div class="metric-value">{stats["std_dev"]:.2f}</div>', unsafe_allow_html=True)
1140
- st.markdown(f'<div class="metric-label">انحراف معیار</div>', unsafe_allow_html=True)
1141
- st.markdown('</div>', unsafe_allow_html=True)
1142
-
1143
- fig = px.histogram(
1144
- x=stats["histogram_data"],
1145
- nbins=20,
1146
- title=f"توزیع مقادیر {selected_layer} در مزرعه {selected_farm}",
1147
- labels={"x": f"مقدار {selected_layer}", "y": "فراوانی"},
1148
- color_discrete_sequence=["#1a8754"]
1149
- )
1150
- fig.update_layout(
1151
- font=dict(family="Vazirmatn"),
1152
- template="plotly_white",
1153
- bargap=0.1
1154
- )
1155
- st.plotly_chart(fig, use_container_width=True)
1156
-
1157
- dates = pd.date_range(end=selected_date, periods=30, freq='D')
1158
- values = [stats["mean"] + np.random.normal(0, stats["std_dev"] / 2) for _ in range(30)]
1159
- values = np.clip(values, stats["min"], stats["max"])
1160
-
1161
- fig = px.line(
1162
- x=dates,
1163
- y=values,
1164
- title=f"روند تغییرات {selected_layer} در 30 روز گذشته",
1165
- labels={"x": "تاریخ", "y": f"مقدار {selected_layer}"},
1166
- markers=True
1167
- )
1168
- fig.update_layout(
1169
- font=dict(family="Vazirmatn"),
1170
- template="plotly_white",
1171
- hovermode="x unified"
1172
- )
1173
- st.plotly_chart(fig, use_container_width=True)
1174
-
1175
- farm_names = coordinates_df['Farm_ID'].tolist()[:5]
1176
- comparison_values = [stats["mean"] + np.random.uniform(-0.2, 0.2) for _ in range(len(farm_names))]
1177
-
1178
- fig = px.bar(
1179
- x=farm_names,
1180
- y=comparison_values,
1181
- title=f"مقایسه {selected_layer} بین مزارع",
1182
- labels={"x": "مزرعه", "y": f"مقدار {selected_layer}"},
1183
- color=comparison_values,
1184
- color_continuous_scale="Viridis"
1185
- )
1186
- fig.update_layout(
1187
- font=dict(family="Vazirmatn"),
1188
- template="plotly_white",
1189
- coloraxis_showscale=False
1190
- )
1191
- st.plotly_chart(fig, use_container_width=True)
1192
  else:
1193
- st.warning("لطفاً ابتدا یک نقشه تولید کنید.")
1194
 
1195
  # Data Entry Page
1196
  elif selected == "ورود اطلاعات":
1197
  st.markdown("## ورود اطلاعات روزانه مزارع")
1198
-
1199
  tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"])
1200
 
1201
  with tab1:
1202
- col1, col2 = st.columns(2)
1203
-
1204
- with col1:
1205
- selected_week = st.selectbox(
1206
- "انتخاب هفته",
1207
- options=[str(i) for i in range(1, 23)],
1208
- format_func=lambda x: f"هفته {x}"
1209
- )
1210
-
1211
- with col2:
1212
- days = day_df['Day'].unique().tolist()
1213
- selected_day = st.selectbox("انتخاب روز", options=days)
1214
-
1215
- filtered_farms = farm_df[farm_df['Week'] == int(selected_week)]
1216
- filtered_farms = filtered_farms[filtered_farms['Farm_ID'].isin(day_df[day_df['Day'] == selected_day]['Farm_ID'])]
1217
-
1218
- if filtered_farms.empty:
1219
- st.warning(f"هیچ مزرعه‌ای برای هفته {selected_week} و روز {selected_day} در پایگاه داده وجود ندارد.")
1220
- else:
1221
- st.markdown("### ورود داده‌های مزارع")
1222
-
1223
- data_key = f"data_{selected_week}_{selected_day}"
1224
  if data_key not in st.session_state:
1225
  st.session_state[data_key] = pd.DataFrame({
1226
  'Farm_ID': filtered_farms['Farm_ID'],
1227
- 'Station1': [0] * len(filtered_farms),
1228
- 'Station2': [0] * len(filtered_farms),
1229
- 'Station3': [0] * len(filtered_farms),
1230
- 'Station4': [0] * len(filtered_farms),
1231
- 'Station5': [0] * len(filtered_farms),
1232
- 'Well1': [0] * len(filtered_farms),
1233
- 'Well2': [0] * len(filtered_farms),
1234
- 'CurrentMoisture': [0] * len(filtered_farms),
1235
- 'CurrentNitrogen': [0] * len(filtered_farms),
1236
- 'CurrentHeight': [0] * len(filtered_farms)
1237
  })
1238
-
1239
- edited_df = st.data_editor(
1240
- st.session_state[data_key],
1241
- use_container_width=True,
1242
- num_rows="fixed",
1243
- column_config={
1244
- "Farm_ID": st.column_config.TextColumn("مزرعه", disabled=True),
1245
- "Station1": st.column_config.NumberColumn("ایستگاه 1", min_value=0, max_value=300, step=1),
1246
- "Station2": st.column_config.NumberColumn("ایستگاه 2", min_value=0, max_value=300, step=1),
1247
- "Station3": st.column_config.NumberColumn("ایستگاه 3", min_value=0, max_value=300, step=1),
1248
- "Station4": st.column_config.NumberColumn("ایستگاه 4", min_value=0, max_value=300, step=1),
1249
- "Station5": st.column_config.NumberColumn("ایستگاه 5", min_value=0, max_value=300, step=1),
1250
- "Well1": st.column_config.NumberColumn("چاهک 1", min_value=0, max_value=300, step=1),
1251
- "Well2": st.column_config.NumberColumn("چاهک 2", min_value=0, max_value=300, step=1),
1252
- "CurrentMoisture": st.column_config.NumberColumn("رطوبت غلاف", min_value=0, max_value=100, step=1),
1253
- "CurrentNitrogen": st.column_config.NumberColumn("نیتروژن", min_value=0, max_value=100, step=1),
1254
- "CurrentHeight": st.column_config.NumberColumn("میانگین ارتفاع", disabled=True),
1255
- },
1256
- hide_index=True
1257
- )
1258
-
1259
- for i in range(len(edited_df)):
1260
- stations = [
1261
- edited_df.iloc[i]['Station1'],
1262
- edited_df.iloc[i]['Station2'],
1263
- edited_df.iloc[i]['Station3'],
1264
- edited_df.iloc[i]['Station4'],
1265
- edited_df.iloc[i]['Station5']
1266
- ]
1267
- valid_stations = [s for s in stations if s > 0]
1268
- if valid_stations:
1269
- edited_df.iloc[i, edited_df.columns.get_loc('CurrentHeight')] = round(sum(valid_stations) / len(valid_stations), 1)
1270
-
1271
- st.session_state[data_key] = edited_df
1272
-
1273
- if st.button("ذخیره اطلاعات", type="primary", use_container_width=True):
1274
- new_data = edited_df.copy()
1275
- new_data['Week'] = int(selected_week)
1276
- new_data['Measurement_Date'] = (datetime.now() - timedelta(weeks=(22 - int(selected_week)))).strftime('%Y-%m-%d')
1277
- new_data['Variety'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Variety'])
1278
- new_data['Age'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Age'])
1279
- new_data['Area'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Area'])
1280
- new_data['Channel'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Channel'])
1281
- new_data['Administration'] = new_data['Farm_ID'].map(farm_df.set_index('Farm_ID')['Administration'])
1282
-
1283
- st.session_state.heights_df = pd.concat([st.session_state.heights_df, new_data], ignore_index=True)
1284
- st.success(f"داده‌های هفته {selected_week} برای روز {selected_day} با موفقیت ذخیره شدند.")
1285
- st.balloons()
1286
 
1287
  with tab2:
1288
- st.markdown("### آپلود فایل اکسل")
1289
-
1290
- uploaded_file = st.file_uploader("فایل اکسل خود را آپلود کنید", type=["xlsx", "xls", "csv"])
1291
-
1292
- if uploaded_file is not None:
1293
  try:
1294
- if uploaded_file.name.endswith('.csv'):
1295
- df = pd.read_csv(uploaded_file)
1296
- else:
1297
- df = pd.read_excel(uploaded_file)
1298
- st.dataframe(df, use_container_width=True)
1299
-
1300
- if st.button("ذخیره فایل", type="primary"):
1301
  st.session_state.heights_df = pd.concat([st.session_state.heights_df, df], ignore_index=True)
1302
- st.success("فایل با موفقیت ذخیره شد.")
1303
- st.balloons()
1304
  except Exception as e:
1305
  st.error(f"خطا در خواندن فایل: {e}")
1306
-
1307
- st.markdown("### راهنمای فرمت فایل")
1308
- st.markdown("""
1309
- فایل اکسل باید شامل ستون‌های زیر باشد:
1310
-
1311
- - Farm_ID
1312
- - Station1 تا Station5
1313
- - Well1 و Well2
1314
- - CurrentMoisture
1315
- - CurrentNitrogen
1316
-
1317
- می‌توانید از [این فایل نمونه](https://example.com/sample.xlsx) به عنوان الگو استفاده کنید.
1318
- """)
1319
-
1320
- st.markdown("""
1321
- <div style="border: 2px dashed #1a8754; border-radius: 10px; padding: 40px; text-align: center; margin: 20px 0;">
1322
- <svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#1a8754" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1323
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
1324
- <polyline points="17 8 12 3 7 8"></polyline>
1325
- <line x1="12" y1="3" x2="12" y2="15"></line>
1326
- </svg>
1327
- <p style="margin-top: 10px; color: #1a8754;">فایل خود را اینجا رها کنید یا روی دکمه بالا کلیک کنید</p>
1328
- </div>
1329
- """, unsafe_allow_html=True)
1330
 
1331
  # Data Analysis Page
1332
  elif selected == "تحلیل داده‌ها":
1333
  st.markdown("## تحلیل هوشمند داده‌ها")
1334
-
1335
- col1, col2 = st.columns([1, 2])
1336
-
1337
- with col1:
1338
- st_lottie(lottie_analysis, height=200, key="analysis_animation")
1339
-
1340
- with col2:
1341
- st.markdown("""
1342
- <div class="glass-card">
1343
- <h3 class="gradient-text">تحلیل پیشرفته داده‌های مزارع</h3>
1344
- <p>در این بخش می‌توانید تحلیل‌های پیشرفته روی داده‌های مزارع انجام دهید و روندها و الگوهای مختلف را بررسی کنید.</p>
1345
- </div>
1346
- """, unsafe_allow_html=True)
1347
-
1348
- tab1, tab2, tab3, tab4 = st.tabs(["تحلیل رشد", "مقایسه واریته‌ها", "تحلیل رطوبت", "پیش‌بینی"])
1349
-
1350
- with tab1:
1351
- st.markdown("### تحلیل رشد مزارع")
1352
-
1353
- col1, col2 = st.columns(2)
1354
-
1355
- with col1:
1356
- selected_variety = st.selectbox(
1357
- "انتخاب واریته",
1358
- ["all"] + list(farm_df['Variety'].unique()),
1359
- format_func=lambda x: "همه واریته‌ها" if x == "all" else x,
1360
- key="growth_variety"
1361
- )
1362
-
1363
- with col2:
1364
- selected_age = st.selectbox(
1365
- "انتخاب سن",
1366
- ["all"] + list(farm_df['Age'].unique()),
1367
- format_func=lambda x: "همه سنین" if x == "all" else x,
1368
- key="growth_age"
1369
- )
1370
-
1371
- growth_data = generate_real_growth_data(selected_variety, selected_age)
1372
-
1373
- if growth_data['individual']:
1374
- chart_data = []
1375
- for farm_data in growth_data['individual']:
1376
- for i, week in enumerate(farm_data['weeks']):
1377
- chart_data.append({
1378
- 'Farm': farm_data['farm_id'],
1379
- 'Week': week,
1380
- 'Height': farm_data['heights'][i],
1381
- 'Variety': farm_data['variety'],
1382
- 'Age': farm_data['age']
1383
- })
1384
-
1385
- chart_df = pd.DataFrame(chart_data)
1386
-
1387
- chart = alt.Chart(chart_df).mark_line(point=True).encode(
1388
- x=alt.X('Week:Q', title='هفته'),
1389
- y=alt.Y('Height:Q', title='ارتفاع (سانتی‌متر)'),
1390
- color=alt.Color('Farm:N', title='مزرعه'),
1391
- tooltip=['Farm', 'Week', 'Height', 'Variety', 'Age']
1392
- ).properties(
1393
- width='container',
1394
- height=400,
1395
- title='روند رشد مزارع بر اساس هفته'
1396
- ).interactive()
1397
-
1398
- st.altair_chart(chart, use_container_width=True)
1399
-
1400
- st.markdown("### تحلیل نرخ رشد")
1401
-
1402
- growth_rates = []
1403
- for farm_data in growth_data['individual']:
1404
- heights = farm_data['heights']
1405
- for i in range(1, len(heights)):
1406
- if heights[i] > 0 and heights[i-1] > 0:
1407
- growth_rate = heights[i] - heights[i-1]
1408
- growth_rates.append({
1409
- 'Farm': farm_data['farm_id'],
1410
- 'Week': farm_data['weeks'][i],
1411
- 'Growth Rate': growth_rate,
1412
- 'Variety': farm_data['variety'],
1413
- 'Age': farm_data['age']
1414
- })
1415
-
1416
- growth_rate_df = pd.DataFrame(growth_rates)
1417
-
1418
- chart = alt.Chart(growth_rate_df).mark_bar().encode(
1419
- x=alt.X('Week:O', title='هفته'),
1420
- y=alt.Y('mean(Growth Rate):Q', title='نرخ رشد (سانتی‌متر در هفته)'),
1421
- color=alt.Color('Farm:N', title='مزرعه'),
1422
- tooltip=['Farm', 'Week', 'mean(Growth Rate)']
1423
- ).properties(
1424
- width='container',
1425
- height=400,
1426
- title='نرخ رشد هفتگی مزارع'
1427
- ).interactive()
1428
-
1429
- st.altair_chart(chart, use_container_width=True)
1430
- else:
1431
- st.warning("داده‌ای برای نمایش وجود ندارد.")
1432
-
1433
- with tab2:
1434
- st.markdown("### مقایسه واریته‌ها")
1435
-
1436
- variety_age_groups = farm_df.groupby(['Variety', 'Age']).size().reset_index(name='Count')
1437
-
1438
- fig = px.density_heatmap(
1439
- variety_age_groups,
1440
- x='Variety',
1441
- y='Age',
1442
- z='Count',
1443
- title='توزیع مزارع بر اساس واریته و سن',
1444
- color_continuous_scale='Viridis'
1445
- )
1446
- fig.update_layout(
1447
- font=dict(family="Vazirmatn"),
1448
- template="plotly_white",
1449
- xaxis_title="واریته",
1450
- yaxis_title="سن"
1451
- )
1452
- st.plotly_chart(fig, use_container_width=True)
1453
-
1454
- variety_heights = farm_df.groupby('Variety')['CurrentHeight'].apply(list).to_dict()
1455
-
1456
- fig = go.Figure()
1457
- for variety, heights in variety_heights.items():
1458
- fig.add_trace(go.Box(
1459
- y=heights,
1460
- name=variety,
1461
- boxpoints='outliers',
1462
- marker_color=f'hsl({hash(variety) % 360}, 70%, 50%)'
1463
- ))
1464
- fig.update_layout(
1465
- title='مقایسه ارتفاع بر اساس واریته',
1466
- yaxis_title='ارتفاع (سانتی‌متر)',
1467
- font=dict(family="Vazirmatn"),
1468
- template="plotly_white",
1469
- boxmode='group'
1470
- )
1471
- st.plotly_chart(fig, use_container_width=True)
1472
-
1473
- variety_stats = {}
1474
- for variety, heights in variety_heights.items():
1475
- variety_stats[variety] = {
1476
- 'میانگین': np.mean(heights),
1477
- 'میانه': np.median(heights),
1478
- 'انحراف معیار': np.std(heights),
1479
- 'حداقل': np.min(heights),
1480
- 'حداکثر': np.max(heights)
1481
- }
1482
- variety_stats_df = pd.DataFrame(variety_stats).T
1483
- st.dataframe(variety_stats_df, use_container_width=True)
1484
-
1485
- with tab3:
1486
- st.markdown("### تحلیل رطوبت مزارع")
1487
-
1488
- farms = farm_df['Farm_ID'].unique()[:10]
1489
- dates = pd.date_range(end=datetime.now(), periods=30, freq='D')
1490
-
1491
- moisture_data = []
1492
- for farm in farms:
1493
- farm_data = farm_df[farm_df['Farm_ID'] == farm]
1494
- for date in dates:
1495
- week_data = farm_data[farm_data['Week'] == (date.isocalendar()[1] % 23 + 1)]
1496
- moisture = week_data['CurrentMoisture'].mean() if not week_data.empty else np.random.uniform(50, 80)
1497
- moisture = max(0, min(100, moisture))
1498
- moisture_data.append({
1499
- 'Farm': farm,
1500
- 'Date': date,
1501
- 'Moisture': moisture
1502
- })
1503
-
1504
- moisture_df = pd.DataFrame(moisture_data)
1505
-
1506
- fig = px.line(
1507
- moisture_df,
1508
- x='Date',
1509
- y='Moisture',
1510
- color='Farm',
1511
- title='روند رطوبت مزارع در 30 روز گذشته',
1512
- labels={'Date': 'تاریخ', 'Moisture': 'رطوبت (%)', 'Farm': 'مزرعه'}
1513
- )
1514
- fig.update_layout(
1515
- font=dict(family="Vazirmatn"),
1516
- template="plotly_white",
1517
- hovermode="x unified"
1518
- )
1519
- st.plotly_chart(fig, use_container_width=True)
1520
-
1521
- st.markdown("### همبستگی رطوبت و ارتفاع")
1522
-
1523
- correlation_data = []
1524
- for farm in farms:
1525
- farm_data = farm_df[farm_df['Farm_ID'] == farm]
1526
- for _, row in farm_data.iterrows():
1527
- correlation_data.append({
1528
- 'Farm': farm,
1529
- 'Moisture': row['CurrentMoisture'],
1530
- 'Height': row['CurrentHeight']
1531
- })
1532
-
1533
- correlation_df = pd.DataFrame(correlation_data)
1534
-
1535
- fig = px.scatter(
1536
- correlation_df,
1537
- x='Moisture',
1538
- y='Height',
1539
- color='Farm',
1540
- title='همبستگی بین رطوبت و ارتفاع',
1541
- labels={'Moisture': 'رطوبت (%)', 'Height': 'ارتفاع (سانتی‌متر)', 'Farm': 'مزرعه'},
1542
- trendline='ols'
1543
- )
1544
- fig.update_layout(
1545
- font=dict(family="Vazirmatn"),
1546
- template="plotly_white"
1547
- )
1548
- st.plotly_chart(fig, use_container_width=True)
1549
-
1550
- correlation = correlation_df['Moisture'].corr(correlation_df['Height'])
1551
- st.info(f"ضریب همبستگی بین رطوبت و ارتفاع: {correlation:.2f}")
1552
-
1553
- with tab4:
1554
- st.markdown("### پیش‌بینی ر��د مزارع")
1555
-
1556
- selected_farm_for_prediction = st.selectbox(
1557
- "انتخاب مزرعه",
1558
- options=farm_df['Farm_ID'].tolist(),
1559
- format_func=lambda x: f"مزرعه {x}"
1560
- )
1561
-
1562
- farm_data = farm_df[farm_df['Farm_ID'] == selected_farm_for_prediction]
1563
- historical_weeks = farm_data['Week'].values
1564
- historical_heights = farm_data['CurrentHeight'].values
1565
-
1566
- if len(historical_weeks) > 1 and len(historical_heights) > 1:
1567
- model = LinearRegression()
1568
- model.fit(historical_weeks.reshape(-1, 1), historical_heights)
1569
-
1570
- future_weeks = np.array(range(max(historical_weeks) + 1, 30)).reshape(-1, 1)
1571
- future_heights = model.predict(future_weeks)
1572
- lower_bound = future_heights - 15
1573
- upper_bound = future_heights + 15
1574
-
1575
- fig = go.Figure()
1576
- fig.add_trace(go.Scatter(
1577
- x=historical_weeks,
1578
- y=historical_heights,
1579
- mode='lines+markers',
1580
- name='داده‌های تاریخی',
1581
- line=dict(color='#1a8754', width=3),
1582
- marker=dict(size=8, color='#1a8754')
1583
- ))
1584
- fig.add_trace(go.Scatter(
1585
- x=future_weeks.flatten(),
1586
- y=future_heights,
1587
- mode='lines',
1588
- name='پیش‌بینی',
1589
- line=dict(color='#1976d2', width=3, dash='dash')
1590
- ))
1591
- fig.add_trace(go.Scatter(
1592
- x=future_weeks.flatten(),
1593
- y=lower_bound,
1594
- mode='lines',
1595
- name='حد پایین',
1596
- line=dict(color='#d32f2f', width=1, dash='dot'),
1597
- showlegend=True
1598
- ))
1599
- fig.add_trace(go.Scatter(
1600
- x=future_weeks.flatten(),
1601
- y=upper_bound,
1602
- mode='lines',
1603
- name='حد بالا',
1604
- line=dict(color='#d32f2f', width=1, dash='dot'),
1605
- fill='tonexty',
1606
- showlegend=True
1607
- ))
1608
- fig.update_layout(
1609
- title=f'پیش‌بینی رشد مزرعه {selected_farm_for_prediction}',
1610
- xaxis_title='هفته',
1611
- yaxis_title='ارتفاع (سانتی‌متر)',
1612
- font=dict(family='Vazirmatn', size=14),
1613
- hovermode='x unified',
1614
- template='plotly_white',
1615
- height=500
1616
- )
1617
- st.plotly_chart(fig, use_container_width=True)
1618
- else:
1619
- st.warning("داده‌های کافی برای پیش‌بینی وجود ندارد.")
1620
 
1621
  # Report Generation Page
1622
  elif selected == "گزارش‌گیری":
1623
  st.markdown("## گزارش‌گیری")
1624
-
1625
- report_week = st.selectbox("انتخاب هفته برای گزارش", options=[str(i) for i in range(1, 23)])
1626
- report_day = st.selectbox("انتخاب روز برای گزارش", options=day_df['Day'].unique().tolist())
1627
-
1628
  report_df = st.session_state.heights_df[
1629
  (st.session_state.heights_df['Week'] == int(report_week)) &
1630
  (st.session_state.heights_df['Farm_ID'].isin(day_df[day_df['Day'] == report_day]['Farm_ID']))
1631
  ]
1632
-
1633
  if not report_df.empty:
1634
- st.markdown(f"### گزارش هفته {report_week} - روز {report_day}")
1635
- st.dataframe(report_df, use_container_width=True)
1636
-
1637
  csv = report_df.to_csv(index=False).encode('utf-8')
1638
- st.download_button(
1639
- label="دانلود گزارش (CSV)",
1640
- data=csv,
1641
- file_name=f"report_week_{report_week}_day_{report_day}.csv",
1642
- mime="text/csv",
1643
- )
1644
-
1645
- st_lottie(lottie_report, height=200, key="report_animation")
1646
  else:
1647
- st.warning(f"داده‌ای برای هفته {report_week} و روز {report_day} یافت نشد.")
1648
 
1649
  # Settings Page
1650
  elif selected == "تنظیمات":
1651
  st.markdown("## تنظیمات سامانه")
1652
-
1653
- st.markdown("""
1654
- <div class="glass-card">
1655
- <h3 class="gradient-text">تنظیمات پیشرفته</h3>
1656
- <p>در این بخش می‌توانید تنظیمات کلی سامانه، از جمله به‌روزرسانی داده‌ها و پیکربندی‌های پیشرفته را مدیریت کنید.</p>
1657
- </div>
1658
- """, unsafe_allow_html=True)
1659
-
1660
- st.markdown("### به‌روزرسانی داده‌ها")
1661
-
1662
- if st.button("بارگذاری مجدد داده‌ها", type="primary", use_container_width=True):
1663
  st.session_state.heights_df = load_farm_data()
1664
- st.success("داده‌ها با موفقیت به‌روزرسانی شدند.")
1665
-
1666
- st.markdown("### تنظیمات ظاهری")
1667
-
1668
- theme = st.radio(
1669
- "انتخاب تم",
1670
- options=["سبز (پیش‌فرض)", "آبی", "سفید"],
1671
- format_func=lambda x: x
1672
- )
1673
-
1674
- if theme == "آبی":
1675
- st.markdown("""
1676
- <style>
1677
- .main-header {background: linear-gradient(90deg, #1976d2 0%, #0d47a1 100%);}
1678
- .metric-card .metric-value {color: #1976d2;}
1679
- .stButton>button {background: linear-gradient(90deg, #1976d2 0%, #0d47a1 100%);}
1680
- .stProgress > div > div > div > div {background-color: #1976d2;}
1681
- </style>
1682
- """, unsafe_allow_html=True)
1683
- elif theme == "سفید":
1684
- st.markdown("""
1685
- <style>
1686
- .main-header {background: linear-gradient(90deg, #ffffff 0%, #f5f5f5 100%);}
1687
- .metric-card .metric-value {color: #333333;}
1688
- .stButton>button {background: linear-gradient(90deg, #ffffff 0%, #f5f5f5 100%); color: #333333;}
1689
- .stProgress > div > div > div > div {background-color: #333333;}
1690
- </style>
1691
- """, unsafe_allow_html=True)
1692
-
1693
- st.markdown("### اطلاعات تماس")
1694
- st.markdown("""
1695
- <div class="neumorphic-card">
1696
- <p>برای پشتیبانی یا مشکلات فنی، با ما تماس بگیرید:</p>
1697
- <p>ایمیل: [email protected]</p>
1698
- <p>تلفن: +98 21 12345678</p>
1699
- </div>
1700
- """, unsafe_allow_html=True)
1701
 
1702
  # Footer
1703
  st.markdown("""
 
37
  initial_sidebar_state="expanded"
38
  )
39
 
40
+ # Custom CSS with modern design and animations
41
  st.markdown("""
42
  <style>
43
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;200;300;400;500;600;700;800;900&display=swap');
 
53
 
54
  /* Header styling */
55
  .main-header {
56
+ background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%);
57
  padding: 1.5rem;
58
  border-radius: 12px;
59
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
 
64
  }
65
 
66
  @keyframes header-glow {
67
+ 0% { box-shadow: 0 8px 32px rgba(46, 204, 113, 0.1); }
68
+ 100% { box-shadow: 0 8px 32px rgba(46, 204, 113, 0.3); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  }
70
 
71
  .main-header h1 {
 
83
  z-index: 1;
84
  }
85
 
86
+ /* Metric card styling */
87
+ .metric-container {
88
+ display: flex;
89
+ justify-content: space-around;
90
+ flex-wrap: wrap;
91
+ gap: 20px;
92
+ padding: 20px;
93
+ }
94
+
95
+ .metric-card {
96
+ background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
97
+ border-radius: 15px;
98
+ padding: 20px;
99
+ width: 220px;
100
+ text-align: center;
101
+ color: white;
102
+ box-shadow: 0 10px 20px rgba(0,0,0,0.2);
103
+ transition: transform 0.3s ease, box-shadow 0.3s ease;
104
+ }
105
+
106
+ .metric-card:hover {
107
+ transform: translateY(-10px);
108
+ box-shadow: 0 15px 30px rgba(0,0,0,0.3);
109
+ }
110
+
111
+ .metric-icon { font-size: 2.5rem; margin-bottom: 10px; }
112
+ .metric-value { font-size: 2rem; font-weight: 700; }
113
+ .metric-label { font-size: 1rem; opacity: 0.9; }
114
+
115
  /* Navigation menu styling */
116
  .st-emotion-cache-1lcbz7b {
117
  background-color: transparent !important;
 
132
  }
133
 
134
  .st-emotion-cache-1lcbz7b .st-emotion-cache-1j7d69d[data-selected="true"] {
135
+ background-color: #2ecc71 !important;
136
  color: white !important;
137
  font-weight: 600 !important;
138
  }
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  /* Button styling */
141
  .stButton>button {
142
  border-radius: 50px;
 
144
  font-weight: 600;
145
  transition: all 0.3s ease;
146
  border: none;
147
+ background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%);
148
+ color: white;
149
  }
150
 
151
  .stButton>button:hover {
 
153
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
154
  }
155
 
 
 
 
 
 
 
 
 
 
 
 
156
  /* Footer styling */
157
  footer {
158
  position: fixed;
159
  left: 0;
160
  bottom: 0;
161
  width: 100%;
162
+ background-color: #2ecc71;
163
  color: white;
164
  text-align: center;
165
  padding: 10px 0;
 
166
  }
167
  </style>
168
  """, unsafe_allow_html=True)
 
172
  def load_farm_data():
173
  try:
174
  df = pd.read_csv("کراپ لاگ کلی (1).csv")
175
+ df.columns = [col.strip() for col in df.columns]
176
  df.rename(columns={
177
+ 'سال': 'Year', 'هفته': 'Week', 'مزرعه': 'Farm_ID', 'کانال': 'Channel', 'اداره': 'Administration',
178
+ 'مساحت': 'Area', 'مساحت زیر مجموعه': 'SubArea', 'رقم': 'Variety', 'سن': 'Age',
179
+ 'ایستگاه 1': 'Station1', 'ایستگاه 2': 'Station2', 'ایستگاه 3': 'Station3',
180
+ 'ایستگاه 4': 'Station4', 'ایستگاه 5': 'Station5', 'ارتفاع هفته جاری مزرعه': 'CurrentHeight',
181
+ 'ارتفاع هفته گذشته مزرعه': 'PreviousHeight', 'رشد هفته جاری': 'CurrentGrowth',
182
+ 'رشد هفته گذشته': 'PreviousGrowth', 'نیتروژن فعلی': 'CurrentNitrogen',
183
+ 'نیتروژن استاندارد فعلی': 'StandardNitrogen', 'نیتروژن قبلی': 'PreviousNitrogen',
184
+ 'نیتروژن استاندارد قبلی': 'PreviousStandardNitrogen', 'رطوبت غلاف فعلی': 'CurrentMoisture',
185
+ 'رطوبت استاندارد فعلی': 'StandardMoisture', 'رطوبت غلاف قبلی': 'PreviousMoisture',
186
+ 'رطوبت استاندارد قبلی': 'PreviousStandardMoisture', 'چاهک 1': 'Well1', 'تاریخ قرائت': 'Well1Date',
187
+ 'چاهک 2': 'Well2', 'تاریخ قرائت.1': 'Well2Date'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  }, inplace=True)
189
+ # Convert numeric columns to float and handle NaN
190
+ numeric_cols = ['Area', 'CurrentHeight', 'PreviousHeight', 'CurrentGrowth', 'PreviousGrowth',
191
+ 'CurrentNitrogen', 'PreviousNitrogen', 'CurrentMoisture', 'PreviousMoisture',
192
+ 'Station1', 'Station2', 'Station3', 'Station4', 'Station5', 'Well1', 'Well2']
193
+ for col in numeric_cols:
194
+ if col in df.columns:
195
+ df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
196
  return df
197
  except Exception as e:
198
  st.error(f"خطا در بارگذاری داده‌های مزارع: {e}")
 
203
  try:
204
  coords_df = pd.read_csv("farm_coordinates.csv")
205
  coords_df.rename(columns={
206
+ 'مزرعه': 'Farm_ID', 'عرض جغرافیایی': 'Latitude', 'طول جغرافیایی': 'Longitude'
 
 
207
  }, inplace=True)
208
  return coords_df
209
  except Exception as e:
 
214
  def load_day_data():
215
  try:
216
  day_df = pd.read_csv("پایگاه داده (1).csv")
217
+ day_df.rename(columns={'مزرعه': 'Farm_ID', 'روز': 'Day'}, inplace=True)
 
 
 
218
  return day_df
219
  except Exception as e:
220
  st.error(f"خطا در بارگذاری داده‌های روزهای هفته: {e}")
 
228
  return None
229
  return r.json()
230
 
231
+ # Initialize Earth Engine
232
  @st.cache_resource
233
  def initialize_earth_engine():
234
  try:
 
275
  if layer_type == "NDVI":
276
  index = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
277
  viz_params = {'min': -0.2, 'max': 0.8, 'palette': ['#ff0000', '#ff4500', '#ffd700', '#32cd32', '#006400']}
 
278
  elif layer_type == "NDMI":
279
  index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
280
  viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
 
 
 
 
 
 
 
 
 
 
 
 
281
  map_id_dict = ee.Image(index).getMapId(viz_params)
282
  folium.TileLayer(
283
  tiles=map_id_dict['tile_fetcher'].url_format,
 
286
  overlay=True,
287
  control=True
288
  ).add_to(m)
289
+ folium.Marker([lat, lon], popup=f'مزرعه {farm_id}', icon=folium.Icon(color='green', icon='leaf')).add_to(m)
 
 
 
 
 
 
 
 
 
 
 
 
 
290
  folium.LayerControl().add_to(m)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  return m
292
  except Exception as e:
293
  st.error(f"خطا در ایجاد نقشه: {e}")
294
  return None
295
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  # Generate real growth data
297
  def generate_real_growth_data(selected_variety="all", selected_age="all"):
298
  filtered_farms = farm_df
 
301
  if selected_age != "all":
302
  filtered_farms = filtered_farms[filtered_farms['Age'] == selected_age]
303
 
 
304
  weeks = filtered_farms['Week'].unique()
305
+ avg_heights = [filtered_farms[filtered_farms['Week'] == week]['CurrentHeight'].mean() for week in weeks]
306
+ return {'weeks': weeks, 'heights': avg_heights}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
 
308
  # Initialize Earth Engine and load data
309
  ee_initialized = initialize_earth_engine()
 
323
  # Main header
324
  st.markdown('<div class="main-header">', unsafe_allow_html=True)
325
  st.markdown('<h1>سامانه هوشمند پایش مزارع نیشکر دهخدا</h1>', unsafe_allow_html=True)
326
+ st.markdown('<p>پلتفرم جامع مدیریت، پایش و تحلیل داده‌های مزارع نیشکر</p>', unsafe_allow_html=True)
327
  st.markdown('</div>', unsafe_allow_html=True)
328
 
329
+ # Navigation menu
330
  selected = option_menu(
331
  menu_title=None,
332
  options=["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل داده‌ها", "گزارش‌گیری", "تنظیمات"],
 
336
  orientation="horizontal",
337
  styles={
338
  "container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "20px"},
339
+ "icon": {"color": "#2ecc71", "font-size": "18px"},
340
  "nav-link": {"font-size": "16px", "text-align": "center", "margin":"0px", "--hover-color": "#e9f7ef", "border-radius": "10px"},
341
+ "nav-link-selected": {"background-color": "#2ecc71", "color": "white", "font-weight": "600"},
342
  }
343
  )
344
 
345
  # Dashboard
346
  if selected == "داشبورد":
347
+ st.markdown('<div class="metric-container">', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
348
 
349
+ st.markdown("""
350
+ <div class="metric-card">
351
+ <div class="metric-icon">🌾</div>
352
+ <div class="metric-value">{}</div>
353
+ <div class="metric-label">تعداد مزارع</div>
354
+ </div>
355
+ """.format(int(len(farm_df["Farm_ID"].unique()))), unsafe_allow_html=True)
 
 
 
 
 
 
356
 
357
+ st.markdown("""
358
+ <div class="metric-card">
359
+ <div class="metric-icon">📏</div>
360
+ <div class="metric-value">{:.0f} ha</div>
361
+ <div class="metric-label">مساحت کل</div>
362
+ </div>
363
+ """.format(farm_df["Area"].sum()), unsafe_allow_html=True)
364
 
365
+ st.markdown("""
366
+ <div class="metric-card">
367
+ <div class="metric-icon">📈</div>
368
+ <div class="metric-value">{:.1f} cm</div>
369
+ <div class="metric-label">میانگین ارتفاع</div>
370
+ </div>
371
+ """.format(farm_df["CurrentHeight"].mean()), unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
 
373
+ st.markdown("""
374
+ <div class="metric-card">
375
+ <div class="metric-icon">💧</div>
376
+ <div class="metric-value">{:.1f}%</div>
377
+ <div class="metric-label">میانگین رطوبت</div>
378
+ </div>
379
+ """.format(farm_df["CurrentMoisture"].mean()), unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
380
 
381
+ st.markdown('</div>', unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
382
 
383
+ st.markdown("### نمودار رشد هفتگی")
384
+ growth_data = generate_real_growth_data()
385
+ fig = px.line(x=growth_data['weeks'], y=growth_data['heights'], title="رشد هفتگی", labels={'x': 'هفته', 'y': 'ارتفاع (سانتی‌متر)'})
386
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
387
 
388
  # Map Page
389
  elif selected == "نقشه مزارع":
390
  st.markdown("## نقشه مزارع با شاخص‌های ماهواره‌ای")
391
+ farm_id = st.selectbox("انتخاب مزرعه", coordinates_df['Farm_ID'].tolist())
392
+ date = st.date_input("انتخاب تاریخ", datetime.now())
393
+ layer_type = st.selectbox("انتخاب شاخص", ["NDVI", "NDMI"])
394
+ if st.button("تولید نقشه"):
395
+ with st.spinner('در حال تولید نقشه...'):
396
+ m = create_ee_map(farm_id, date.strftime('%Y-%m-%d'), layer_type)
397
+ if m:
398
+ folium_static(m, width=800, height=600)
399
+ st.success(f"نقشه {layer_type} برای مزرعه {farm_id} تولید شد.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  else:
401
+ st.error("خطا در تولید نقشه.")
402
 
403
  # Data Entry Page
404
  elif selected == "ورود اطلاعات":
405
  st.markdown("## ورود اطلاعات روزانه مزارع")
 
406
  tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"])
407
 
408
  with tab1:
409
+ week = st.selectbox("انتخاب هفته", [str(i) for i in range(1, 23)])
410
+ day = st.selectbox("انتخاب روز", day_df['Day'].unique())
411
+ filtered_farms = farm_df[farm_df['Week'] == int(week)]
412
+ if not filtered_farms.empty:
413
+ data_key = f"data_{week}_{day}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
414
  if data_key not in st.session_state:
415
  st.session_state[data_key] = pd.DataFrame({
416
  'Farm_ID': filtered_farms['Farm_ID'],
417
+ 'CurrentHeight': [0.0] * len(filtered_farms),
418
+ 'CurrentMoisture': [0.0] * len(filtered_farms)
 
 
 
 
 
 
 
 
419
  })
420
+ edited_df = st.data_editor(st.session_state[data_key], use_container_width=True)
421
+ if st.button("ذخیره اطلاعات"):
422
+ st.session_state.heights_df = pd.concat([st.session_state.heights_df, edited_df], ignore_index=True)
423
+ st.success("داده‌ها ذخیره شدند.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
 
425
  with tab2:
426
+ uploaded_file = st.file_uploader("آپلود فایل", type=["csv", "xlsx"])
427
+ if uploaded_file:
 
 
 
428
  try:
429
+ df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
430
+ numeric_cols = ['CurrentHeight', 'CurrentMoisture']
431
+ for col in numeric_cols:
432
+ if col in df.columns:
433
+ df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
434
+ st.dataframe(df)
435
+ if st.button("ذخیره فایل"):
436
  st.session_state.heights_df = pd.concat([st.session_state.heights_df, df], ignore_index=True)
437
+ st.success("فایل ذخیره شد.")
 
438
  except Exception as e:
439
  st.error(f"خطا در خواندن فایل: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
 
441
  # Data Analysis Page
442
  elif selected == "تحلیل داده‌ها":
443
  st.markdown("## تحلیل هوشمند داده‌ها")
444
+ growth_data = generate_real_growth_data()
445
+ fig = go.Figure()
446
+ fig.add_trace(go.Scatter(x=growth_data['weeks'], y=growth_data['heights'], mode='lines+markers', name='رشد'))
447
+ fig.update_layout(title='رشد هفتگی', xaxis_title='هفته', yaxis_title='ارتفاع (سانتی‌متر)')
448
+ st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
449
 
450
  # Report Generation Page
451
  elif selected == "گزارش‌گیری":
452
  st.markdown("## گزارش‌گیری")
453
+ report_week = st.selectbox("انتخاب هفته", [str(i) for i in range(1, 23)])
454
+ report_day = st.selectbox("انتخاب روز", day_df['Day'].unique())
 
 
455
  report_df = st.session_state.heights_df[
456
  (st.session_state.heights_df['Week'] == int(report_week)) &
457
  (st.session_state.heights_df['Farm_ID'].isin(day_df[day_df['Day'] == report_day]['Farm_ID']))
458
  ]
 
459
  if not report_df.empty:
460
+ st.dataframe(report_df)
 
 
461
  csv = report_df.to_csv(index=False).encode('utf-8')
462
+ st.download_button(label="دانلود گزارش", data=csv, file_name=f"report_week_{report_week}.csv")
 
 
 
 
 
 
 
463
  else:
464
+ st.warning(f"داده‌ای برای هفته {report_week} یافت نشد.")
465
 
466
  # Settings Page
467
  elif selected == "تنظیمات":
468
  st.markdown("## تنظیمات سامانه")
469
+ if st.button("بارگذاری مجدد داده‌ها"):
 
 
 
 
 
 
 
 
 
 
470
  st.session_state.heights_df = load_farm_data()
471
+ st.success("داده‌ها به‌روزرسانی شدند.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
 
473
  # Footer
474
  st.markdown("""