Esmaeilkianii commited on
Commit
9205504
·
verified ·
1 Parent(s): 030dfb7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +171 -1676
app.py CHANGED
@@ -1,7 +1,7 @@
1
- import streamlit as st
2
  import pandas as pd
3
  import numpy as np
4
- import folium#
5
  from streamlit_folium import folium_static
6
  import ee
7
  import os
@@ -28,6 +28,7 @@ from streamlit_card import card
28
  import pydeck as pdk
29
  import math
30
 
 
31
  # Page configuration with custom theme
32
  st.set_page_config(
33
  page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
@@ -40,15 +41,15 @@ st.set_page_config(
40
  st.markdown("""
41
  <style>
42
  @import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;200;300;400;500;600;700;800;900&display=swap');
43
-
44
  * {
45
  font-family: 'Vazirmatn', sans-serif !important;
46
  }
47
-
48
  .main {
49
  background: linear-gradient(135deg, #f5f7fa 0%, #e4e9f2 100%);
50
  }
51
-
52
  .main-header {
53
  background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
54
  padding: 1.5rem;
@@ -59,12 +60,12 @@ st.markdown("""
59
  overflow: hidden;
60
  animation: header-glow 3s infinite alternate;
61
  }
62
-
63
  @keyframes header-glow {
64
  0% { box-shadow: 0 8px 32px rgba(26, 135, 84, 0.1); }
65
  100% { box-shadow: 0 8px 32px rgba(26, 135, 84, 0.3); }
66
  }
67
-
68
  .main-header::before {
69
  content: '';
70
  position: absolute;
@@ -76,7 +77,7 @@ st.markdown("""
76
  transform: rotate(30deg);
77
  z-index: 0;
78
  }
79
-
80
  .main-header h1 {
81
  color: white;
82
  font-weight: 700;
@@ -84,26 +85,26 @@ st.markdown("""
84
  position: relative;
85
  z-index: 1;
86
  }
87
-
88
  .main-header p {
89
  color: rgba(255, 255, 255, 0.8);
90
  margin: 0;
91
  position: relative;
92
  z-index: 1;
93
  }
94
-
95
  .stcard {
96
  border-radius: 12px;
97
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
98
  transition: all 0.3s ease;
99
  overflow: hidden;
100
  }
101
-
102
  .stcard:hover {
103
  transform: translateY(-5px);
104
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
105
  }
106
-
107
  .stButton>button {
108
  border-radius: 50px;
109
  padding: 0.5rem 1.5rem;
@@ -111,23 +112,23 @@ st.markdown("""
111
  transition: all 0.3s ease;
112
  border: none;
113
  }
114
-
115
  .stButton>button:hover {
116
  transform: translateY(-2px);
117
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
118
  }
119
-
120
  .primary-btn {
121
  background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
122
  color: white;
123
  }
124
-
125
  .secondary-btn {
126
  background: white;
127
  color: #1a8754;
128
  border: 1px solid #1a8754 !important;
129
  }
130
-
131
  .metric-card {
132
  background: white;
133
  border-radius: 12px;
@@ -136,45 +137,45 @@ st.markdown("""
136
  transition: all 0.3s ease;
137
  text-align: center;
138
  }
139
-
140
  .metric-card:hover {
141
  transform: translateY(-5px);
142
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
143
  }
144
-
145
  .metric-card .metric-value {
146
  font-size: 2.5rem;
147
  font-weight: 700;
148
  color: #1a8754;
149
  margin-bottom: 0.5rem;
150
  }
151
-
152
  .metric-card .metric-label {
153
  font-size: 1rem;
154
  color: #6c757d;
155
  }
156
-
157
  .map-container {
158
  border-radius: 12px;
159
  overflow: hidden;
160
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
161
  }
162
-
163
  .stTabs [data-baseweb="tab-list"] {
164
  gap: 8px;
165
  }
166
-
167
  .stTabs [data-baseweb="tab"] {
168
  border-radius: 4px 4px 0px 0px;
169
  padding: 10px 16px;
170
  background-color: #f8f9fa;
171
  }
172
-
173
  .stTabs [aria-selected="true"] {
174
  background-color: #1a8754 !important;
175
  color: white !important;
176
  }
177
-
178
  [data-testid="stSidebar"] {
179
  background-color: #ffffff;
180
  border-right: 1px solid #e9ecef;
@@ -184,18 +185,18 @@ st.markdown("""
184
  0% { opacity: 0; transform: translateY(20px); }
185
  100% { opacity: 1; transform: translateY(0); }
186
  }
187
-
188
  .animate-fadeIn {
189
  animation: fadeIn 0.5s ease forwards;
190
  }
191
-
192
  .loading-spinner {
193
  display: flex;
194
  justify-content: center;
195
  align-items: center;
196
  height: 100px;
197
  }
198
-
199
  .loading-spinner::after {
200
  content: "";
201
  width: 40px;
@@ -205,41 +206,41 @@ st.markdown("""
205
  border-radius: 50%;
206
  animation: spin 1s linear infinite;
207
  }
208
-
209
  @keyframes spin {
210
  0% { transform: rotate(0deg); }
211
  100% { transform: rotate(360deg); }
212
  }
213
-
214
  .rtl {
215
  direction: rtl;
216
  text-align: right;
217
  }
218
-
219
  ::-webkit-scrollbar {
220
  width: 8px;
221
  height: 8px;
222
  }
223
-
224
  ::-webkit-scrollbar-track {
225
  background: #f1f1f1;
226
  border-radius: 10px;
227
  }
228
-
229
  ::-webkit-scrollbar-thumb {
230
  background: #1a8754;
231
  border-radius: 10px;
232
  }
233
-
234
  ::-webkit-scrollbar-thumb:hover {
235
  background: #115740;
236
  }
237
-
238
  .tooltip {
239
  position: relative;
240
  display: inline-block;
241
  }
242
-
243
  .tooltip .tooltiptext {
244
  visibility: hidden;
245
  width: 120px;
@@ -256,12 +257,12 @@ st.markdown("""
256
  opacity: 0;
257
  transition: opacity 0.3s;
258
  }
259
-
260
  .tooltip:hover .tooltiptext {
261
  visibility: visible;
262
  opacity: 1;
263
  }
264
-
265
  .dataframe {
266
  border-collapse: collapse;
267
  width: 100%;
@@ -269,31 +270,31 @@ st.markdown("""
269
  overflow: hidden;
270
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
271
  }
272
-
273
  .dataframe th {
274
  background-color: #1a8754;
275
  color: white;
276
  padding: 12px;
277
  text-align: right;
278
  }
279
-
280
  .dataframe td {
281
  padding: 10px 12px;
282
  border-bottom: 1px solid #e9ecef;
283
  }
284
-
285
  .dataframe tr:nth-child(even) {
286
  background-color: #f8f9fa;
287
  }
288
-
289
  .dataframe tr:hover {
290
  background-color: #e9ecef;
291
  }
292
-
293
  .stProgress > div > div > div > div {
294
  background-color: #1a8754;
295
  }
296
-
297
  .notification {
298
  background-color: #d1e7dd;
299
  color: #0f5132;
@@ -304,17 +305,17 @@ st.markdown("""
304
  align-items: center;
305
  animation: slideIn 0.5s ease;
306
  }
307
-
308
  @keyframes slideIn {
309
  0% { transform: translateX(100%); opacity: 0; }
310
  100% { transform: translateX(0); opacity: 1; }
311
  }
312
-
313
  .notification-icon {
314
  margin-right: 0.5rem;
315
  font-size: 1.2rem;
316
  }
317
-
318
  .custom-select {
319
  background-color: white;
320
  border-radius: 8px;
@@ -322,7 +323,7 @@ st.markdown("""
322
  border: 1px solid #ced4da;
323
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
324
  }
325
-
326
  .glass-card {
327
  background: rgba(255, 255, 255, 0.7);
328
  backdrop-filter: blur(10px);
@@ -332,36 +333,36 @@ st.markdown("""
332
  padding: 1.5rem;
333
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
334
  }
335
-
336
  .neumorphic-card {
337
  background: #f0f0f3;
338
  border-radius: 12px;
339
  box-shadow: 10px 10px 20px #d1d1d4, -10px -10px 20px #ffffff;
340
  padding: 1.5rem;
341
  }
342
-
343
  .gradient-text {
344
  background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
345
  -webkit-background-clip: text;
346
  -webkit-text-fill-color: transparent;
347
  font-weight: 700;
348
  }
349
-
350
  @keyframes pulse {
351
  0% { transform: scale(1); }
352
  50% { transform: scale(1.05); }
353
  100% { transform: scale(1); }
354
  }
355
-
356
  .pulse-animation {
357
  animation: pulse 2s infinite;
358
  }
359
-
360
  .stRadio > div {
361
  display: flex;
362
  gap: 10px;
363
  }
364
-
365
  .stRadio label {
366
  cursor: pointer;
367
  background-color: #f8f9fa;
@@ -369,27 +370,27 @@ st.markdown("""
369
  border-radius: 50px;
370
  transition: all 0.3s ease;
371
  }
372
-
373
  .stRadio label:hover {
374
  background-color: #e9ecef;
375
  }
376
-
377
  .stRadio input {
378
  display: none;
379
  }
380
-
381
  .stRadio input:checked + label {
382
  background-color: #1a8754;
383
  color: white;
384
  }
385
-
386
  .stSelectbox, .stNumberInput {
387
  background-color: #f0f2f6;
388
  border-radius: 10px;
389
  padding: 10px;
390
  margin: 10px 0;
391
  }
392
-
393
  .custom-card {
394
  background-color: white;
395
  padding: 20px;
@@ -397,13 +398,13 @@ st.markdown("""
397
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
398
  margin: 10px 0;
399
  }
400
-
401
  .metric-container {
402
  display: flex;
403
  justify-content: space-between;
404
  flex-wrap: wrap;
405
  }
406
-
407
  .metric-card {
408
  background-color: #1a8754;
409
  color: white;
@@ -435,16 +436,16 @@ def initialize_earth_engine():
435
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dehkhodamap-e9f0da4ce9f6514021%40ee-esmaeilkiani13877.iam.gserviceaccount.com",
436
  "universe_domain": "googleapis.com"
437
  }
438
-
439
  credentials_file = 'ee-esmaeilkiani13877-cfdea6eaf411.json'
440
  with open(credentials_file, 'w') as f:
441
  json.dump(credentials_dict, f)
442
-
443
  credentials = ee.ServiceAccountCredentials(service_account, credentials_file)
444
  ee.Initialize(credentials)
445
-
446
  os.remove(credentials_file)
447
-
448
  return True
449
  except Exception as e:
450
  st.error(f"خطا در اتصال به Earth Engine: {e}")
@@ -506,30 +507,41 @@ def create_ee_map(farm_id, date_str, layer_type="NDVI"):
506
  try:
507
  farm_row = coordinates_df[coordinates_df['مزرعه'] == farm_id].iloc[0]
508
  lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی']
509
-
510
  m = folium.Map(location=[lat, lon], zoom_start=14, tiles='CartoDB positron')
511
-
512
  date_obj = datetime.strptime(date_str, '%Y-%m-%d')
513
  start_date = (date_obj - timedelta(days=5)).strftime('%Y-%m-%d')
514
  end_date = (date_obj + timedelta(days=5)).strftime('%Y-%m-%d')
515
-
516
  region = ee.Geometry.Point([lon, lat]).buffer(1500)
517
-
518
- s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
519
- .filterDate(start_date, end_date) \
520
- .filterBounds(region) \
521
- .sort('CLOUDY_PIXEL_PERCENTAGE') \
522
- .first()
523
-
524
  if layer_type == "NDVI":
 
 
 
 
 
 
525
  index = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
526
  viz_params = {'min': -0.2, 'max': 0.8, 'palette': ['#ff0000', '#ff4500', '#ffd700', '#32cd32', '#006400']}
527
  legend_title = 'شاخص پوشش گیاهی (NDVI)'
528
  elif layer_type == "NDMI":
 
 
 
 
 
529
  index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
530
  viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
531
  legend_title = 'شاخص رطوبت (NDMI)'
532
  elif layer_type == "EVI":
 
 
 
 
 
533
  nir = s2.select('B8')
534
  red = s2.select('B4')
535
  blue = s2.select('B2')
@@ -537,35 +549,66 @@ def create_ee_map(farm_id, date_str, layer_type="NDVI"):
537
  viz_params = {'min': 0, 'max': 1, 'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#4caf50']}
538
  legend_title = 'شاخص پیشرفته گیاهی (EVI)'
539
  elif layer_type == "NDWI":
 
 
 
 
 
540
  index = s2.normalizedDifference(['B3', 'B8']).rename('NDWI')
541
  viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#00008b', '#00b7eb', '#add8e6', '#fdae61', '#d73027']}
542
  legend_title = 'شاخص آب (NDWI)'
543
  elif layer_type == "SoilMoisture":
544
- s1 = ee.ImageCollection('COPERNICUS/S1_GRD') \
545
- .filterDate(start_date, end_date) \
546
- .filterBounds(region) \
547
- .sort('system:time_start') \
548
- .first()
549
- index = s1.select('VV').rename('SoilMoisture')
550
- viz_params = {'min': -25, 'max': -5, 'palette': ['#00008b', '#add8e6', '#ffffff']}
551
- legend_title = 'رطوبت خاک (Soil Moisture)'
552
-
553
- map_id_dict = ee.Image(index).getMapId(viz_params)
554
- folium.TileLayer(
555
- tiles=map_id_dict['tile_fetcher'].url_format,
556
- attr='Google Earth Engine',
557
- name=layer_type,
558
- overlay=True,
559
- control=True
560
- ).add_to(m)
561
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  folium.Marker(
563
- [lat, lon],
564
- popup=f'مزرعه {farm_id}',
565
  tooltip=f'م��رعه {farm_id}',
566
  icon=folium.Icon(color='green', icon='leaf')
567
  ).add_to(m)
568
-
569
  folium.Circle(
570
  [lat, lon],
571
  radius=1500,
@@ -574,13 +617,13 @@ def create_ee_map(farm_id, date_str, layer_type="NDVI"):
574
  fill_color='green',
575
  fill_opacity=0.1
576
  ).add_to(m)
577
-
578
  folium.LayerControl().add_to(m)
579
-
580
  legend_html = '''
581
- <div style="position: fixed;
582
- bottom: 50px; right: 50px;
583
- border: 2px solid grey; z-index: 9999;
584
  background-color: white;
585
  padding: 10px;
586
  border-radius: 5px;
@@ -601,1582 +644,34 @@ def create_ee_map(farm_id, date_str, layer_type="NDVI"):
601
  </div>
602
  </div>
603
  '''
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
604
  m.get_root().html.add_child(folium.Element(legend_html))
605
-
606
  return m
607
  except Exception as e:
608
- st.error(f"خطا در ایجاد نقشه: {e}")
609
- return None
610
-
611
- # Generate mock growth data
612
- def generate_mock_growth_data(farm_data, selected_variety="all", selected_age="all"):
613
- weeks = list(range(1, 23))
614
-
615
- filtered_farms = farm_data
616
- if selected_variety != "all":
617
- filtered_farms = filtered_farms[filtered_farms['واریته'] == selected_variety]
618
- if selected_age != "all":
619
- filtered_farms = filtered_farms[filtered_farms['سن'] == selected_age]
620
-
621
- farm_growth_data = []
622
- for _, farm in filtered_farms.iterrows():
623
- base_height = np.random.uniform(50, 100)
624
- growth_rate = np.random.uniform(5, 15)
625
-
626
- growth_data = {
627
- 'farm_id': farm['مزرعه'],
628
- 'variety': farm['واریته'],
629
- 'age': farm['سن'],
630
- 'weeks': weeks,
631
- 'heights': [round(base_height + growth_rate * week) for week in weeks]
632
- }
633
- farm_growth_data.append(growth_data)
634
-
635
- if farm_growth_data:
636
- avg_heights = []
637
- for week in weeks:
638
- week_heights = [farm['heights'][week-1] for farm in farm_growth_data]
639
- avg_heights.append(round(sum(week_heights) / len(week_heights)))
640
-
641
- avg_growth_data = {
642
- 'farm_id': 'میانگین',
643
- 'variety': 'همه',
644
- 'age': 'همه',
645
- 'weeks': weeks,
646
- 'heights': avg_heights
647
- }
648
-
649
- return {
650
- 'individual': farm_growth_data,
651
- 'average': avg_growth_data
652
- }
653
- else:
654
- return {
655
- 'individual': [],
656
- 'average': {
657
- 'farm_id': 'میانگین',
658
- 'variety': 'همه',
659
- 'age': 'همه',
660
- 'weeks': weeks,
661
- 'heights': [0] * len(weeks)
662
- }
663
- }
664
-
665
- # Calculate statistics for a farm
666
- def calculate_farm_stats(farm_id, layer_type="NDVI"):
667
- if layer_type == "NDVI":
668
- mean = round(np.random.uniform(0.6, 0.8), 2)
669
- min_val = round(mean - np.random.uniform(0.2, 0.3), 2)
670
- max_val = round(mean + np.random.uniform(0.1, 0.2), 2)
671
- std_dev = round(np.random.uniform(0.05, 0.15), 2)
672
- elif layer_type == "NDMI":
673
- mean = round(np.random.uniform(0.3, 0.5), 2)
674
- min_val = round(mean - np.random.uniform(0.2, 0.3), 2)
675
- max_val = round(mean + np.random.uniform(0.1, 0.2), 2)
676
- std_dev = round(np.random.uniform(0.05, 0.15), 2)
677
- elif layer_type == "EVI":
678
- mean = round(np.random.uniform(0.4, 0.6), 2)
679
- min_val = round(mean - np.random.uniform(0.2, 0.3), 2)
680
- max_val = round(mean + np.random.uniform(0.1, 0.2), 2)
681
- std_dev = round(np.random.uniform(0.05, 0.15), 2)
682
- elif layer_type == "NDWI":
683
- mean = round(np.random.uniform(-0.1, 0.1), 2)
684
- min_val = round(mean - np.random.uniform(0.2, 0.3), 2)
685
- max_val = round(mean + np.random.uniform(0.1, 0.2), 2)
686
- std_dev = round(np.random.uniform(0.05, 0.15), 2)
687
- elif layer_type == "SoilMoisture":
688
- mean = round(np.random.uniform(-20, -10), 2)
689
- min_val = round(mean - np.random.uniform(5, 10), 2)
690
- max_val = round(mean + np.random.uniform(5, 10), 2)
691
- std_dev = round(np.random.uniform(2, 5), 2)
692
-
693
- hist_data = np.random.normal(mean, std_dev, 1000)
694
- hist_data = np.clip(hist_data, min_val, max_val)
695
-
696
- return {
697
- 'mean': mean,
698
- 'min': min_val,
699
- 'max': max_val,
700
- 'std_dev': std_dev,
701
- 'histogram_data': hist_data
702
- }
703
-
704
- # Initialize Earth Engine
705
- ee_initialized = initialize_earth_engine()
706
-
707
- # Load data
708
- farm_df = load_farm_data()
709
- coordinates_df = load_coordinates_data()
710
-
711
- # Load animations
712
- lottie_farm = load_lottie_url('https://assets5.lottiefiles.com/packages/lf20_ystsffqy.json')
713
- lottie_analysis = load_lottie_url('https://assets3.lottiefiles.com/packages/lf20_qp1q7mct.json')
714
- lottie_report = load_lottie_url('https://assets9.lottiefiles.com/packages/lf20_vwcugezu.json')
715
-
716
- # Create session state for storing data
717
- if 'heights_df' not in st.session_state:
718
- st.session_state.heights_df = pd.DataFrame(columns=[
719
- 'Farm_ID', 'Week', 'Measurement_Date', 'Height', 'Station1', 'Station2', 'Station3',
720
- 'Station4', 'Station5', 'Groundwater1', 'Groundwater2', 'Sheath_Moisture', 'Nitrogen',
721
- 'Variety', 'Age', 'Area', 'Channel', 'Administration'
722
- ])
723
-
724
- # Main header
725
- st.markdown('<div class="main-header">', unsafe_allow_html=True)
726
- st.markdown('<h1>سامانه هوشمند پایش مزارع نیشکر دهخدا</h1>', unsafe_allow_html=True)
727
- st.markdown('<p>پلتفرم جامع مدیریت، پایش و تحلیل داده‌های مزارع نیشکر با استفاده از تصاویر ماهواره‌ای و هوش مصنوعی</p>', unsafe_allow_html=True)
728
- st.markdown('</div>', unsafe_allow_html=True)
729
-
730
- # Create a modern navigation menu
731
- selected = option_menu(
732
- menu_title=None,
733
- options=["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل داده‌ها", "گزارش‌گیری", "تنظیمات"],
734
- icons=["speedometer2", "map", "pencil-square", "graph-up", "file-earmark-text", "gear"],
735
- menu_icon="cast",
736
- default_index=0,
737
- orientation="horizontal",
738
- styles={
739
- "container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "20px"},
740
- "icon": {"color": "#1a8754", "font-size": "18px"},
741
- "nav-link": {"font-size": "16px", "text-align": "center", "margin":"0px", "--hover-color": "#e9f7ef", "border-radius": "10px"},
742
- "nav-link-selected": {"background-color": "#1a8754", "color": "white", "font-weight": "600"},
743
- }
744
- )
745
-
746
- # Dashboard
747
- if selected == "داشبورد":
748
- col1, col2, col3, col4 = st.columns(4)
749
-
750
- with col1:
751
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
752
- st.markdown(f'<div class="metric-value">{len(farm_df)}</div>', unsafe_allow_html=True)
753
- st.markdown('<div class="metric-label">تعداد مزارع</div>', unsafe_allow_html=True)
754
- st.markdown('</div>', unsafe_allow_html=True)
755
-
756
- with col2:
757
- active_farms = int(len(farm_df) * 0.85)
758
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
759
- st.markdown(f'<div class="metric-value">{active_farms}</div>', unsafe_allow_html=True)
760
- st.markdown('<div class="metric-label">مزارع فعال</div>', unsafe_allow_html=True)
761
- st.markdown('</div>', unsafe_allow_html=True)
762
-
763
- with col3:
764
- avg_height = 175
765
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
766
- st.markdown(f'<div class="metric-value">{avg_height} cm</div>', unsafe_allow_html=True)
767
- st.markdown('<div class="metric-label">میانگین ارتفاع</div>', unsafe_allow_html=True)
768
- st.markdown('</div>', unsafe_allow_html=True)
769
-
770
- with col4:
771
- avg_moisture = 68
772
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
773
- st.markdown(f'<div class="metric-value">{avg_moisture}%</div>', unsafe_allow_html=True)
774
- st.markdown('<div class="metric-label">میانگین رطوبت</div>', unsafe_allow_html=True)
775
- st.markdown('</div>', unsafe_allow_html=True)
776
-
777
- tab1, tab2, tab3, tab4 = st.tabs(["نمای کلی", "نقشه مزارع", "نمودارها", "داده‌ها"])
778
-
779
- with tab1:
780
- st.markdown("### توزیع واریته‌ها و سن محصول")
781
-
782
- col1, col2 = st.columns(2)
783
-
784
- with col1:
785
- variety_counts = farm_df['واریته'].value_counts().reset_index()
786
- variety_counts.columns = ['واریته', 'تعداد']
787
- fig = px.pie(
788
- variety_counts,
789
- values='تعداد',
790
- names='واریته',
791
- title='توزیع واریته‌ها',
792
- color_discrete_sequence=px.colors.sequential.Greens_r
793
- )
794
- fig.update_traces(textposition='inside', textinfo='percent+label')
795
- fig.update_layout(
796
- font=dict(family="Vazirmatn"),
797
- legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5)
798
- )
799
- st.plotly_chart(fig, use_container_width=True)
800
-
801
- with col2:
802
- age_counts = farm_df['سن'].value_counts().reset_index()
803
- age_counts.columns = ['سن', 'تعداد']
804
- fig = px.pie(
805
- age_counts,
806
- values='تعداد',
807
- names='سن',
808
- title='توزیع سن محصول',
809
- color_discrete_sequence=px.colors.sequential.Blues_r
810
- )
811
- fig.update_traces(textposition='inside', textinfo='percent+label')
812
- fig.update_layout(
813
- font=dict(family="Vazirmatn"),
814
- legend=dict(orientation="h", yanchor="bottom", y=-0.3, xanchor="center", x=0.5)
815
- )
816
- st.plotly_chart(fig, use_container_width=True)
817
-
818
- st.markdown("### اطلاعات کلی مزارع")
819
-
820
- total_area = farm_df['مساحت'].astype(float).sum()
821
-
822
- col1, col2, col3 = st.columns(3)
823
- col1.metric("تعداد کل مزارع", f"{len(farm_df)}")
824
- col2.metric("مساحت کل (هکتار)", f"{total_area:.2f}")
825
- col3.metric("تعداد کانال‌ها", f"{farm_df['کانال'].nunique()}")
826
-
827
- st.markdown('<hr style="height:2px;border:none;color:#1a8754;background-color:#1a8754;margin:30px 0;">', unsafe_allow_html=True)
828
- st_lottie(lottie_farm, height=300, key="farm_animation")
829
-
830
- with tab2:
831
- st.markdown("### نقشه مزارع")
832
-
833
- if coordinates_df is not None and not coordinates_df.empty:
834
- m = folium.Map(location=[31.45, 48.72], zoom_start=12, tiles='CartoDB positron')
835
-
836
- for _, farm in coordinates_df.iterrows():
837
- lat = farm['عرض جغرافیایی']
838
- lon = farm['طول جغرافیایی']
839
- name = farm['مزرعه']
840
-
841
- farm_info = farm_df[farm_df['مزرعه'] == name]
842
- if not farm_info.empty:
843
- variety = farm_info['واریته'].iloc[0]
844
- age = farm_info['سن'].iloc[0]
845
- area = farm_info['مساحت'].iloc[0]
846
- popup_text = f"""
847
- <div style="direction: rtl; text-align: right; font-family: 'Vazirmatn', sans-serif;">
848
- <h4>مزرعه {name}</h4>
849
- <p>واریته: {variety}</p>
850
- <p>سن: {age}</p>
851
- <p>مساحت: {area} هکتار</p>
852
- </div>
853
- """
854
- else:
855
- popup_text = f"<div style='direction: rtl;'>مزرعه {name}</div>"
856
-
857
- folium.Marker(
858
- [lat, lon],
859
- popup=folium.Popup(popup_text, max_width=300),
860
- tooltip=f"مزرعه {name}",
861
- icon=folium.Icon(color='green', icon='leaf')
862
- ).add_to(m)
863
-
864
- st.markdown('<div class="map-container">', unsafe_allow_html=True)
865
- folium_static(m, width=1000, height=600)
866
- st.markdown('</div>', unsafe_allow_html=True)
867
- else:
868
- st.warning("داده‌های مختصات در دسترس نیست.")
869
-
870
- with tab3:
871
- st.markdown("### نمودار رشد هفتگی")
872
-
873
- col1, col2 = st.columns(2)
874
- with col1:
875
- selected_variety = st.selectbox(
876
- "انتخاب واریته",
877
- ["all"] + list(farm_df['واریته'].unique()),
878
- format_func=lambda x: "همه واریته‌ها" if x == "all" else x
879
- )
880
-
881
- with col2:
882
- selected_age = st.selectbox(
883
- "انتخاب سن",
884
- ["all"] + list(farm_df['سن'].unique()),
885
- format_func=lambda x: "همه سنین" if x == "all" else x
886
- )
887
-
888
- growth_data = generate_mock_growth_data(farm_df, selected_variety, selected_age)
889
-
890
- chart_tab1, chart_tab2 = st.tabs(["میانگین رشد", "رشد مزارع فردی"])
891
-
892
- with chart_tab1:
893
- avg_data = growth_data['average']
894
- fig = go.Figure()
895
- fig.add_trace(go.Scatter(
896
- x=avg_data['weeks'],
897
- y=avg_data['heights'],
898
- mode='lines+markers',
899
- name='میانگین رشد',
900
- line=dict(color='#1a8754', width=3),
901
- marker=dict(size=8, color='#1a8754')
902
- ))
903
- fig.update_layout(
904
- title='میانگین رشد هفتگی',
905
- xaxis_title='هفته',
906
- yaxis_title='ارتفاع (سانتی‌متر)',
907
- font=dict(family='Vazirmatn', size=14),
908
- hovermode='x unified',
909
- template='plotly_white',
910
- height=500
911
- )
912
- st.plotly_chart(fig, use_container_width=True)
913
-
914
- with chart_tab2:
915
- if growth_data['individual']:
916
- fig = go.Figure()
917
- colors = ['#1a8754', '#1976d2', '#e65100', '#9c27b0', '#d32f2f']
918
- for i, farm_data in enumerate(growth_data['individual'][:5]):
919
- fig.add_trace(go.Scatter(
920
- x=farm_data['weeks'],
921
- y=farm_data['heights'],
922
- mode='lines+markers',
923
- name=f"مزرعه {farm_data['farm_id']}",
924
- line=dict(color=colors[i % len(colors)], width=2),
925
- marker=dict(size=6, color=colors[i % len(colors)])
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
- else:
938
- st.warning("داده‌ای برای نمایش وجود ندارد.")
939
-
940
- with tab4:
941
- st.markdown("### داده‌های مزارع")
942
-
943
- search_term = st.text_input("جستجو در داده‌ها", placeholder="نام مزرعه، واریته، سن و...")
944
-
945
- if search_term:
946
- filtered_df = farm_df[
947
- farm_df['مزرعه'].astype(str).str.contains(search_term) |
948
- farm_df['واریته'].astype(str).str.contains(search_term) |
949
- farm_df['سن'].astype(str).str.contains(search_term) |
950
- farm_df['کانال'].astype(str).str.contains(search_term)
951
- ]
952
- else:
953
- filtered_df = farm_df
954
-
955
- if not filtered_df.empty:
956
- csv = filtered_df.to_csv(index=False).encode('utf-8')
957
- st.download_button(
958
- label="دانلود داده‌ها (CSV)",
959
- data=csv,
960
- file_name="farm_data.csv",
961
- mime="text/csv",
962
- )
963
- st.dataframe(
964
- filtered_df,
965
- use_container_width=True,
966
- height=400,
967
- hide_index=True
968
- )
969
- st.info(f"نمایش {len(filtered_df)} مزرعه از {len(farm_df)} مزرعه")
970
- else:
971
- st.warning("هیچ داده‌ای یافت نشد.")
972
-
973
- # Map Page
974
- elif selected == "نقشه مزارع":
975
- st.markdown("## نقشه مزارع با شاخص‌های ماهواره‌ای")
976
-
977
- col1, col2 = st.columns([1, 3])
978
-
979
- with col1:
980
- st.markdown('<div class="glass-card">', unsafe_allow_html=True)
981
- st.markdown("### تنظیمات نقشه")
982
-
983
- selected_farm = st.selectbox(
984
- "انتخاب مزرعه",
985
- options=coordinates_df['مزرعه'].tolist(),
986
- index=0,
987
- format_func=lambda x: f"مزرعه {x}"
988
- )
989
-
990
- selected_date = st.date_input(
991
- "انتخاب تاریخ",
992
- value=datetime.now(),
993
- format="YYYY-MM-DD"
994
- )
995
-
996
- selected_layer = st.selectbox(
997
- "انتخاب شاخص",
998
- options=["NDVI", "NDMI", "EVI", "NDWI", "SoilMoisture"],
999
- format_func=lambda x: {
1000
- "NDVI": "شاخص پوشش گیاهی (NDVI)",
1001
- "NDMI": "شاخص رطوبت (NDMI)",
1002
- "EVI": "شاخص پیشرفته گیاهی (EVI)",
1003
- "NDWI": "شاخص آب (NDWI)",
1004
- "SoilMoisture": "رطوبت خاک (Soil Moisture)"
1005
- }[x]
1006
- )
1007
-
1008
- generate_map = st.button(
1009
- "تولید نقشه",
1010
- type="primary",
1011
- use_container_width=True
1012
- )
1013
-
1014
- st.markdown('<hr style="margin: 20px 0;">', unsafe_allow_html=True)
1015
-
1016
- st.markdown("### راهنمای شاخص‌ها")
1017
-
1018
- with st.expander("شاخص پوشش گیاهی (NDVI)", expanded=selected_layer == "NDVI"):
1019
- st.markdown("""
1020
- **شاخ�� تفاضل نرمال‌شده پوشش گیاهی (NDVI)** معیاری برای سنجش سلامت و تراکم پوشش گیاهی است.
1021
- - **مقادیر بالا (0.6 تا 1.0)**: پوشش گیاهی متراکم و سالم
1022
- - **مقادیر متوسط (0.2 تا 0.6)**: پوشش گیاهی متوسط
1023
- - **مقادیر پایین (-1.0 تا 0.2)**: پوشش گیاهی کم یا خاک لخت
1024
- فرمول: NDVI = (NIR - RED) / (NIR + RED)
1025
- """)
1026
-
1027
- with st.expander("شاخص رطوبت (NDMI)", expanded=selected_layer == "NDMI"):
1028
- st.markdown("""
1029
- **شاخص تفاضل نرمال‌شده رطوبت (NDMI)** برای ارزیابی محتوای رطوبت گیاهان استفاده می‌شود.
1030
- - **مقادیر بالا (0.4 تا 1.0)**: محتوای رطوبت بالا
1031
- - **مقادیر متوسط (0.0 تا 0.4)**: محتوای رطوبت متوسط
1032
- - **مقادیر پایین (-1.0 تا 0.0)**: محتوای رطوبت کم
1033
- فرمول: NDMI = (NIR - SWIR) / (NIR + SWIR)
1034
- """)
1035
-
1036
- with st.expander("شاخص پیشرفته گیاهی (EVI)", expanded=selected_layer == "EVI"):
1037
- st.markdown("""
1038
- **شاخص پیشرفته پوشش گیاهی (EVI)** نسخه بهبودیافته NDVI است که حساسیت کمتری به اثرات خاک و اتمسفر دارد.
1039
- - **مقادیر بالا (0.4 تا 1.0)**: پوشش گیاهی متراکم و سالم
1040
- - **مقادیر متوسط (0.2 تا 0.4)**: پوشش گیاهی متوسط
1041
- - **مقادیر پایین (0.0 تا 0.2)**: پوشش گیاهی کم
1042
- فرمول: EVI = 2.5 * ((NIR - RED) / (NIR + 6*RED - 7.5*BLUE + 1))
1043
- """)
1044
-
1045
- with st.expander("شاخص آب (NDWI)", expanded=selected_layer == "NDWI"):
1046
- st.markdown("""
1047
- **شاخص تفاضل نرمال‌شده آب (NDWI)** برای شناسایی پهنه‌های آبی و ارزیابی محتوای آب در گیاهان استفاده می‌شود.
1048
- - **مقادیر بالا (0.3 تا 1.0)**: پهنه‌های آبی
1049
- - **مقادیر متوسط (0.0 تا 0.3)**: محتوای آب متوسط
1050
- - **مقادیر پایین (-1.0 تا 0.0)**: محتوای آب کم یا خاک خشک
1051
- فرمول: NDWI = (GREEN - NIR) / (GREEN + NIR)
1052
- """)
1053
-
1054
- with st.expander("رطوبت خاک (Soil Moisture)", expanded=selected_layer == "SoilMoisture"):
1055
- st.markdown("""
1056
- **رطوبت خاک (Soil Moisture)** با استفاده از داده‌های راداری Sentinel-1 سطح رطوبت خاک را به صورت داینامیک بررسی می‌کند.
1057
- - **مقادیر بالا**: رطوبت خاک بالا
1058
- - **مقادیر پایین**: رطوبت خاک کم
1059
- این شاخص به مدیریت بهتر منابع آب کمک می‌کند.
1060
- """)
1061
-
1062
- st.markdown('</div>', unsafe_allow_html=True)
1063
-
1064
- with col2:
1065
- map_tab, stats_tab = st.tabs(["نقشه", "آمار و تحلیل"])
1066
-
1067
- with map_tab:
1068
- st.markdown('<div class="map-container">', unsafe_allow_html=True)
1069
-
1070
- if generate_map or 'last_map' not in st.session_state:
1071
- with st.spinner('در حال تولید نقشه...'):
1072
- m = create_ee_map(
1073
- selected_farm,
1074
- selected_date.strftime('%Y-%m-%d'),
1075
- selected_layer
1076
- )
1077
- if m:
1078
- st.session_state.last_map = m
1079
- folium_static(m, width=800, height=600)
1080
- st.success(f"نقشه {selected_layer} برای مزرعه {selected_farm} با موفقیت تولید شد.")
1081
- else:
1082
- st.error("خطا در تولید نقشه. لطفاً دوباره تلاش کنید.")
1083
- elif 'last_map' in st.session_state:
1084
- folium_static(st.session_state.last_map, width=800, height=600)
1085
-
1086
- st.markdown('</div>', unsafe_allow_html=True)
1087
- st.info("""
1088
- **نکته:** این نقشه بر اساس تصاویر Sentinel-2 و Sentinel-1 تولید شده است.
1089
- برای دقت بیشتر، تاریخی با ابرناکی کم انتخاب کنید.
1090
- """)
1091
-
1092
- with stats_tab:
1093
- if 'last_map' in st.session_state:
1094
- stats = calculate_farm_stats(selected_farm, selected_layer)
1095
-
1096
- col1, col2, col3, col4 = st.columns(4)
1097
-
1098
- with col1:
1099
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1100
- st.markdown(f'<div class="metric-value">{stats["mean"]}</div>', unsafe_allow_html=True)
1101
- st.markdown(f'<div class="metric-label">میانگین {selected_layer}</div>', unsafe_allow_html=True)
1102
- st.markdown('</div>', unsafe_allow_html=True)
1103
-
1104
- with col2:
1105
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1106
- st.markdown(f'<div class="metric-value">{stats["max"]}</div>', unsafe_allow_html=True)
1107
- st.markdown(f'<div class="metric-label">حداکثر {selected_layer}</div>', unsafe_allow_html=True)
1108
- st.markdown('</div>', unsafe_allow_html=True)
1109
-
1110
- with col3:
1111
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1112
- st.markdown(f'<div class="metric-value">{stats["min"]}</div>', unsafe_allow_html=True)
1113
- st.markdown(f'<div class="metric-label">حداقل {selected_layer}</div>', unsafe_allow_html=True)
1114
- st.markdown('</div>', unsafe_allow_html=True)
1115
-
1116
- with col4:
1117
- st.markdown('<div class="metric-card">', unsafe_allow_html=True)
1118
- st.markdown(f'<div class="metric-value">{stats["std_dev"]}</div>', unsafe_allow_html=True)
1119
- st.markdown(f'<div class="metric-label">انحراف معیار</div>', unsafe_allow_html=True)
1120
- st.markdown('</div>', unsafe_allow_html=True)
1121
-
1122
- fig = px.histogram(
1123
- x=stats["histogram_data"],
1124
- nbins=20,
1125
- title=f"توزیع مقادیر {selected_layer} در مزرعه {selected_farm}",
1126
- labels={"x": f"مقدار {selected_layer}", "y": "فراوانی"},
1127
- color_discrete_sequence=["#1a8754"]
1128
- )
1129
- fig.update_layout(
1130
- font=dict(family="Vazirmatn"),
1131
- template="plotly_white",
1132
- bargap=0.1
1133
- )
1134
- st.plotly_chart(fig, use_container_width=True)
1135
-
1136
- st.markdown("### تحلیل زمانی")
1137
- dates = pd.date_range(end=selected_date, periods=30, freq='D')
1138
- values = np.random.normal(stats["mean"], stats["std_dev"] / 2, 30)
1139
- values = np.clip(values, stats["min"], stats["max"])
1140
- fig = px.line(
1141
- x=dates,
1142
- y=values,
1143
- title=f"روند تغییرات {selected_layer} در 30 روز گذشته",
1144
- labels={"x": "تاریخ", "y": f"مقدار {selected_layer}"},
1145
- markers=True
1146
- )
1147
- fig.update_layout(
1148
- font=dict(family="Vazirmatn"),
1149
- template="plotly_white",
1150
- hovermode="x unified"
1151
- )
1152
- st.plotly_chart(fig, use_container_width=True)
1153
-
1154
- st.markdown("### تخمین نیاز آبی")
1155
- water_requirement = estimate_water_requirement(selected_farm, selected_date.strftime('%Y-%m-%d'))
1156
- if water_requirement is not None:
1157
- st.metric("نیاز آبی (mm/day)", f"{water_requirement:.2f}")
1158
- st.info(f"نیاز آبی تخمینی برای مزرعه {selected_farm}: {water_requirement:.2f} میلی‌متر در روز")
1159
- else:
1160
- st.warning("داده‌های هواشناسی در دسترس نیست.")
1161
-
1162
- if selected_layer == "SoilMoisture":
1163
- st.markdown("### پیشنهادات مدیریت آب")
1164
- if stats["mean"] < -20:
1165
- st.markdown("- **افزایش آبیاری**: رطوبت خاک بسیار پایین است.")
1166
- elif stats["mean"] > -10:
1167
- st.markdown("- **کاهش آبیاری**: رطوبت خاک بیش از حد است.")
1168
- else:
1169
- st.markdown("- **مدیریت بهینه**: رطوبت خاک در محدوده مناسب است.")
1170
- else:
1171
- st.warning("لطفاً ابتدا یک نقشه تولید کنید.")
1172
-
1173
- # Data Entry Page
1174
- elif selected == "ورود اطلاعات":
1175
- st.markdown("## ورود اطلاعات روزانه مزارع")
1176
-
1177
- tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"])
1178
-
1179
- with tab1:
1180
- col1, col2 = st.columns(2)
1181
-
1182
- with col1:
1183
- selected_week = st.selectbox(
1184
- "انتخاب هفته",
1185
- options=[str(i) for i in range(1, 23)],
1186
- format_func=lambda x: f"هفته {x}"
1187
- )
1188
-
1189
- with col2:
1190
- selected_day = st.selectbox(
1191
- "انتخاب روز",
1192
- options=["شنبه", "یکشنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنجشنبه"]
1193
- )
1194
-
1195
- filtered_farms = farm_df[farm_df['روز'] == selected_day]
1196
-
1197
- if filtered_farms.empty:
1198
- st.warning(f"هیچ مزرعه‌ای برای روز {selected_day} در پایگاه داده وجود ندارد.")
1199
- else:
1200
- st.markdown("### ورود داده‌های مزارع")
1201
-
1202
- data_key = f"data_{selected_week}_{selected_day}"
1203
- if data_key not in st.session_state:
1204
- st.session_state[data_key] = pd.DataFrame({
1205
- 'مزرعه': filtered_farms['مزرعه'],
1206
- 'ایستگاه 1': [0] * len(filtered_farms),
1207
- 'ایستگاه 2': [0] * len(filtered_farms),
1208
- 'ایستگاه 3': [0] * len(filtered_farms),
1209
- 'ایستگاه 4': [0] * len(filtered_farms),
1210
- 'ایستگاه 5': [0] * len(filtered_farms),
1211
- 'چاهک 1': [0] * len(filtered_farms),
1212
- 'چاهک 2': [0] * len(filtered_farms),
1213
- 'رطوبت غلاف': [0] * len(filtered_farms),
1214
- 'نیتروژن': [0] * len(filtered_farms),
1215
- 'میانگین ارتفاع': [0] * len(filtered_farms)
1216
- })
1217
-
1218
- edited_df = st.data_editor(
1219
- st.session_state[data_key],
1220
- use_container_width=True,
1221
- num_rows="fixed",
1222
- column_config={
1223
- "مزرعه": st.column_config.TextColumn("مزرعه", disabled=True),
1224
- "ایستگاه 1": st.column_config.NumberColumn("ایستگاه 1", min_value=0, max_value=300, step=1),
1225
- "ایستگاه 2": st.column_config.NumberColumn("ایستگاه 2", min_value=0, max_value=300, step=1),
1226
- "ایستگاه 3": st.column_config.NumberColumn("ایستگاه 3", min_value=0, max_value=300, step=1),
1227
- "ایستگاه 4": st.column_config.NumberColumn("ایستگاه 4", min_value=0, max_value=300, step=1),
1228
- "ایستگاه 5": st.column_config.NumberColumn("ایستگاه 5", min_value=0, max_value=300, step=1),
1229
- "چاهک 1": st.column_config.NumberColumn("چاهک 1", min_value=0, max_value=300, step=1),
1230
- "چاهک 2": st.column_config.NumberColumn("چاهک 2", min_value=0, max_value=300, step=1),
1231
- "رطوبت غلاف": st.column_config.NumberColumn("رطوبت غلاف", min_value=0, max_value=100, step=1),
1232
- "نیتروژن": st.column_config.NumberColumn("نیتروژن", min_value=0, max_value=100, step=1),
1233
- "میانگین ارتفاع": st.column_config.NumberColumn("میانگین ارتفاع", disabled=True),
1234
- },
1235
- hide_index=True
1236
- )
1237
-
1238
- for i in range(len(edited_df)):
1239
- stations = [
1240
- edited_df.iloc[i]['ایستگاه 1'],
1241
- edited_df.iloc[i]['ایستگاه 2'],
1242
- edited_df.iloc[i]['ایستگاه 3'],
1243
- edited_df.iloc[i]['ایستگاه 4'],
1244
- edited_df.iloc[i]['ایستگاه 5']
1245
- ]
1246
- valid_stations = [s for s in stations if s > 0]
1247
- if valid_stations:
1248
- edited_df.iloc[i, edited_df.columns.get_loc('میانگین ارتفاع')] = round(sum(valid_stations) / len(valid_stations), 1)
1249
-
1250
- st.session_state[data_key] = edited_df
1251
-
1252
- if st.button("ذخیره اطلاعات", type="primary", use_container_width=True):
1253
- new_data = edited_df.copy()
1254
- new_data['Farm_ID'] = new_data['مزرعه']
1255
- new_data['Week'] = int(selected_week)
1256
- new_data['Measurement_Date'] = (datetime.now() - timedelta(weeks=(22 - int(selected_week)))).strftime('%Y-%m-%d')
1257
- new_data['Height'] = new_data['میانگین ارتفاع']
1258
- new_data['Station1'] = new_data['ایستگاه 1']
1259
- new_data['Station2'] = new_data['ایستگاه 2']
1260
- new_data['Station3'] = new_data['ایستگاه 3']
1261
- new_data['Station4'] = new_data['ایستگاه 4']
1262
- new_data['Station5'] = new_data['ایستگاه 5']
1263
- new_data['Groundwater1'] = new_data['چاهک 1']
1264
- new_data['Groundwater2'] = new_data['چاهک 2']
1265
- new_data['Sheath_Moisture'] = new_data['رطوبت غلاف']
1266
- new_data['Nitrogen'] = new_data['نیتروژن']
1267
-
1268
- new_data = new_data.merge(
1269
- farm_df[['مزرعه', 'واریته', 'سن', 'مساحت', 'کانال', 'اداره']],
1270
- left_on='Farm_ID',
1271
- right_on='مزرعه',
1272
- how='left'
1273
- )
1274
-
1275
- new_data = new_data.rename(columns={
1276
- 'واریته': 'Variety',
1277
- 'سن': 'Age',
1278
- 'مساحت': 'Area',
1279
- 'کانال': 'Channel',
1280
- 'اداره': 'Administration'
1281
- })
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
- if st.button("ذخیره فایل", type="primary"):
1300
- st.success("فایل با موفقیت ذخیره شد.")
1301
- st.balloons()
1302
- except Exception as e:
1303
- st.error(f"خطا در خواندن فایل: {e}")
1304
-
1305
- st.markdown("### راهنمای فرمت فایل")
1306
- st.markdown("""
1307
- فایل اکسل باید شامل ستون‌های زیر باشد:
1308
- - مزرعه
1309
- - ایستگاه 1 تا 5
1310
- - چاهک 1 و 2
1311
- - رطوبت غلاف
1312
- - نیتروژن
1313
- می‌توانید از [این فایل نمونه](https://example.com/sample.xlsx) به عنوان الگو استفاده کنید.
1314
- """)
1315
-
1316
- st.markdown("""
1317
- <div style="border: 2px dashed #1a8754; border-radius: 10px; padding: 40px; text-align: center; margin: 20px 0;">
1318
- <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">
1319
- <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
1320
- <polyline points="17 8 12 3 7 8"></polyline>
1321
- <line x1="12" y1="3" x2="12" y2="15"></line>
1322
- </svg>
1323
- <p style="margin-top: 10px; color: #1a8754;">فایل خود را اینجا رها کنید یا روی دکمه بالا کلیک کنید</p>
1324
- </div>
1325
- """, unsafe_allow_html=True)
1326
-
1327
- # Data Analysis Page
1328
- elif selected == "تحلیل داده‌ها":
1329
- st.markdown("## تحلیل هوشمند داده‌ها")
1330
-
1331
- col1, col2 = st.columns([1, 2])
1332
-
1333
- with col1:
1334
- st_lottie(lottie_analysis, height=200, key="analysis_animation")
1335
-
1336
- with col2:
1337
- st.markdown("""
1338
- <div class="glass-card">
1339
- <h3 class="gradient-text">تحلیل پیشرفته داده‌های مزارع</h3>
1340
- <p>در این بخش می‌توانید تحلیل‌های پیشرفته روی داده‌های مزارع انجام دهید و روندها و الگوهای مختلف را بررسی کنید.</p>
1341
- </div>
1342
- """, unsafe_allow_html=True)
1343
-
1344
- tab1, tab2, tab3, tab4 = st.tabs(["تحلیل رشد", "مقایسه واریته‌ها", "تحلیل رطوبت", "پیش‌بینی"])
1345
-
1346
- with tab1:
1347
- st.markdown("### تحلیل رشد مزارع")
1348
-
1349
- col1, col2 = st.columns(2)
1350
-
1351
- with col1:
1352
- selected_variety = st.selectbox(
1353
- "انتخاب واریته",
1354
- ["all"] + list(farm_df['واریته'].unique()),
1355
- format_func=lambda x: "همه واریته‌ها" if x == "all" else x,
1356
- key="growth_variety"
1357
- )
1358
-
1359
- with col2:
1360
- selected_age = st.selectbox(
1361
- "انتخاب سن",
1362
- ["all"] + list(farm_df['سن'].unique()),
1363
- format_func=lambda x: "همه سنین" if x == "all" else x,
1364
- key="growth_age"
1365
- )
1366
-
1367
- growth_data = generate_mock_growth_data(farm_df, selected_variety, selected_age)
1368
-
1369
- if growth_data['individual']:
1370
- chart_data = []
1371
- for farm_data in growth_data['individual']:
1372
- for i, week in enumerate(farm_data['weeks']):
1373
- chart_data.append({
1374
- 'Farm': farm_data['farm_id'],
1375
- 'Week': week,
1376
- 'Height': farm_data['heights'][i],
1377
- 'Variety': farm_data['variety'],
1378
- 'Age': farm_data['age']
1379
- })
1380
-
1381
- chart_df = pd.DataFrame(chart_data)
1382
-
1383
- chart = alt.Chart(chart_df).mark_line(point=True).encode(
1384
- x=alt.X('Week:Q', title='هفته'),
1385
- y=alt.Y('Height:Q', title='ارتفاع (سانتی‌متر)'),
1386
- color=alt.Color('Farm:N', title='مزرعه'),
1387
- tooltip=['Farm', 'Week', 'Height', 'Variety', 'Age']
1388
- ).properties(
1389
- width='container',
1390
- height=400,
1391
- title='روند رشد مزارع بر اساس هفته'
1392
- ).interactive()
1393
-
1394
- st.altair_chart(chart, use_container_width=True)
1395
-
1396
- st.markdown("### تحلیل نرخ رشد")
1397
-
1398
- growth_rates = []
1399
- for farm_data in growth_data['individual']:
1400
- heights = farm_data['heights']
1401
- for i in range(1, len(heights)):
1402
- growth_rate = heights[i] - heights[i-1]
1403
- growth_rates.append({
1404
- 'Farm': farm_data['farm_id'],
1405
- 'Week': farm_data['weeks'][i],
1406
- 'Growth Rate': growth_rate,
1407
- 'Variety': farm_data['variety'],
1408
- 'Age': farm_data['age']
1409
- })
1410
-
1411
- growth_rate_df = pd.DataFrame(growth_rates)
1412
-
1413
- chart = alt.Chart(growth_rate_df).mark_bar().encode(
1414
- x=alt.X('Week:O', title='هفته'),
1415
- y=alt.Y('mean(Growth Rate):Q', title='نرخ رشد (سانتی‌متر در هفته)'),
1416
- color=alt.Color('Farm:N', title='مزرعه'),
1417
- tooltip=['Farm', 'Week', 'mean(Growth Rate)']
1418
- ).properties(
1419
- width='container',
1420
- height=400,
1421
- title='نرخ رشد هفتگی مزارع'
1422
- ).interactive()
1423
-
1424
- st.altair_chart(chart, use_container_width=True)
1425
- else:
1426
- st.warning("داده‌ای برای نمایش وجود ندارد.")
1427
-
1428
- with tab2:
1429
- st.markdown("### مقایسه واریته‌ها")
1430
-
1431
- variety_age_groups = farm_df.groupby(['واریته', 'سن']).size().reset_index(name='تعداد')
1432
-
1433
- fig = px.density_heatmap(
1434
- variety_age_groups,
1435
- x='واریته',
1436
- y='سن',
1437
- z='تعداد',
1438
- title='توزیع مزارع بر اساس واریته و سن',
1439
- color_continuous_scale='Viridis'
1440
- )
1441
- fig.update_layout(
1442
- font=dict(family="Vazirmatn"),
1443
- template="plotly_white",
1444
- xaxis_title="واریته",
1445
- yaxis_title="سن"
1446
- )
1447
- st.plotly_chart(fig, use_container_width=True)
1448
-
1449
- varieties = farm_df['واریته'].unique()
1450
- variety_heights = {variety: np.random.normal(150, 20, 100) for variety in varieties}
1451
-
1452
- fig = go.Figure()
1453
- for variety in varieties:
1454
- fig.add_trace(go.Box(
1455
- y=variety_heights[variety],
1456
- name=variety,
1457
- boxpoints='outliers',
1458
- marker_color=f'hsl({hash(variety) % 360}, 70%, 50%)'
1459
- ))
1460
- fig.update_layout(
1461
- title='مقایسه ارتفاع بر اساس واریته',
1462
- yaxis_title='ارتفاع (سانتی‌متر)',
1463
- font=dict(family="Vazirmatn"),
1464
- template="plotly_white",
1465
- boxmode='group'
1466
- )
1467
- st.plotly_chart(fig, use_container_width=True)
1468
-
1469
- st.markdown("### مقایسه آماری واریته‌ها")
1470
- variety_stats = {}
1471
- for variety in varieties:
1472
- heights = variety_heights[variety]
1473
- variety_stats[variety] = {
1474
- 'میانگین': np.mean(heights),
1475
- 'میانه': np.median(heights),
1476
- 'انحراف معیار': np.std(heights),
1477
- 'حداقل': np.min(heights),
1478
- 'حداکثر': np.max(heights)
1479
- }
1480
- variety_stats_df = pd.DataFrame(variety_stats).T
1481
- st.dataframe(variety_stats_df, use_container_width=True)
1482
-
1483
- with tab3:
1484
- st.markdown("### تحلیل رطوبت مزارع")
1485
-
1486
- farms = farm_df['مزرعه'].unique()[:10]
1487
- dates = pd.date_range(end=datetime.now(), periods=30, freq='D')
1488
-
1489
- moisture_data = []
1490
- for farm in farms:
1491
- base_moisture = np.random.uniform(50, 80)
1492
- for date in dates:
1493
- moisture = base_moisture + np.random.normal(0, 5)
1494
- moisture = max(0, min(100, moisture))
1495
- moisture_data.append({
1496
- 'Farm': farm,
1497
- 'Date': date,
1498
- 'Moisture': moisture
1499
- })
1500
-
1501
- moisture_df = pd.DataFrame(moisture_data)
1502
-
1503
- fig = px.line(
1504
- moisture_df,
1505
- x='Date',
1506
- y='Moisture',
1507
- color='Farm',
1508
- title='روند رطوبت مزارع در 30 روز گذشته',
1509
- labels={'Date': 'تاریخ', 'Moisture': 'رطوبت (%)', 'Farm': 'مزرعه'}
1510
- )
1511
- fig.update_layout(
1512
- font=dict(family="Vazirmatn"),
1513
- template="plotly_white",
1514
- hovermode="x unified"
1515
- )
1516
- st.plotly_chart(fig, use_container_width=True)
1517
-
1518
- st.markdown("### همبستگی رطوبت و ارتفاع")
1519
-
1520
- correlation_data = []
1521
- for farm in farms:
1522
- for _ in range(20):
1523
- moisture = np.random.uniform(40, 90)
1524
- height = 100 + moisture * 1.5 + np.random.normal(0, 20)
1525
- correlation_data.append({
1526
- 'Farm': farm,
1527
- 'Moisture': moisture,
1528
- 'Height': height
1529
- })
1530
-
1531
- correlation_df = pd.DataFrame(correlation_data)
1532
-
1533
- fig = px.scatter(
1534
- correlation_df,
1535
- x='Moisture',
1536
- y='Height',
1537
- color='Farm',
1538
- title='همبستگی بین رطوبت و ارتفاع',
1539
- labels={'Moisture': 'رطوبت (%)', 'Height': 'ارتفاع (سانتی‌متر)', 'Farm': 'مزرعه'},
1540
- trendline='ols'
1541
- )
1542
- fig.update_layout(
1543
- font=dict(family="Vazirmatn"),
1544
- template="plotly_white"
1545
- )
1546
- st.plotly_chart(fig, use_container_width=True)
1547
-
1548
- correlation = correlation_df['Moisture'].corr(correlation_df['Height'])
1549
- st.info(f"ضریب همبستگی بین رطوبت و ارتفاع: {correlation:.2f}")
1550
-
1551
- with tab4:
1552
- st.markdown("### پیش‌بینی رشد مزارع")
1553
-
1554
- selected_farm_for_prediction = st.selectbox(
1555
- "انتخاب مزرعه",
1556
- options=farm_df['مزرعه'].tolist(),
1557
- format_func=lambda x: f"مزرعه {x}"
1558
- )
1559
-
1560
- weeks = list(range(1, 16))
1561
- heights = [50 + i * 10 + np.random.normal(0, 5) for i in range(len(weeks))]
1562
-
1563
- historical_df = pd.DataFrame({
1564
- 'Week': weeks,
1565
- 'Height': heights
1566
- })
1567
-
1568
- future_weeks = list(range(16, 23))
1569
-
1570
- from sklearn.linear_model import LinearRegression
1571
-
1572
- model = LinearRegression()
1573
- model.fit(np.array(weeks).reshape(-1, 1), heights)
1574
-
1575
- future_heights = model.predict(np.array(future_weeks).reshape(-1, 1))
1576
-
1577
- lower_bound = future_heights - 15
1578
- upper_bound = future_heights + 15
1579
-
1580
- future_df = pd.DataFrame({
1581
- 'Week': future_weeks,
1582
- 'Height': future_heights,
1583
- 'Lower': lower_bound,
1584
- 'Upper': upper_bound
1585
- })
1586
-
1587
- fig = go.Figure()
1588
-
1589
- fig.add_trace(go.Scatter(
1590
- x=historical_df['Week'],
1591
- y=historical_df['Height'],
1592
- mode='lines+markers',
1593
- name='داده‌های تاریخی',
1594
- line=dict(color='#1a8754', width=3),
1595
- marker=dict(size=8, color='#1a8754')
1596
- ))
1597
-
1598
- fig.add_trace(go.Scatter(
1599
- x=future_df['Week'],
1600
- y=future_df['Height'],
1601
- mode='lines+markers',
1602
- name='پیش‌بینی',
1603
- line=dict(color='#ff9800', width=3, dash='dash'),
1604
- marker=dict(size=8, color='#ff9800')
1605
- ))
1606
-
1607
- fig.add_trace(go.Scatter(
1608
- x=future_df['Week'].tolist() + future_df['Week'].tolist()[::-1],
1609
- y=future_df['Upper'].tolist() + future_df['Lower'].tolist()[::-1],
1610
- fill='toself',
1611
- fillcolor='rgba(255, 152, 0, 0.2)',
1612
- line=dict(color='rgba(255, 152, 0, 0)'),
1613
- hoverinfo='skip',
1614
- showlegend=False
1615
- ))
1616
-
1617
- fig.update_layout(
1618
- title=f'پیش‌بینی رشد مزرعه {selected_farm_for_prediction}',
1619
- xaxis_title='هفته',
1620
- yaxis_title='ارتفاع (سانتی‌متر)',
1621
- font=dict(family='Vazirmatn', size=14),
1622
- hovermode='x unified',
1623
- template='plotly_white',
1624
- height=500,
1625
- legend=dict(
1626
- orientation="h",
1627
- yanchor="bottom",
1628
- y=1.02,
1629
- xanchor="right",
1630
- x=1
1631
- )
1632
- )
1633
-
1634
- fig.add_vline(x=15.5, line_width=1, line_dash="dash", line_color="gray")
1635
-
1636
- st.plotly_chart(fig, use_container_width=True)
1637
-
1638
- st.markdown("### جزئیات پیش‌بینی")
1639
-
1640
- col1, col2 = st.columns(2)
1641
-
1642
- with col1:
1643
- st.metric(
1644
- label="ارتفاع فعلی",
1645
- value=f"{heights[-1]:.1f} cm",
1646
- delta=f"{heights[-1] - heights[-2]:.1f} cm"
1647
- )
1648
-
1649
- with col2:
1650
- st.metric(
1651
- label="ارتفاع پیش‌بینی شده (هفته 22)",
1652
- value=f"{future_heights[-1]:.1f} cm",
1653
- delta=f"{future_heights[-1] - heights[-1]:.1f} cm"
1654
- )
1655
-
1656
- prediction_table = pd.DataFrame({
1657
- 'هفته': future_weeks,
1658
- 'ارتفاع پیش‌بینی شده': [f"{h:.1f}" for h in future_heights],
1659
- 'حد پایین': [f"{l:.1f}" for l in lower_bound],
1660
- 'حد بالا': [f"{u:.1f}" for u in upper_bound]
1661
- })
1662
-
1663
- st.dataframe(prediction_table, use_container_width=True, hide_index=True)
1664
-
1665
- st.markdown("### عوامل مؤثر بر رشد")
1666
-
1667
- factors = ['رطوبت', 'نیتروژن', 'دما', 'آبیاری', 'نور']
1668
- factor_values = [85, 70, 60, 90, 75]
1669
-
1670
- fig = go.Figure()
1671
-
1672
- fig.add_trace(go.Scatterpolar(
1673
- r=factor_values,
1674
- theta=factors,
1675
- fill='toself',
1676
- name='عوامل مؤثر',
1677
- line_color='#1a8754'
1678
- ))
1679
-
1680
- fig.update_layout(
1681
- polar=dict(
1682
- radialaxis=dict(
1683
- visible=True,
1684
- range=[0, 100]
1685
- )
1686
- ),
1687
- showlegend=False,
1688
- font=dict(family='Vazirmatn'),
1689
- height=400
1690
- )
1691
-
1692
- st.plotly_chart(fig, use_container_width=True)
1693
-
1694
- # Reporting Page
1695
- elif selected == "گزارش‌گیری":
1696
- st.markdown("## گزارش‌گیری پیشرفته")
1697
-
1698
- col1, col2 = st.columns([1, 2])
1699
-
1700
- with col1:
1701
- st_lottie(lottie_report, height=200, key="report_animation")
1702
-
1703
- with col2:
1704
- st.markdown("""
1705
- <div class="glass-card">
1706
- <h3 class="gradient-text">گزارش‌گیری پیشرفته</h3>
1707
- <p>در این بخش می‌توانید گزارش‌های مختلف از وضعیت مزارع تهیه کنید و آن‌ها را به صورت PDF یا Excel دانلود کنید.</p>
1708
- </div>
1709
- """, unsafe_allow_html=True)
1710
-
1711
- col1, col2 = st.columns(2)
1712
-
1713
- with col1:
1714
- start_date = st.date_input(
1715
- "تاریخ شروع",
1716
- value=datetime.now() - timedelta(days=30),
1717
- format="YYYY-MM-DD"
1718
- )
1719
-
1720
- with col2:
1721
- end_date = st.date_input(
1722
- "تاریخ پایان",
1723
- value=datetime.now(),
1724
- format="YYYY-MM-DD"
1725
- )
1726
-
1727
- report_type = st.selectbox(
1728
- "نوع گزارش",
1729
- options=["گزارش کلی", "گزارش رشد", "گزارش رطوبت", "گزارش مقایسه‌ای واریته‌ها"]
1730
- )
1731
-
1732
- if st.button("تولید گزارش", type="primary", use_container_width=True):
1733
- with st.spinner('در حال تولید گزارش...'):
1734
- time.sleep(2)
1735
-
1736
- if report_type == "گزارش کلی":
1737
- st.markdown("### گزارش کلی وضعیت مزارع")
1738
-
1739
- col1, col2, col3, col4 = st.columns(4)
1740
-
1741
- with col1:
1742
- st.metric("تعداد کل مزارع", len(farm_df))
1743
-
1744
- with col2:
1745
- st.metric("میانگین ارتفاع", f"{np.random.uniform(150, 200):.1f} cm")
1746
-
1747
- with col3:
1748
- st.metric("میانگین رطوبت", f"{np.random.uniform(60, 80):.1f}%")
1749
-
1750
- with col4:
1751
- st.metric("میانگین نیتروژن", f"{np.random.uniform(40, 60):.1f}%")
1752
-
1753
- farm_counts = farm_df['اداره'].value_counts()
1754
- fig = px.pie(
1755
- values=farm_counts.values,
1756
- names=farm_counts.index,
1757
- title='توزیع مزارع بر اساس اداره',
1758
- color_discrete_sequence=px.colors.sequential.Greens_r
1759
- )
1760
- fig.update_traces(textposition='inside', textinfo='percent+label')
1761
- fig.update_layout(font=dict(family="Vazirmatn"))
1762
- st.plotly_chart(fig, use_container_width=True)
1763
-
1764
- weeks = list(range(1, 23))
1765
- heights = [100 + i * 5 + np.random.normal(0, 10) for i in range(len(weeks))]
1766
- fig = px.line(
1767
- x=weeks,
1768
- y=heights,
1769
- title='روند رشد کلی مزارع',
1770
- labels={'x': 'هفته', 'y': 'ارتفاع (سانتی‌متر)'}
1771
- )
1772
- fig.update_layout(font=dict(family="Vazirmatn"))
1773
- st.plotly_chart(fig, use_container_width=True)
1774
-
1775
- top_farms = pd.DataFrame({
1776
- 'مزرعه': ['مزرعه ' + str(i) for i in range(1, 6)],
1777
- 'ارتفاع': [round(np.random.uniform(180, 220), 1) for _ in range(5)],
1778
- 'رطوبت': [round(np.random.uniform(60, 80), 1) for _ in range(5)],
1779
- 'نیتروژن': [round(np.random.uniform(40, 60), 1) for _ in range(5)]
1780
- })
1781
- st.markdown("### 5 مزرعه برتر")
1782
- st.dataframe(top_farms, use_container_width=True, hide_index=True)
1783
-
1784
- elif report_type == "گزارش رشد":
1785
- st.markdown("### گزارش رشد مزارع")
1786
-
1787
- col1, col2, col3 = st.columns(3)
1788
-
1789
- with col1:
1790
- st.metric("میانگین رشد هفتگی", f"{np.random.uniform(10, 15):.1f} cm")
1791
-
1792
- with col2:
1793
- st.metric("حداکثر رشد هفتگی", f"{np.random.uniform(20, 25):.1f} cm")
1794
-
1795
- with col3:
1796
- st.metric("حداقل رشد هفتگی", f"{np.random.uniform(5, 10):.1f} cm")
1797
-
1798
- weeks = list(range(1, 23))
1799
- farms = ['مزرعه 1', 'مزرعه 2', 'مزرعه 3', 'مزرعه 4', 'مزرعه 5']
1800
- fig = go.Figure()
1801
- for farm in farms:
1802
- heights = [100 + i * 5 + np.random.normal(0, 10) for i in range(len(weeks))]
1803
- fig.add_trace(go.Scatter(
1804
- x=weeks,
1805
- y=heights,
1806
- mode='lines+markers',
1807
- name=farm
1808
- ))
1809
- fig.update_layout(
1810
- title='روند رشد مزارع',
1811
- xaxis_title='هفته',
1812
- yaxis_title='ارتفاع (سانتی‌متر)',
1813
- font=dict(family="Vazirmatn"),
1814
- legend_title='مزرعه',
1815
- hovermode="x unified"
1816
- )
1817
- st.plotly_chart(fig, use_container_width=True)
1818
-
1819
- growth_rates = np.random.normal(12, 3, 1000)
1820
- fig = px.histogram(
1821
- x=growth_rates,
1822
- nbins=30,
1823
- title='توزیع نرخ رشد هفتگی',
1824
- labels={'x': 'نرخ رشد (سانتی‌متر در هفته)', 'y': 'فراوانی'},
1825
- color_discrete_sequence=['#1a8754']
1826
- )
1827
- fig.update_layout(font=dict(family="Vazirmatn"))
1828
- st.plotly_chart(fig, use_container_width=True)
1829
-
1830
- st.markdown("### عوامل مؤثر بر رشد")
1831
- factors = ['رطوبت', 'نیتروژن', 'دما', 'آبیاری', 'نور']
1832
- correlations = [0.8, 0.7, 0.5, 0.9, 0.6]
1833
- fig = px.bar(
1834
- x=factors,
1835
- y=correlations,
1836
- title='همبستگی عوامل مختلف با نرخ رشد',
1837
- labels={'x': 'عامل', 'y': 'ضریب همبستگی'},
1838
- color=correlations,
1839
- color_continuous_scale='Viridis'
1840
- )
1841
- fig.update_layout(font=dict(family="Vazirmatn"))
1842
- st.plotly_chart(fig, use_container_width=True)
1843
-
1844
- elif report_type == "گزارش رطوبت":
1845
- st.markdown("### گزارش رطوبت مزارع")
1846
-
1847
- col1, col2, col3 = st.columns(3)
1848
-
1849
- with col1:
1850
- st.metric("میانگین رطوبت", f"{np.random.uniform(60, 70):.1f}%")
1851
-
1852
- with col2:
1853
- st.metric("حداکثر رطوبت", f"{np.random.uniform(80, 90):.1f}%")
1854
-
1855
- with col3:
1856
- st.metric("حداقل رطوبت", f"{np.random.uniform(40, 50):.1f}%")
1857
-
1858
- dates = pd.date_range(start=start_date, end=end_date)
1859
- moisture_levels = [np.random.uniform(50, 80) for _ in range(len(dates))]
1860
- fig = px.line(
1861
- x=dates,
1862
- y=moisture_levels,
1863
- title='روند رطوبت مزارع',
1864
- labels={'x': 'تاریخ', 'y': 'رطوبت (%)'}
1865
- )
1866
- fig.update_layout(font=dict(family="Vazirmatn"))
1867
- st.plotly_chart(fig, use_container_width=True)
1868
-
1869
- fig = px.histogram(
1870
- x=moisture_levels,
1871
- nbins=30,
1872
- title='توزیع رطوبت مزارع',
1873
- labels={'x': 'رطوبت (%)', 'y': 'فراوانی'},
1874
- color_discrete_sequence=['#1a8754']
1875
- )
1876
- fig.update_layout(font=dict(family="Vazirmatn"))
1877
- st.plotly_chart(fig, use_container_width=True)
1878
-
1879
- growth_levels = [h + np.random.normal(0, 10) for h in moisture_levels]
1880
- fig = px.scatter(
1881
- x=moisture_levels,
1882
- y=growth_levels,
1883
- title='رابطه بین رطوبت و رشد',
1884
- labels={'x': 'رطوبت (%)', 'y': 'رشد (سانتی‌متر)'},
1885
- trendline='ols'
1886
- )
1887
- fig.update_layout(font=dict(family="Vazirmatn"))
1888
- st.plotly_chart(fig, use_container_width=True)
1889
-
1890
- st.markdown("### توصیه‌های مدیریت رطوبت")
1891
- recommendations = [
1892
- "افزایش دفعات آبیاری در مزارع با رطوبت پایین",
1893
- "بهبود سیستم زهکشی در مزارع با رطوبت بالا",
1894
- "استفاده از مالچ برای حفظ رطوبت خاک",
1895
- "تنظیم زمان آبیاری بر اساس شرایط آب و هوایی",
1896
- "پایش مداوم رطوبت خاک با استفاده از سنسورها"
1897
- ]
1898
- for rec in recommendations:
1899
- st.markdown(f"- {rec}")
1900
-
1901
- elif report_type == "گزارش مقایسه‌ای واریته‌ها":
1902
- st.markdown("### گزارش مقایسه‌ای واریته‌های نیشکر")
1903
-
1904
- varieties = ['CP57-614', 'CP69-1062', 'CP73-21', 'SP70-1143', 'IRC99-02']
1905
- heights = [np.random.uniform(180, 220) for _ in varieties]
1906
- sugar_contents = [np.random.uniform(12, 16) for _ in varieties]
1907
- growth_rates = [np.random.uniform(10, 15) for _ in varieties]
1908
-
1909
- fig = go.Figure(data=[
1910
- go.Bar(name='ارتفاع (cm)', x=varieties, y=heights),
1911
- go.Bar(name='محتوای قند (%)', x=varieties, y=sugar_contents),
1912
- go.Bar(name='رشد (cm/هفته)', x=varieties, y=growth_rates)
1913
- ])
1914
- fig.update_layout(
1915
- title='مقایسه واریته‌های نیشکر',
1916
- xaxis_title='واریته',
1917
- yaxis_title='مقدار',
1918
- barmode='group',
1919
- font=dict(family="Vazirmatn")
1920
- )
1921
- st.plotly_chart(fig, use_container_width=True)
1922
-
1923
- variety_data = pd.DataFrame({
1924
- 'واریته': varieties,
1925
- 'ارتفاع (cm)': [round(h, 1) for h in heights],
1926
- 'محتوای قند (%)': [round(s, 1) for s in sugar_contents],
1927
- 'رشد(cm/هفته)': [round(g, 1) for g in growth_rates],
1928
- 'مقاومت به آفات': [np.random.choice(['کم', 'متوسط', 'زیاد']) for _ in varieties],
1929
- 'نیاز آبی': [np.random.choice(['کم', 'متوسط', 'زیاد']) for _ in varieties]
1930
- })
1931
- st.dataframe(variety_data, use_container_width=True, hide_index=True)
1932
-
1933
- categories = ['ارتفاع', 'محتوای قند', 'رشد', 'مقاومت به آفات', 'بهره‌وری آب']
1934
- fig = go.Figure()
1935
- for variety in varieties:
1936
- values = [
1937
- heights[varieties.index(variety)] / max(heights) * 100,
1938
- sugar_contents[varieties.index(variety)] / max(sugar_contents) * 100,
1939
- growth_rates[varieties.index(variety)] / max(growth_rates) * 100,
1940
- np.random.uniform(60, 100),
1941
- np.random.uniform(60, 100)
1942
- ]
1943
- fig.add_trace(go.Scatterpolar(
1944
- r=values,
1945
- theta=categories,
1946
- fill='toself',
1947
- name=variety
1948
- ))
1949
- fig.update_layout(
1950
- polar=dict(
1951
- radialaxis=dict(
1952
- visible=True,
1953
- range=[0, 100]
1954
- )
1955
- ),
1956
- title='مقایسه واریته‌ها',
1957
- font=dict(family="Vazirmatn"),
1958
- showlegend=True
1959
- )
1960
- st.plotly_chart(fig, use_container_width=True)
1961
-
1962
- st.markdown("### توصیه‌های کشت واریته‌ها")
1963
- recommendations = [
1964
- f"واریته {np.random.choice(varieties)} برای مناطق با آب و هوای گرم و مرطوب مناسب‌تر است.",
1965
- f"برای افزایش عملکرد تولید شکر، کشت واریته {np.random.choice(varieties)} توصیه می‌شود.",
1966
- f"در مناطق با محدودیت آب، استفاده از واریته {np.random.choice(varieties)} به دلیل نیاز آبی کمتر مناسب است.",
1967
- f"برای مقاومت بهتر در برا��ر آفات، واریته {np.random.choice(varieties)} پیشنهاد می‌شود.",
1968
- "تنوع در کشت واریته‌ها می‌تواند به کاهش ریسک‌های مرتبط با آفات و بیماری‌ها کمک کند."
1969
- ]
1970
- for rec in recommendations:
1971
- st.markdown(f"- {rec}")
1972
-
1973
- col1, col2 = st.columns(2)
1974
- with col1:
1975
- st.download_button(
1976
- label="دانلود گزارش (PDF)",
1977
- data=b"This is a mock PDF report",
1978
- file_name="farm_report.pdf",
1979
- mime="application/pdf",
1980
- )
1981
- with col2:
1982
- st.download_button(
1983
- label="دانلود داده‌ها (Excel)",
1984
- data=b"This is a mock Excel file",
1985
- file_name="farm_data.xlsx",
1986
- mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
1987
- )
1988
-
1989
- # Settings Page
1990
- elif selected == "تنظیمات":
1991
- st.markdown("## تنظیمات سیستم")
1992
-
1993
- tab1, tab2, tab3, tab4 = st.tabs(["تنظیمات کاربری", "تنظیمات سیستم", "مدیریت داده‌ها", "پشتیبان‌گیری"])
1994
-
1995
- with tab1:
1996
- st.markdown("### تنظیمات کاربری")
1997
-
1998
- st.markdown("#### پروفایل کاربری")
1999
- col1, col2 = st.columns(2)
2000
- with col1:
2001
- user_name = st.text_input("نام کاربری", value="کاربر نمونه")
2002
- with col2:
2003
- user_email = st.text_input("ایمیل", value="[email protected]")
2004
-
2005
- user_role = st.selectbox(
2006
- "نقش کاربری",
2007
- options=["مدیریت مطالعات", "پرسنل", "اپراتور"],
2008
- index=1
2009
- )
2010
-
2011
- st.markdown("#### تغییر رمز عبور")
2012
- col1, col2 = st.columns(2)
2013
- with col1:
2014
- current_password = st.text_input("رمز عبور فعلی", type="password")
2015
- with col2:
2016
- new_password = st.text_input("رمز عبور جدید", type="password")
2017
-
2018
- confirm_password = st.text_input("تکرار رمز عبور جدید", type="password")
2019
-
2020
- if st.button("تغییر رمز عبور", type="primary"):
2021
- st.success("رمز عبور با موفقیت تغییر کرد.")
2022
-
2023
- st.markdown("#### تنظیمات اعلان‌ها")
2024
- email_notifications = st.checkbox("دریافت اعلان‌ها از طریق ایمیل", value=True)
2025
- sms_notifications = st.checkbox("دریافت اعلان‌ها از طریق پیامک", value=False)
2026
- notification_frequency = st.radio(
2027
- "تناوب دریافت اعلان‌ها",
2028
- options=["روزانه", "هفتگی", "ماهانه"],
2029
- index=1
2030
- )
2031
-
2032
- with tab2:
2033
- st.markdown("### تنظیمات سیستم")
2034
-
2035
- system_language = st.selectbox(
2036
- "زبان سیستم",
2037
- options=["فارسی", "English", "العربية"],
2038
- index=0
2039
- )
2040
-
2041
- date_format = st.selectbox(
2042
- "فرمت تاریخ",
2043
- options=["YYYY/MM/DD", "DD/MM/YYYY", "MM/DD/YYYY"],
2044
- index=0
2045
- )
2046
-
2047
- st.markdown("#### تنظیمات ظاهری")
2048
- theme = st.radio(
2049
- "تم",
2050
- options=["روشن", "تیره", "سیستم"],
2051
- index=2
2052
- )
2053
- primary_color = st.color_picker("رنگ اصلی", value="#1a8754")
2054
-
2055
- st.markdown("#### تنظیمات نقشه")
2056
- default_map_view = st.selectbox(
2057
- "نمای پیش‌فرض نقشه",
2058
- options=["نقشه", "ماهواره", "ترکیبی"],
2059
- index=0
2060
- )
2061
- default_map_layer = st.selectbox(
2062
- "لایه پیش‌فرض نقشه",
2063
- options=["NDVI", "NDMI", "EVI", "NDWI", "SoilMoisture"],
2064
- index=0
2065
- )
2066
-
2067
- st.markdown("#### تنظیمات مدل هوش مصنوعی")
2068
- ai_model = st.selectbox(
2069
- "مدل هوش مصنوعی",
2070
- options=["GPT-3", "GPT-4", "BERT"],
2071
- index=1
2072
- )
2073
- model_update_frequency = st.selectbox(
2074
- "تناوب به‌روزرسانی مدل",
2075
- options=["روزانه", "هفتگی", "ماهانه"],
2076
- index=1
2077
- )
2078
-
2079
- if st.button("ذخیره تنظیمات", type="primary"):
2080
- st.success("تنظیمات با موفقیت ذخیره شدند.")
2081
-
2082
- with tab3:
2083
- st.markdown("### مدیریت داده‌ها")
2084
-
2085
- st.markdown("#### ورود داده")
2086
- uploaded_file = st.file_uploader("آپلود فایل داده", type=["csv", "xlsx"])
2087
- if uploaded_file is not None:
2088
- st.success(f"فایل {uploaded_file.name} با موفقیت آپلود شد.")
2089
- if st.button("وارد کردن داده‌ها"):
2090
- st.info("در حال پردازش و وارد کردن داده‌ها...")
2091
- time.sleep(2)
2092
- st.success("داده‌ها با موفقیت وارد شدند.")
2093
-
2094
- st.markdown("#### خروجی داده")
2095
- export_format = st.selectbox(
2096
- "فرمت خروجی",
2097
- options=["CSV", "Excel", "JSON"],
2098
- index=1
2099
- )
2100
- if st.button("دریافت خروجی"):
2101
- st.info("در حال آماده‌سازی فایل خروجی...")
2102
- time.sleep(2)
2103
- st.success("فایل خروجی آماده دانلود است.")
2104
- st.download_button(
2105
- label="دانلود فایل خروجی",
2106
- data=b"This is a mock export file",
2107
- file_name=f"farm_data_export.{export_format.lower()}",
2108
- mime="application/octet-stream",
2109
- )
2110
-
2111
- st.markdown("#### پاکسازی داده‌ها")
2112
- cleanup_options = st.multiselect(
2113
- "گزینه‌های پاکسازی",
2114
- options=["حذف داده‌های تکراری", "حذف داده‌های ناقص", "نرمال‌سازی داده‌ها"],
2115
- default=["حذف داده‌های تکراری"]
2116
- )
2117
- if st.button("اجرای پاکسازی"):
2118
- st.info("در حال اجرای عملیات پاکسازی...")
2119
- time.sleep(2)
2120
- st.success("عملیات پاکسازی با موفقیت انجام شد.")
2121
-
2122
- st.markdown("#### تنظیمات نمایش داده")
2123
- chart_theme = st.selectbox(
2124
- "تم نمودارها",
2125
- options=["پیش‌فرض", "روشن", "تیره", "رنگی"],
2126
- index=0
2127
- )
2128
- show_data_labels = st.checkbox("نمایش برچسب‌های داده", value=True)
2129
- if st.button("اعمال تنظیمات نمایش"):
2130
- st.success("تنظیمات نمایش داده با موفقیت اعمال شدند.")
2131
-
2132
- with tab4:
2133
- st.markdown("### پشتیبان‌گیری و بازیابی")
2134
-
2135
- st.markdown("#### ایجاد نسخه پشتیبان")
2136
- backup_type = st.radio(
2137
- "نوع پشتیبان‌گیری",
2138
- options=["پشتیبان کامل", "پشتیبان افزایشی"],
2139
- index=0
2140
- )
2141
- include_images = st.checkbox("شامل تصاویر", value=True)
2142
- include_user_data = st.checkbox("شامل داده‌های کاربران", value=True)
2143
- if st.button("ایجاد نسخه پشتیبان", type="primary"):
2144
- st.info("در حال ایجاد نسخه پشتیبان...")
2145
- progress_bar = st.progress(0)
2146
- for i in range(100):
2147
- time.sleep(0.05)
2148
- progress_bar.progress(i + 1)
2149
- st.success("نسخه پشتیبان با موفقیت ایجاد شد.")
2150
-
2151
- st.markdown("#### بازیابی از نسخه پشتیبان")
2152
- backup_file = st.file_uploader("آپلود فایل پشتیبان", type=["zip", "bak"])
2153
- if backup_file is not None:
2154
- st.warning("هشدار: بازیابی از نسخه پشتیبان ممکن است داده‌های فعلی را بازنویسی کند.")
2155
- if st.button("شروع بازیابی"):
2156
- st.info("در حال بازیابی از نسخه پشتیبان...")
2157
- progress_bar = st.progress(0)
2158
- for i in range(100):
2159
- time.sleep(0.05)
2160
- progress_bar.progress(i + 1)
2161
- st.success("بازیابی از نسخه پشتیبان با موفقیت انجام شد.")
2162
-
2163
- st.markdown("#### تنظیمات پشتیبان‌گیری خودکار")
2164
- auto_backup = st.checkbox("فعال‌سازی پشتیبان‌گیری خودکار", value=True)
2165
- if auto_backup:
2166
- backup_frequency = st.selectbox(
2167
- "تناوب پشتیبان‌گیری",
2168
- options=["روزانه", "هفتگی", "ماهانه"],
2169
- index=1
2170
- )
2171
- backup_time = st.time_input("زمان پشتیبان‌گیری", value=datetime.now().replace(hour=1, minute=0, second=0, microsecond=0))
2172
- retain_backups = st.number_input("تعداد نسخه‌های پشتیبان برای نگهداری", min_value=1, value=7)
2173
-
2174
- if st.button("ذخیره تنظیمات پشتیبان‌گیری"):
2175
- st.success("تنظیمات پشتیبان‌گیری با موفقیت ذخیره شدند.")
2176
-
2177
- # Add a footer
2178
- st.markdown("""
2179
- <footer style="position: fixed; left: 0; bottom: 0; width: 100%; background-color: #1a8754; color: white; text-align: center; padding: 10px 0;">
2180
- <p>© سامانه هوشمند پایش مزارع نیشکر کشت و صنعت دهخدا | طراحی شده توسط تیم مطالعات کاربردی توسعه</p>
2181
- </footer>
2182
- """, unsafe_allow_html=True)
 
1
+ import streamlit as st
2
  import pandas as pd
3
  import numpy as np
4
+ import folium
5
  from streamlit_folium import folium_static
6
  import ee
7
  import os
 
28
  import pydeck as pdk
29
  import math
30
 
31
+
32
  # Page configuration with custom theme
33
  st.set_page_config(
34
  page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
 
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');
44
+
45
  * {
46
  font-family: 'Vazirmatn', sans-serif !important;
47
  }
48
+
49
  .main {
50
  background: linear-gradient(135deg, #f5f7fa 0%, #e4e9f2 100%);
51
  }
52
+
53
  .main-header {
54
  background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
55
  padding: 1.5rem;
 
60
  overflow: hidden;
61
  animation: header-glow 3s infinite alternate;
62
  }
63
+
64
  @keyframes header-glow {
65
  0% { box-shadow: 0 8px 32px rgba(26, 135, 84, 0.1); }
66
  100% { box-shadow: 0 8px 32px rgba(26, 135, 84, 0.3); }
67
  }
68
+
69
  .main-header::before {
70
  content: '';
71
  position: absolute;
 
77
  transform: rotate(30deg);
78
  z-index: 0;
79
  }
80
+
81
  .main-header h1 {
82
  color: white;
83
  font-weight: 700;
 
85
  position: relative;
86
  z-index: 1;
87
  }
88
+
89
  .main-header p {
90
  color: rgba(255, 255, 255, 0.8);
91
  margin: 0;
92
  position: relative;
93
  z-index: 1;
94
  }
95
+
96
  .stcard {
97
  border-radius: 12px;
98
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
99
  transition: all 0.3s ease;
100
  overflow: hidden;
101
  }
102
+
103
  .stcard:hover {
104
  transform: translateY(-5px);
105
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
106
  }
107
+
108
  .stButton>button {
109
  border-radius: 50px;
110
  padding: 0.5rem 1.5rem;
 
112
  transition: all 0.3s ease;
113
  border: none;
114
  }
115
+
116
  .stButton>button:hover {
117
  transform: translateY(-2px);
118
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
119
  }
120
+
121
  .primary-btn {
122
  background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
123
  color: white;
124
  }
125
+
126
  .secondary-btn {
127
  background: white;
128
  color: #1a8754;
129
  border: 1px solid #1a8754 !important;
130
  }
131
+
132
  .metric-card {
133
  background: white;
134
  border-radius: 12px;
 
137
  transition: all 0.3s ease;
138
  text-align: center;
139
  }
140
+
141
  .metric-card:hover {
142
  transform: translateY(-5px);
143
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
144
  }
145
+
146
  .metric-card .metric-value {
147
  font-size: 2.5rem;
148
  font-weight: 700;
149
  color: #1a8754;
150
  margin-bottom: 0.5rem;
151
  }
152
+
153
  .metric-card .metric-label {
154
  font-size: 1rem;
155
  color: #6c757d;
156
  }
157
+
158
  .map-container {
159
  border-radius: 12px;
160
  overflow: hidden;
161
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
162
  }
163
+
164
  .stTabs [data-baseweb="tab-list"] {
165
  gap: 8px;
166
  }
167
+
168
  .stTabs [data-baseweb="tab"] {
169
  border-radius: 4px 4px 0px 0px;
170
  padding: 10px 16px;
171
  background-color: #f8f9fa;
172
  }
173
+
174
  .stTabs [aria-selected="true"] {
175
  background-color: #1a8754 !important;
176
  color: white !important;
177
  }
178
+
179
  [data-testid="stSidebar"] {
180
  background-color: #ffffff;
181
  border-right: 1px solid #e9ecef;
 
185
  0% { opacity: 0; transform: translateY(20px); }
186
  100% { opacity: 1; transform: translateY(0); }
187
  }
188
+
189
  .animate-fadeIn {
190
  animation: fadeIn 0.5s ease forwards;
191
  }
192
+
193
  .loading-spinner {
194
  display: flex;
195
  justify-content: center;
196
  align-items: center;
197
  height: 100px;
198
  }
199
+
200
  .loading-spinner::after {
201
  content: "";
202
  width: 40px;
 
206
  border-radius: 50%;
207
  animation: spin 1s linear infinite;
208
  }
209
+
210
  @keyframes spin {
211
  0% { transform: rotate(0deg); }
212
  100% { transform: rotate(360deg); }
213
  }
214
+
215
  .rtl {
216
  direction: rtl;
217
  text-align: right;
218
  }
219
+
220
  ::-webkit-scrollbar {
221
  width: 8px;
222
  height: 8px;
223
  }
224
+
225
  ::-webkit-scrollbar-track {
226
  background: #f1f1f1;
227
  border-radius: 10px;
228
  }
229
+
230
  ::-webkit-scrollbar-thumb {
231
  background: #1a8754;
232
  border-radius: 10px;
233
  }
234
+
235
  ::-webkit-scrollbar-thumb:hover {
236
  background: #115740;
237
  }
238
+
239
  .tooltip {
240
  position: relative;
241
  display: inline-block;
242
  }
243
+
244
  .tooltip .tooltiptext {
245
  visibility: hidden;
246
  width: 120px;
 
257
  opacity: 0;
258
  transition: opacity 0.3s;
259
  }
260
+
261
  .tooltip:hover .tooltiptext {
262
  visibility: visible;
263
  opacity: 1;
264
  }
265
+
266
  .dataframe {
267
  border-collapse: collapse;
268
  width: 100%;
 
270
  overflow: hidden;
271
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
272
  }
273
+
274
  .dataframe th {
275
  background-color: #1a8754;
276
  color: white;
277
  padding: 12px;
278
  text-align: right;
279
  }
280
+
281
  .dataframe td {
282
  padding: 10px 12px;
283
  border-bottom: 1px solid #e9ecef;
284
  }
285
+
286
  .dataframe tr:nth-child(even) {
287
  background-color: #f8f9fa;
288
  }
289
+
290
  .dataframe tr:hover {
291
  background-color: #e9ecef;
292
  }
293
+
294
  .stProgress > div > div > div > div {
295
  background-color: #1a8754;
296
  }
297
+
298
  .notification {
299
  background-color: #d1e7dd;
300
  color: #0f5132;
 
305
  align-items: center;
306
  animation: slideIn 0.5s ease;
307
  }
308
+
309
  @keyframes slideIn {
310
  0% { transform: translateX(100%); opacity: 0; }
311
  100% { transform: translateX(0); opacity: 1; }
312
  }
313
+
314
  .notification-icon {
315
  margin-right: 0.5rem;
316
  font-size: 1.2rem;
317
  }
318
+
319
  .custom-select {
320
  background-color: white;
321
  border-radius: 8px;
 
323
  border: 1px solid #ced4da;
324
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
325
  }
326
+
327
  .glass-card {
328
  background: rgba(255, 255, 255, 0.7);
329
  backdrop-filter: blur(10px);
 
333
  padding: 1.5rem;
334
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
335
  }
336
+
337
  .neumorphic-card {
338
  background: #f0f0f3;
339
  border-radius: 12px;
340
  box-shadow: 10px 10px 20px #d1d1d4, -10px -10px 20px #ffffff;
341
  padding: 1.5rem;
342
  }
343
+
344
  .gradient-text {
345
  background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
346
  -webkit-background-clip: text;
347
  -webkit-text-fill-color: transparent;
348
  font-weight: 700;
349
  }
350
+
351
  @keyframes pulse {
352
  0% { transform: scale(1); }
353
  50% { transform: scale(1.05); }
354
  100% { transform: scale(1); }
355
  }
356
+
357
  .pulse-animation {
358
  animation: pulse 2s infinite;
359
  }
360
+
361
  .stRadio > div {
362
  display: flex;
363
  gap: 10px;
364
  }
365
+
366
  .stRadio label {
367
  cursor: pointer;
368
  background-color: #f8f9fa;
 
370
  border-radius: 50px;
371
  transition: all 0.3s ease;
372
  }
373
+
374
  .stRadio label:hover {
375
  background-color: #e9ecef;
376
  }
377
+
378
  .stRadio input {
379
  display: none;
380
  }
381
+
382
  .stRadio input:checked + label {
383
  background-color: #1a8754;
384
  color: white;
385
  }
386
+
387
  .stSelectbox, .stNumberInput {
388
  background-color: #f0f2f6;
389
  border-radius: 10px;
390
  padding: 10px;
391
  margin: 10px 0;
392
  }
393
+
394
  .custom-card {
395
  background-color: white;
396
  padding: 20px;
 
398
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
399
  margin: 10px 0;
400
  }
401
+
402
  .metric-container {
403
  display: flex;
404
  justify-content: space-between;
405
  flex-wrap: wrap;
406
  }
407
+
408
  .metric-card {
409
  background-color: #1a8754;
410
  color: white;
 
436
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dehkhodamap-e9f0da4ce9f6514021%40ee-esmaeilkiani13877.iam.gserviceaccount.com",
437
  "universe_domain": "googleapis.com"
438
  }
439
+
440
  credentials_file = 'ee-esmaeilkiani13877-cfdea6eaf411.json'
441
  with open(credentials_file, 'w') as f:
442
  json.dump(credentials_dict, f)
443
+
444
  credentials = ee.ServiceAccountCredentials(service_account, credentials_file)
445
  ee.Initialize(credentials)
446
+
447
  os.remove(credentials_file)
448
+
449
  return True
450
  except Exception as e:
451
  st.error(f"خطا در اتصال به Earth Engine: {e}")
 
507
  try:
508
  farm_row = coordinates_df[coordinates_df['مزرعه'] == farm_id].iloc[0]
509
  lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی']
510
+
511
  m = folium.Map(location=[lat, lon], zoom_start=14, tiles='CartoDB positron')
512
+
513
  date_obj = datetime.strptime(date_str, '%Y-%m-%d')
514
  start_date = (date_obj - timedelta(days=5)).strftime('%Y-%m-%d')
515
  end_date = (date_obj + timedelta(days=5)).strftime('%Y-%m-%d')
516
+
517
  region = ee.Geometry.Point([lon, lat]).buffer(1500)
518
+
519
+
 
 
 
 
 
520
  if layer_type == "NDVI":
521
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
522
+ .filterDate(start_date, end_date) \
523
+ .filterBounds(region) \
524
+ .sort('CLOUDY_PIXEL_PERCENTAGE') \
525
+ .first()
526
+
527
  index = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
528
  viz_params = {'min': -0.2, 'max': 0.8, 'palette': ['#ff0000', '#ff4500', '#ffd700', '#32cd32', '#006400']}
529
  legend_title = 'شاخص پوشش گیاهی (NDVI)'
530
  elif layer_type == "NDMI":
531
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
532
+ .filterDate(start_date, end_date) \
533
+ .filterBounds(region) \
534
+ .sort('CLOUDY_PIXEL_PERCENTAGE') \
535
+ .first()
536
  index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
537
  viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
538
  legend_title = 'شاخص رطوبت (NDMI)'
539
  elif layer_type == "EVI":
540
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
541
+ .filterDate(start_date, end_date) \
542
+ .filterBounds(region) \
543
+ .sort('CLOUDY_PIXEL_PERCENTAGE') \
544
+ .first()
545
  nir = s2.select('B8')
546
  red = s2.select('B4')
547
  blue = s2.select('B2')
 
549
  viz_params = {'min': 0, 'max': 1, 'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#4caf50']}
550
  legend_title = 'شاخص پیشرفته گیاهی (EVI)'
551
  elif layer_type == "NDWI":
552
+ s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
553
+ .filterDate(start_date, end_date) \
554
+ .filterBounds(region) \
555
+ .sort('CLOUDY_PIXEL_PERCENTAGE') \
556
+ .first()
557
  index = s2.normalizedDifference(['B3', 'B8']).rename('NDWI')
558
  viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#00008b', '#00b7eb', '#add8e6', '#fdae61', '#d73027']}
559
  legend_title = 'شاخص آب (NDWI)'
560
  elif layer_type == "SoilMoisture":
561
+ try:
562
+ s1 = ee.ImageCollection('COPERNICUS/S1_GRD') \
563
+ .filterDate(start_date, end_date) \
564
+ .filterBounds(region) \
565
+ .filter(ee.Filter.listContains('transmitterReceiverPolarisation', 'VV')) \
566
+ .sort('system:time_start') \
567
+ .first()
568
+
569
+ soil_moisture = s1.select('VV')
570
+
571
+ viz_params = {
572
+ 'min': -25,
573
+ 'max': -5,
574
+ 'palette': ['#00008b', '#00b7eb', '#add8e6', '#ffffff'],
575
+ 'bands': 'VV'
576
+ }
577
+ legend_title = 'رطوبت خاک (VV)'
578
+
579
+ soil_moisture_db = soil_moisture.log10().multiply(10.0)
580
+ map_id_dict = soil_moisture_db.getMapId(viz_params)
581
+
582
+ folium.TileLayer(
583
+ tiles=map_id_dict['tile_fetcher'].url_format,
584
+ attr='Google Earth Engine',
585
+ name=layer_type,
586
+ overlay=True,
587
+ control=True
588
+ ).add_to(m)
589
+
590
+ except Exception as e:
591
+ st.error(f"Error loading Soil Moisture data: {e}")
592
+ return None
593
+
594
+ if layer_type != "SoilMoisture": # for other layers
595
+ map_id_dict = ee.Image(index).getMapId(viz_params)
596
+ folium.TileLayer(
597
+ tiles=map_id_dict['tile_fetcher'].url_format,
598
+ attr='Google Earth Engine',
599
+ name=layer_type,
600
+ overlay=True,
601
+ control=True
602
+ ).add_to(m)
603
+
604
+
605
  folium.Marker(
606
+ [lat, lon],
607
+ popup=f'مزرعه {farm_id}',
608
  tooltip=f'م��رعه {farm_id}',
609
  icon=folium.Icon(color='green', icon='leaf')
610
  ).add_to(m)
611
+
612
  folium.Circle(
613
  [lat, lon],
614
  radius=1500,
 
617
  fill_color='green',
618
  fill_opacity=0.1
619
  ).add_to(m)
620
+
621
  folium.LayerControl().add_to(m)
622
+
623
  legend_html = '''
624
+ <div style="position: fixed;
625
+ bottom: 50px; right: 50px;
626
+ border: 2px solid grey; z-index: 9999;
627
  background-color: white;
628
  padding: 10px;
629
  border-radius: 5px;
 
644
  </div>
645
  </div>
646
  '''
647
+ if layer_type == "SoilMoisture":
648
+ legend_html = '''
649
+ <div style="position: fixed;
650
+ bottom: 50px; right: 50px;
651
+ border: 2px solid grey; z-index: 9999;
652
+ background-color: white;
653
+ padding: 10px;
654
+ border-radius: 5px;
655
+ direction: rtl;
656
+ font-family: 'Vazirmatn', sans-serif;">
657
+ <div style="font-size: 14px; font-weight: bold; margin-bottom: 5px;">''' + legend_title + '''</div>
658
+ <div style="display: flex; align-items: center; margin-bottom: 5px;">
659
+ <div style="background: ''' + viz_params['palette'][0] + '''; width: 20px; height: 20px; margin-left: 5px;"></div>
660
+ <span>خشک</span>
661
+ </div>
662
+ <div style="display: flex; align-items: center; margin-bottom: 5px;">
663
+ <div style="background: ''' + viz_params['palette'][2] + '''; width: 20px; height: 20px; margin-left: 5px;"></div>
664
+ <span>معتدل</span>
665
+ </div>
666
+ <div style="display: flex; align-items: center;">
667
+ <div style="background: ''' + viz_params['palette'][-1] + '''; width: 20px; height: 20px; margin-left: 5px;"></div>
668
+ <span>مرطوب</span>
669
+ </div>
670
+ </div>
671
+ '''
672
+
673
  m.get_root().html.add_child(folium.Element(legend_html))
674
+
675
  return m
676
  except Exception as e:
677
+ st.error(f"خط