Esmaeilkianii commited on
Commit
8b37100
·
verified ·
1 Parent(s): a3ab3e3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +30 -238
app.py CHANGED
@@ -244,15 +244,6 @@ def display_metric_card(title, value, icon="📊"):
244
  </div>
245
  """, unsafe_allow_html=True)
246
 
247
- # تابع برای تبدیل ستون به مقادیر عددی با مدیریت خطا
248
- def convert_to_numeric(df, column_name):
249
- try:
250
- # تلاش برای تبدیل ستون به عدد و جایگزینی خطاها با NaN
251
- return pd.to_numeric(df[column_name], errors='coerce')
252
- except Exception as e:
253
- st.warning(f"خطا در تبدیل ستون {column_name} به عدد: {e}")
254
- return pd.Series([np.nan] * len(df))
255
-
256
  # تابع برای بارگذاری داده‌های مزارع نیشکر
257
  def load_farm_data():
258
  try:
@@ -262,28 +253,20 @@ def load_farm_data():
262
  # بارگذاری فایل پایگاه داده مزارع
263
  farm_database = pd.read_csv('پایگاه داده (1).csv')
264
 
265
- # تبدیل ستون‌های عددی به عدد با مدیریت خطا
266
- if 'سن' in farm_coordinates.columns:
267
- farm_coordinates['سن'] = convert_to_numeric(farm_coordinates, 'سن')
268
-
269
- if 'سن' in farm_database.columns:
270
- farm_database['سن'] = convert_to_numeric(farm_database, 'سن')
271
-
272
- if 'مساحت' in farm_database.columns:
273
- farm_database['مساحت'] = convert_to_numeric(farm_database, 'مساحت')
274
-
275
- if 'مساحت زیرمجموعه' in farm_database.columns:
276
- farm_database['مساحت زیرمجموعه'] = convert_to_numeric(farm_database, 'مساحت زیرمجموعه')
277
-
278
  # ادغام داده‌ها بر اساس ستون مزرعه
279
  merged_data = pd.merge(farm_database, farm_coordinates, on='مزرعه', how='inner')
280
 
 
 
 
 
 
 
281
  return farm_coordinates, farm_database, merged_data
282
  except Exception as e:
283
  st.error(f"خطا در بارگذاری داده‌ها: {e}")
284
 
285
  # ایجاد داده‌های نمونه در صورت عدم وجود فایل‌ها
286
- # داده‌های نمونه برای مختصات مزارع
287
  farm_coordinates = pd.DataFrame({
288
  'مزرعه': [f'مزرعه {i}' for i in range(1, 21)],
289
  'سن': np.random.randint(1, 10, 20),
@@ -292,7 +275,6 @@ def load_farm_data():
292
  'عرض جغرافیایی': np.random.uniform(31.0, 32.0, 20)
293
  })
294
 
295
- # داده‌های نمونه برای پایگاه داده مزارع
296
  days = ['شنبه', 'یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه']
297
  farm_database = pd.DataFrame({
298
  'مزرعه': [f'مزرعه {i}' for i in range(1, 21)],
@@ -309,26 +291,28 @@ def load_farm_data():
309
  # ادغام داده‌ها
310
  merged_data = pd.merge(farm_database, farm_coordinates, on='مزرعه', how='inner')
311
 
 
 
 
 
 
 
312
  return farm_coordinates, farm_database, merged_data
313
 
314
  # تابع برای ایجاد نقشه مزارع
315
  def create_farm_map(df, selected_farms=None):
316
- # ایجاد نقشه با مرکزیت میانگین مختصات
317
  center_lat = df['عرض جغرافیایی'].mean()
318
  center_lon = df['طول جغرافیایی'].mean()
319
  m = folium.Map(location=[center_lat, center_lon], zoom_start=10)
320
 
321
- # اضافه کردن لایه‌های مختلف
322
  folium.TileLayer('openstreetmap').add_to(m)
323
  folium.TileLayer('Stamen Terrain').add_to(m)
324
  folium.TileLayer('Stamen Toner').add_to(m)
325
  folium.TileLayer('Stamen Watercolor').add_to(m)
326
  folium.TileLayer('CartoDB positron').add_to(m)
327
 
328
- # اضافه کردن کنترل لایه‌ها
329
  folium.LayerControl().add_to(m)
330
 
331
- # اضافه کردن مارکرها برای هر مزرعه
332
  for idx, row in df.iterrows():
333
  color = 'green'
334
  if selected_farms is not None and row['مزرعه'] in selected_farms:
@@ -357,7 +341,6 @@ def create_farm_map(df, selected_farms=None):
357
 
358
  # تابع برای ایجاد نمودارهای تحلیلی
359
  def create_analysis_charts(df):
360
- # نمودار توزیع واریته
361
  variety_column = 'واریته' if 'واریته' in df.columns else 'تنوع'
362
  fig_variety = px.pie(
363
  df,
@@ -371,7 +354,6 @@ def create_analysis_charts(df):
371
  title_font_size=20
372
  )
373
 
374
- # ن��ودار سن مزارع
375
  age_column = 'سن_x' if 'سن_x' in df.columns else 'سن'
376
  fig_age = px.histogram(
377
  df,
@@ -387,7 +369,6 @@ def create_analysis_charts(df):
387
  title_font_size=20
388
  )
389
 
390
- # نمودار مساحت مزارع
391
  if 'مساحت' in df.columns:
392
  fig_area = px.scatter(
393
  df,
@@ -412,7 +393,6 @@ def create_analysis_charts(df):
412
 
413
  # تابع برای ایجاد مدل پیش‌بینی
414
  def create_prediction_model(df):
415
- # ایجاد داده‌های نمونه برای سری زمانی
416
  dates = pd.date_range(end=datetime.now(), periods=90)
417
  farm_ids = df['مزرعه'].unique()
418
 
@@ -422,16 +402,15 @@ def create_prediction_model(df):
422
  base_height = np.random.uniform(0.5, 3.0)
423
 
424
  for date in dates:
425
- # شبیه‌سازی رشد با الگوی سینوسی + روند
426
  day_of_year = date.dayofyear
427
  seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
428
  trend_factor = date.dayofyear / 365 * 0.3
429
 
430
  ndvi = base_ndvi + seasonal_factor + trend_factor + np.random.normal(0, 0.05)
431
- ndvi = max(0, min(1, ndvi)) # محدود کردن به بازه 0 تا 1
432
 
433
  height = base_height + seasonal_factor * 2 + trend_factor + np.random.normal(0, 0.1)
434
- height = max(0, height) # ارتفاع نمی‌تواند منفی باشد
435
 
436
  time_series_data.append({
437
  'farm_id': farm_id,
@@ -444,22 +423,17 @@ def create_prediction_model(df):
444
 
445
  time_df = pd.DataFrame(time_series_data)
446
 
447
- # ایجاد ویژگی‌ها برای مدل
448
  X = time_df[['ndvi', 'day_of_year', 'month']].values
449
  y = time_df['height'].values
450
 
451
- # تقسیم داده‌ها به آموزش و آزمون
452
  X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
453
 
454
- # آموزش مدل
455
  model = RandomForestRegressor(n_estimators=100, random_state=42)
456
  model.fit(X_train, y_train)
457
 
458
- # ارزیابی مدل
459
  y_pred = model.predict(X_test)
460
  mse = mean_squared_error(y_test, y_pred)
461
 
462
- # ایجاد نمودار برای نمایش نتایج
463
  results_df = pd.DataFrame({
464
  'Actual': y_test,
465
  'Predicted': y_pred
@@ -488,8 +462,7 @@ def create_prediction_model(df):
488
  title_font_size=20
489
  )
490
 
491
- # ایجاد نمودار سری زمانی
492
- farm_id = farm_ids[0] # انتخاب اولین مزرعه برای نمایش
493
  farm_data = time_df[time_df['farm_id'] == farm_id]
494
 
495
  fig_ts = px.line(
@@ -515,7 +488,6 @@ farm_coordinates, farm_database, merged_data = load_farm_data()
515
  # منوی اصلی
516
  st.title("🌱 سامانه مانیتورینگ مزارع نیشکر")
517
 
518
- # منوی افقی
519
  tabs = st.tabs([
520
  "📊 داشبورد",
521
  "🗺️ نقشه مزارع",
@@ -525,11 +497,9 @@ tabs = st.tabs([
525
  "⚙️ تنظیمات"
526
  ])
527
 
528
- # تب داشبورد
529
  with tabs[0]:
530
  st.header("داشبورد مدیریت مزارع نیشکر")
531
 
532
- # کارت‌های اطلاعاتی
533
  col1, col2, col3, col4 = st.columns(4)
534
 
535
  with col1:
@@ -540,41 +510,18 @@ with tabs[0]:
540
  display_metric_card("تعداد واریته‌ها", unique_varieties, "🌿")
541
 
542
  with col3:
543
- # محاسبه میانگین سن با مدیریت خطا
544
- try:
545
- age_column = 'سن_x' if 'سن_x' in merged_data.columns else 'سن'
546
- # تبدیل به عدد با مدیریت خطا
547
- age_values = pd.to_numeric(merged_data[age_column], errors='coerce')
548
- # محاسبه میانگین با نادیده گرفتن مقادیر NaN
549
- avg_age = age_values.mean()
550
- if pd.isna(avg_age):
551
- display_metric_card("میانگین سن", "نامشخص", "📅")
552
- else:
553
- display_metric_card("میانگین سن", f"{avg_age:.1f} سال", "📅")
554
- except Exception as e:
555
- st.warning(f"خطا در محاسبه میانگین سن: {e}")
556
- display_metric_card("میانگین سن", "نامشخص", "📅")
557
 
558
  with col4:
559
  if 'مساحت' in merged_data.columns:
560
- try:
561
- # تبدیل به عدد با مدیریت خطا
562
- area_values = pd.to_numeric(merged_data['مساحت'], errors='coerce')
563
- # محاسبه مجموع با نادیده گرفتن مقادیر NaN
564
- total_area = area_values.sum()
565
- if pd.isna(total_area):
566
- display_metric_card("مساحت کل", "نامشخص", "📏")
567
- else:
568
- display_metric_card("مساحت کل", f"{total_area:.1f} هکتار", "📏")
569
- except Exception as e:
570
- st.warning(f"خطا در محاسبه مساحت کل: {e}")
571
- display_metric_card("مساحت کل", "نامشخص", "📏")
572
  else:
573
  display_metric_card("تعداد کانال‌ها", len(merged_data['کانال'].unique()) if 'کانال' in merged_data.columns else "نامشخص", "🚿")
574
 
575
  st.markdown("---")
576
 
577
- # نمودارهای تحلیلی
578
  st.subheader("تحلیل کلی مزارع")
579
 
580
  col1, col2 = st.columns(2)
@@ -588,10 +535,8 @@ with tabs[0]:
588
  if fig_area:
589
  st.plotly_chart(fig_area, use_container_width=True)
590
 
591
- # جدول اطلاعات مزارع
592
  st.subheader("اطلاعات مزارع")
593
 
594
- # انتخاب ستون‌های مهم برای نمایش
595
  if 'مساحت' in merged_data.columns:
596
  display_columns = ['مزرعه', 'کانال', 'اداره', 'سن_x', 'واریته', 'مساحت', 'روز']
597
  column_config = {
@@ -619,7 +564,6 @@ with tabs[0]:
619
  hide_index=True
620
  )
621
 
622
- # تب نقشه مزارع
623
  with tabs[1]:
624
  st.header("نقشه مزارع نیشکر")
625
 
@@ -628,16 +572,13 @@ with tabs[1]:
628
  with col1:
629
  st.subheader("فیلتر مزارع")
630
 
631
- # فیلتر بر اساس روز هفته
632
  days = ['همه روزها', 'شنبه', 'یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه']
633
  selected_day = st.selectbox("انتخاب روز هفته", days)
634
 
635
- # فیلتر بر اساس واریته
636
  variety_column = 'واریته' if 'واریته' in merged_data.columns else 'تنوع'
637
  varieties = ['همه واریته‌ها'] + list(merged_data[variety_column].unique())
638
  selected_variety = st.selectbox("انتخاب واریته", varieties)
639
 
640
- # فیلتر بر اساس سن
641
  age_column = 'سن_x' if 'سن_x' in merged_data.columns else 'سن'
642
  min_age, max_age = int(merged_data[age_column].min()), int(merged_data[age_column].max())
643
  age_range = st.slider(
@@ -647,7 +588,6 @@ with tabs[1]:
647
  value=(min_age, max_age)
648
  )
649
 
650
- # اعمال فیلترها
651
  filtered_df = merged_data.copy()
652
 
653
  if selected_day != 'همه روزها' and 'روز' in filtered_df.columns:
@@ -661,14 +601,12 @@ with tabs[1]:
661
  st.write(f"تعداد مزارع نمایش داده شده: {len(filtered_df)}")
662
 
663
  with col2:
664
- # نمایش نقشه
665
  if len(filtered_df) > 0:
666
  farm_map = create_farm_map(filtered_df)
667
  folium_static(farm_map, width=800, height=500)
668
  else:
669
  st.warning("هیچ مزرعه‌ای با فیلترهای انتخاب شده یافت نشد.")
670
 
671
- # تب ورود اطلاعات
672
  with tabs[2]:
673
  st.header("ورود و آپلود اطلاعات")
674
 
@@ -680,14 +618,12 @@ with tabs[2]:
680
  col1, col2 = st.columns(2)
681
 
682
  with col1:
683
- # انتخاب مزرعه موجود یا ایجاد مزرعه جدید
684
  option = st.radio("انتخاب گزینه", ["ویرایش مزرعه موجود", "ایجاد مزرعه جدید"])
685
 
686
  if option == "ویرایش مزرعه موجود":
687
  farm_name = st.selectbox("انتخاب مزرعه", merged_data['مزرعه'].tolist())
688
  selected_farm = merged_data[merged_data['مزرعه'] == farm_name].iloc[0]
689
 
690
- # فیلدهای ورودی بر اساس ستون‌های موجود
691
  if 'کانال' in merged_data.columns:
692
  canal = st.text_input("کانال", value=selected_farm['کانال'])
693
  office = st.text_input("اداره", value=selected_farm['اداره'])
@@ -706,10 +642,8 @@ with tabs[2]:
706
  if st.button("بروزرسانی اطلاعات"):
707
  st.success("اطلاعات مزرعه با موفقیت بروزرسانی شد.")
708
  else:
709
- # ایجاد مزرعه جدید
710
  farm_name = st.text_input("نام مزرعه", value=f"مزرعه جدید")
711
 
712
- # فیلدهای ورودی بر اساس ستون‌های موجود
713
  if 'کانال' in merged_data.columns:
714
  canal = st.text_input("کانال")
715
  office = st.text_input("اداره")
@@ -727,11 +661,9 @@ with tabs[2]:
727
  st.success("مزرعه جدید با موفقیت ثبت شد.")
728
 
729
  with col2:
730
- # نمایش نقشه برای انتخاب موقعیت
731
  st.subheader("انتخاب موقعیت روی نقشه")
732
  st.write("برای انتخاب موقعیت دقیق، روی نقشه کلیک کنید.")
733
 
734
- # نمایش نقشه
735
  center_lat = merged_data['عرض جغرافیایی'].mean()
736
  center_lon = merged_data['طول جغرافیایی'].mean()
737
  m = folium.Map(location=[center_lat, center_lon], zoom_start=10)
@@ -790,7 +722,6 @@ with tabs[2]:
790
  except Exception as e:
791
  st.error(f"خطا در خواندن فایل: {e}")
792
 
793
- # تب تحلیل داده‌ها
794
  with tabs[3]:
795
  st.header("تحلیل داده‌ها")
796
 
@@ -799,35 +730,27 @@ with tabs[3]:
799
  with analysis_tabs[0]:
800
  st.subheader("محاسبه و نمایش شاخص‌های گیاهی")
801
 
802
- # اتصال به Google Earth Engine
803
  ee_connected = initialize_earth_engine()
804
 
805
  if ee_connected:
806
- # انتخاب مزرعه
807
  farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_indices")
808
  selected_farm = merged_data[merged_data['مزرعه'] == farm_name].iloc[0]
809
 
810
- # انتخاب تاریخ
811
  date = st.date_input("انتخاب تاریخ", value=datetime.now() - timedelta(days=7))
812
 
813
  if st.button("محاسبه شاخص‌ها"):
814
  with st.spinner("در حال محاسبه شاخص‌ها..."):
815
- # ایجاد geometry برای مزرعه
816
  point = ee.Geometry.Point([selected_farm['طول جغرافیایی'], selected_farm['عرض جغرافیایی']])
817
- buffer = point.buffer(100) # بافر 100 متری
818
 
819
- # محاسبه شاخص‌ها
820
  indices = calculate_indices(date.strftime('%Y-%m-%d'), buffer)
821
 
822
  if indices is not None:
823
- # نمایش نتایج
824
  st.success("شاخص‌ها با موفقیت محاسبه شدند.")
825
 
826
- # ایجاد نقشه برای نمایش شاخص‌ها
827
  Map = geemap.Map()
828
  Map.centerObject(buffer, 14)
829
 
830
- # اضافه کردن لایه‌های مختلف
831
  vis_params = {
832
  'min': 0,
833
  'max': 1,
@@ -841,13 +764,10 @@ with tabs[3]:
841
  Map.addLayer(indices.select('LAI'), {'min': 0, 'max': 5, 'palette': ['red', 'yellow', 'green']}, 'LAI')
842
  Map.addLayer(indices.select('CHL'), {'min': 0, 'max': 3, 'palette': ['red', 'yellow', 'green']}, 'CHL')
843
 
844
- # اضافه کردن کنترل لایه‌ها
845
  Map.addLayerControl()
846
 
847
- # نمایش نقشه
848
  Map.to_streamlit(height=500)
849
 
850
- # نمایش مقادیر میانگین
851
  col1, col2, col3 = st.columns(3)
852
 
853
  with col1:
@@ -867,16 +787,12 @@ with tabs[3]:
867
  with analysis_tabs[1]:
868
  st.subheader("تحلیل سری زمانی")
869
 
870
- # ایجاد مدل و نمودارها
871
  model, fig_model, fig_ts, time_df = create_prediction_model(merged_data)
872
 
873
- # انتخاب مزرعه
874
  farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_ts")
875
 
876
- # فیلتر داده‌ها برای مزرعه انتخاب شده
877
  farm_time_data = time_df[time_df['farm_id'] == farm_name]
878
 
879
- # نمایش نمودار سری زمانی
880
  fig_farm_ts = px.line(
881
  farm_time_data,
882
  x='date',
@@ -894,19 +810,16 @@ with tabs[3]:
894
 
895
  st.plotly_chart(fig_farm_ts, use_container_width=True)
896
 
897
- # تحلیل روند
898
  st.subheader("تحلیل روند")
899
 
900
  col1, col2 = st.columns(2)
901
 
902
  with col1:
903
- # محاسبه میانگین متحرک
904
  window_size = st.slider("اندازه پنجره میانگین متحرک", min_value=3, max_value=30, value=7)
905
 
906
  farm_time_data['ndvi_ma'] = farm_time_data['ndvi'].rolling(window=window_size).mean()
907
  farm_time_data['height_ma'] = farm_time_data['height'].rolling(window=window_size).mean()
908
 
909
- # نمایش نمودار میانگین متحرک
910
  fig_ma = px.line(
911
  farm_time_data.dropna(),
912
  x='date',
@@ -925,7 +838,6 @@ with tabs[3]:
925
  st.plotly_chart(fig_ma, use_container_width=True)
926
 
927
  with col2:
928
- # تحلیل فصلی
929
  monthly_data = farm_time_data.groupby('month').agg({
930
  'ndvi': 'mean',
931
  'height': 'mean'
@@ -952,18 +864,14 @@ with tabs[3]:
952
  with analysis_tabs[2]:
953
  st.subheader("تشخیص تنش‌ها")
954
 
955
- # انتخاب مزرعه
956
  farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_stress")
957
 
958
- # ایجاد داده‌های نمونه برای تنش‌ها
959
  dates = pd.date_range(end=datetime.now(), periods=90)
960
  stress_data = []
961
 
962
- # شبیه‌سازی تنش آبی
963
  water_stress_threshold = 0.3
964
  base_ndwi = np.random.uniform(0.2, 0.5)
965
 
966
- # شبیه‌سازی تنش بیماری
967
  disease_threshold = 0.4
968
  base_chl = np.random.uniform(1.5, 2.5)
969
 
@@ -971,7 +879,6 @@ with tabs[3]:
971
  day_of_year = date.dayofyear
972
  seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
973
 
974
- # شبیه‌سازی تنش آبی در روزهای خاص
975
  water_stress_event = False
976
  if 30 <= day_of_year <= 40 or 70 <= day_of_year <= 75:
977
  water_stress_event = True
@@ -979,7 +886,6 @@ with tabs[3]:
979
  else:
980
  ndwi = base_ndwi + seasonal_factor + np.random.normal(0, 0.05)
981
 
982
- # شبیه‌سازی تنش بیماری در روزهای خاص
983
  disease_event = False
984
  if 50 <= day_of_year <= 60:
985
  disease_event = True
@@ -987,7 +893,6 @@ with tabs[3]:
987
  else:
988
  chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
989
 
990
- # محدود کردن مقادیر
991
  ndwi = max(-0.5, min(0.8, ndwi))
992
  chl = max(0.5, min(3.0, chl))
993
 
@@ -998,15 +903,13 @@ with tabs[3]:
998
  'water_stress': 1 if ndwi < water_stress_threshold else 0,
999
  'disease_stress': 1 if chl < disease_threshold else 0,
1000
  'water_stress_event': water_stress_event,
1001
- 'disease_stress_event': disease_stress_event
1002
  })
1003
 
1004
  stress_df = pd.DataFrame(stress_data)
1005
 
1006
- # نمایش نمودار تنش‌ها
1007
  fig_stress = go.Figure()
1008
 
1009
- # اضافه کردن NDWI
1010
  fig_stress.add_trace(go.Scatter(
1011
  x=stress_df['date'],
1012
  y=stress_df['ndwi'],
@@ -1014,7 +917,6 @@ with tabs[3]:
1014
  line=dict(color='blue')
1015
  ))
1016
 
1017
- # اضافه کردن CHL
1018
  fig_stress.add_trace(go.Scatter(
1019
  x=stress_df['date'],
1020
  y=stress_df['chl'],
@@ -1023,7 +925,6 @@ with tabs[3]:
1023
  yaxis='y2'
1024
  ))
1025
 
1026
- # اضافه کردن خط آستانه تنش آبی
1027
  fig_stress.add_shape(
1028
  type='line',
1029
  x0=stress_df['date'].min(),
@@ -1034,7 +935,6 @@ with tabs[3]:
1034
  name='آستانه تنش آبی'
1035
  )
1036
 
1037
- # اضافه کردن خط آستانه تنش بیماری
1038
  fig_stress.add_shape(
1039
  type='line',
1040
  x0=stress_df['date'].min(),
@@ -1046,7 +946,6 @@ with tabs[3]:
1046
  name='آستانه تنش بیماری'
1047
  )
1048
 
1049
- # تنظیمات نمودار
1050
  fig_stress.update_layout(
1051
  title='تشخیص تنش‌های آبی و بیماری',
1052
  font_family="Vazirmatn",
@@ -1077,13 +976,11 @@ with tabs[3]:
1077
 
1078
  st.plotly_chart(fig_stress, use_container_width=True)
1079
 
1080
- # نمایش هشدارها
1081
  st.subheader("هشدارهای تنش")
1082
 
1083
  col1, col2 = st.columns(2)
1084
 
1085
  with col1:
1086
- # هشدارهای تنش آبی
1087
  water_stress_days = stress_df[stress_df['water_stress'] == 1]
1088
 
1089
  if len(water_stress_days) > 0:
@@ -1101,7 +998,6 @@ with tabs[3]:
1101
  st.success("هیچ تنش آبی تشخیص داده نشده است.")
1102
 
1103
  with col2:
1104
- # هشدارهای تنش بیماری
1105
  disease_stress_days = stress_df[stress_df['disease_stress'] == 1]
1106
 
1107
  if len(disease_stress_days) > 0:
@@ -1121,13 +1017,10 @@ with tabs[3]:
1121
  with analysis_tabs[3]:
1122
  st.subheader("پیش‌بینی رشد")
1123
 
1124
- # انتخاب مزرعه
1125
  farm_name = st.selectbox("انتخاب مزرعه برای پیش‌بینی", merged_data['مزرعه'].tolist(), key="farm_select_predict")
1126
 
1127
- # فیلتر داده‌ها برای مزرعه انتخاب شده
1128
  farm_time_data = time_df[time_df['farm_id'] == farm_name]
1129
 
1130
- # تنظیمات پیش‌بینی
1131
  st.subheader("تنظیمات پ��ش‌بینی")
1132
 
1133
  col1, col2 = st.columns(2)
@@ -1140,7 +1033,6 @@ with tabs[3]:
1140
 
1141
  if st.button("انجام پیش‌بینی"):
1142
  with st.spinner("در حال انجام پیش‌بینی..."):
1143
- # ایجاد داده‌های آینده برای پیش‌بینی
1144
  last_date = farm_time_data['date'].max()
1145
  future_dates = pd.date_range(start=last_date + timedelta(days=1), periods=days_to_predict)
1146
 
@@ -1154,7 +1046,6 @@ with tabs[3]:
1154
 
1155
  future_df = pd.DataFrame(future_data)
1156
 
1157
- # پیش‌بینی NDVI برای روزهای آینده (با الگوی سینوسی + روند)
1158
  base_ndvi = farm_time_data['ndvi'].iloc[-1]
1159
  future_ndvi = []
1160
 
@@ -1162,38 +1053,28 @@ with tabs[3]:
1162
  seasonal_factor = np.sin(day / 365 * 2 * np.pi) * 0.2
1163
  trend_factor = day / 365 * 0.1
1164
  ndvi = base_ndvi + seasonal_factor + trend_factor
1165
- ndvi = max(0, min(1, ndvi)) # محدود کردن به بازه 0 تا 1
1166
  future_ndvi.append(ndvi)
1167
 
1168
  future_df['ndvi'] = future_ndvi
1169
 
1170
- # پیش‌بینی ارتفاع با استفاده از مدل
1171
  X_future = future_df[['ndvi', 'day_of_year', 'month']].values
1172
  future_df['height'] = model.predict(X_future)
1173
 
1174
- # محاسبه فاصله اطمینان
1175
- z_score = {
1176
- 90: 1.645,
1177
- 95: 1.96,
1178
- 99: 2.576
1179
- }.get(confidence_interval, 1.96)
1180
-
1181
  mse = mean_squared_error(y_test, y_pred)
1182
  std_dev = np.sqrt(mse)
1183
 
1184
  future_df['height_lower'] = future_df['height'] - z_score * std_dev
1185
  future_df['height_upper'] = future_df['height'] + z_score * std_dev
1186
 
1187
- # ترکیب داده‌های گذشته و آینده
1188
  combined_df = pd.concat([
1189
  farm_time_data[['date', 'ndvi', 'height']],
1190
  future_df[['date', 'ndvi', 'height', 'height_lower', 'height_upper']]
1191
  ])
1192
 
1193
- # نمایش نمودار پیش‌بینی
1194
  fig_predict = go.Figure()
1195
 
1196
- # داده‌های گذشته
1197
  past_data = combined_df[combined_df['date'] <= last_date]
1198
 
1199
  fig_predict.add_trace(go.Scatter(
@@ -1203,7 +1084,6 @@ with tabs[3]:
1203
  line=dict(color='blue')
1204
  ))
1205
 
1206
- # داده‌های پیش‌بینی شده
1207
  future_data = combined_df[combined_df['date'] > last_date]
1208
 
1209
  fig_predict.add_trace(go.Scatter(
@@ -1213,7 +1093,6 @@ with tabs[3]:
1213
  line=dict(color='red')
1214
  ))
1215
 
1216
- # فاصله اطمینان
1217
  fig_predict.add_trace(go.Scatter(
1218
  x=future_data['date'],
1219
  y=future_data['height_upper'],
@@ -1232,7 +1111,6 @@ with tabs[3]:
1232
  showlegend=True
1233
  ))
1234
 
1235
- # تنظیمات نمودار
1236
  fig_predict.update_layout(
1237
  title=f'پیش‌بینی ارتفاع برای {days_to_predict} روز آینده',
1238
  font_family="Vazirmatn",
@@ -1251,7 +1129,6 @@ with tabs[3]:
1251
 
1252
  st.plotly_chart(fig_predict, use_container_width=True)
1253
 
1254
- # نمایش جدول پیش‌بینی
1255
  st.subheader("جدول پیش‌بینی")
1256
 
1257
  st.dataframe(
@@ -1266,7 +1143,6 @@ with tabs[3]:
1266
  hide_index=True
1267
  )
1268
 
1269
- # نمایش خلاصه پیش‌بینی
1270
  st.subheader("خلاصه پیش‌بینی")
1271
 
1272
  col1, col2, col3 = st.columns(3)
@@ -1294,17 +1170,14 @@ with tabs[3]:
1294
  f"{final_height:.2f} متر"
1295
  )
1296
 
1297
- # تب گزارش‌گیری
1298
  with tabs[4]:
1299
  st.header("گزارش‌گیری")
1300
 
1301
- # انتخاب نوع گزارش
1302
  report_type = st.selectbox(
1303
  "انتخاب نوع گزارش",
1304
  ["گزارش وضعیت کلی مزارع", "گزارش تحلیل شاخص‌ها", "گزارش تنش‌ها", "گزارش پیش‌بینی رشد"]
1305
  )
1306
 
1307
- # انتخاب بازه زمانی
1308
  col1, col2 = st.columns(2)
1309
 
1310
  with col1:
@@ -1313,16 +1186,14 @@ with tabs[4]:
1313
  with col2:
1314
  end_date = st.date_input("تاریخ پایان", value=datetime.now())
1315
 
1316
- # انتخاب مزارع
1317
  selected_farms = st.multiselect(
1318
  "انتخاب مزارع",
1319
  options=merged_data['مزرعه'].tolist()
1320
  )
1321
 
1322
  if not selected_farms:
1323
- selected_farms = merged_data['مزرعه'].tolist() # اگر هیچ مزرعه‌ای انتخاب نشده باشد، همه مزارع انتخاب می‌شوند
1324
 
1325
- # فیلتر داده‌ها بر اساس مزارع انتخاب شده
1326
  filtered_farm_df = merged_data[merged_data['مزرعه'].isin(selected_farms)]
1327
 
1328
  if st.button("تولید گزارش"):
@@ -1330,7 +1201,6 @@ with tabs[4]:
1330
  if report_type == "گزارش وضعیت کلی مزارع":
1331
  st.subheader("گزارش وضعیت کلی مزارع")
1332
 
1333
- # نمودارهای تحلیلی
1334
  fig_variety, fig_age, fig_area = create_analysis_charts(filtered_farm_df)
1335
 
1336
  col1, col2 = st.columns(2)
@@ -1344,10 +1214,8 @@ with tabs[4]:
1344
  if fig_area:
1345
  st.plotly_chart(fig_area, use_container_width=True)
1346
 
1347
- # جدول اطلاعات مزارع
1348
  st.subheader("اطلاعات مزارع")
1349
 
1350
- # انتخاب ستون‌های مهم برای نمایش
1351
  if 'مساحت' in filtered_farm_df.columns:
1352
  display_columns = ['مزرعه', 'کانال', 'اداره', 'سن_x', 'واریته', 'مساحت', 'روز']
1353
  column_config = {
@@ -1375,7 +1243,6 @@ with tabs[4]:
1375
  hide_index=True
1376
  )
1377
 
1378
- # آمار خلاصه
1379
  st.subheader("آمار خلاصه")
1380
 
1381
  col1, col2, col3, col4 = st.columns(4)
@@ -1399,15 +1266,12 @@ with tabs[4]:
1399
  else:
1400
  st.metric("تعداد مزارع", len(filtered_farm_df))
1401
 
1402
- # نقشه مزارع
1403
  st.subheader("نقشه مزارع")
1404
  farm_map = create_farm_map(filtered_farm_df)
1405
  folium_static(farm_map, width=800, height=500)
1406
 
1407
- # دانلود گزارش
1408
  st.subheader("دانلود گزارش")
1409
 
1410
- # ایجاد فایل PDF
1411
  figs = [fig_variety, fig_age]
1412
  if fig_area:
1413
  figs.append(fig_area)
@@ -1416,7 +1280,6 @@ with tabs[4]:
1416
 
1417
  pdf_buffer = create_report_pdf(figs, tables, "گزارش وضعیت کلی مزارع")
1418
 
1419
- # دانلود PDF
1420
  st.download_button(
1421
  label="دانلود گزارش (PDF)",
1422
  data=pdf_buffer,
@@ -1424,7 +1287,6 @@ with tabs[4]:
1424
  mime="application/pdf"
1425
  )
1426
 
1427
- # دانلود Excel
1428
  excel_buffer = io.BytesIO()
1429
  filtered_farm_df.to_excel(excel_buffer, index=False)
1430
  excel_buffer.seek(0)
@@ -1439,11 +1301,9 @@ with tabs[4]:
1439
  elif report_type == "گزارش تحلیل شاخص‌ها":
1440
  st.subheader("گزارش تحلیل شاخص‌ها")
1441
 
1442
- # ایجاد داده‌های نمونه برای شاخص‌ها
1443
  indices_data = []
1444
 
1445
  for farm_name in selected_farms:
1446
- # مقادیر پایه برای هر مزرعه
1447
  base_ndvi = np.random.uniform(0.3, 0.8)
1448
  base_ndwi = np.random.uniform(0.1, 0.5)
1449
  base_evi = np.random.uniform(0.3, 0.7)
@@ -1451,11 +1311,9 @@ with tabs[4]:
1451
  base_lai = np.random.uniform(1.0, 4.0)
1452
  base_chl = np.random.uniform(1.0, 3.0)
1453
 
1454
- # ایجاد داده‌ها برای بازه زمانی انتخاب شده
1455
  date_range = pd.date_range(start=start_date, end=end_date)
1456
 
1457
  for date in date_range:
1458
- # شبیه‌سازی تغییرات با الگوی سینوسی
1459
  day_of_year = date.dayofyear
1460
  seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.1
1461
 
@@ -1466,7 +1324,6 @@ with tabs[4]:
1466
  lai = base_lai + seasonal_factor * 2 + np.random.normal(0, 0.2)
1467
  chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
1468
 
1469
- # محدود کردن مقادیر
1470
  ndvi = max(0, min(1, ndvi))
1471
  ndwi = max(-0.5, min(0.8, ndwi))
1472
  evi = max(0, min(1, evi))
@@ -1487,7 +1344,6 @@ with tabs[4]:
1487
 
1488
  indices_df = pd.DataFrame(indices_data)
1489
 
1490
- # نمایش میانگین شاخص‌ها برای هر مزرعه
1491
  avg_indices = indices_df.groupby('farm_name').agg({
1492
  'ndvi': 'mean',
1493
  'ndwi': 'mean',
@@ -1512,10 +1368,8 @@ with tabs[4]:
1512
  hide_index=True
1513
  )
1514
 
1515
- # نمودار مقایسه‌ای شاخص‌ها
1516
  st.subheader("مقایسه شاخص‌ها بین مزارع")
1517
 
1518
- # انتخاب شاخص برای نمایش
1519
  selected_index = st.selectbox(
1520
  "انتخاب شاخص",
1521
  ["NDVI", "NDWI", "EVI", "NDMI", "LAI", "CHL"]
@@ -1530,7 +1384,6 @@ with tabs[4]:
1530
  "CHL": "chl"
1531
  }
1532
 
1533
- # نمودار مقایسه‌ای
1534
  fig_compare = px.bar(
1535
  avg_indices,
1536
  x='farm_name',
@@ -1549,19 +1402,15 @@ with tabs[4]:
1549
 
1550
  st.plotly_chart(fig_compare, use_container_width=True)
1551
 
1552
- # نمودار سری زمانی
1553
  st.subheader("سری زمانی شاخص‌ها")
1554
 
1555
- # انتخاب مزرعه برای نمایش سری زمانی
1556
  selected_farm_name = st.selectbox(
1557
  "انتخاب مزرعه",
1558
  options=selected_farms
1559
  )
1560
 
1561
- # فیلتر داده‌ها برای مزرعه انتخاب شده
1562
  farm_indices = indices_df[indices_df['farm_name'] == selected_farm_name]
1563
 
1564
- # نمودار سری زمانی
1565
  fig_ts = px.line(
1566
  farm_indices,
1567
  x='date',
@@ -1579,16 +1428,13 @@ with tabs[4]:
1579
 
1580
  st.plotly_chart(fig_ts, use_container_width=True)
1581
 
1582
- # دانلود گزارش
1583
  st.subheader("دانلود گزارش")
1584
 
1585
- # ایجاد فایل PDF
1586
  figs = [fig_compare, fig_ts]
1587
  tables = [avg_indices]
1588
 
1589
  pdf_buffer = create_report_pdf(figs, tables, "گزارش تحلیل شاخص‌ها")
1590
 
1591
- # دانلود PDF
1592
  st.download_button(
1593
  label="دانلود گزارش (PDF)",
1594
  data=pdf_buffer,
@@ -1596,7 +1442,6 @@ with tabs[4]:
1596
  mime="application/pdf"
1597
  )
1598
 
1599
- # دانلود Excel
1600
  excel_buffer = io.BytesIO()
1601
  indices_df.to_excel(excel_buffer, index=False)
1602
  excel_buffer.seek(0)
@@ -1611,42 +1456,35 @@ with tabs[4]:
1611
  elif report_type == "گزارش تنش‌ها":
1612
  st.subheader("گزارش تنش‌ها")
1613
 
1614
- # ایجاد داده‌های نمونه برای تنش‌ها
1615
  stress_data = []
1616
 
1617
  for farm_name in selected_farms:
1618
- # مقادیر پایه برای هر مزرعه
1619
  base_ndwi = np.random.uniform(0.2, 0.5)
1620
  base_chl = np.random.uniform(1.5, 2.5)
1621
 
1622
- # آستانه‌های تنش
1623
  water_stress_threshold = 0.3
1624
  disease_threshold = 0.4
1625
 
1626
- # ایجاد داده‌ها برای بازه زمانی انتخاب شده
1627
  date_range = pd.date_range(start=start_date, end=end_date)
1628
 
1629
  for date in date_range:
1630
  day_of_year = date.dayofyear
1631
  seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
1632
 
1633
- # شبیه‌سازی تنش آبی در روزهای خاص
1634
  water_stress_event = False
1635
- if (day_of_year % 30) < 5: # هر 30 روز، 5 روز تنش آبی
1636
  water_stress_event = True
1637
  ndwi = base_ndwi - 0.3 + np.random.normal(0, 0.05)
1638
  else:
1639
  ndwi = base_ndwi + seasonal_factor + np.random.normal(0, 0.05)
1640
 
1641
- # شبیه‌سازی تنش بیماری در روزهای خاص
1642
  disease_event = False
1643
- if (day_of_year % 45) < 7: # هر 45 روز، 7 روز تنش بیماری
1644
  disease_event = True
1645
  chl = base_chl - 0.5 + np.random.normal(0, 0.1)
1646
  else:
1647
  chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
1648
 
1649
- # محدود کردن مقادیر
1650
  ndwi = max(-0.5, min(0.8, ndwi))
1651
  chl = max(0.5, min(3.0, chl))
1652
 
@@ -1658,12 +1496,11 @@ with tabs[4]:
1658
  'water_stress': 1 if ndwi < water_stress_threshold else 0,
1659
  'disease_stress': 1 if chl < disease_threshold else 0,
1660
  'water_stress_event': water_stress_event,
1661
- 'disease_stress_event': disease_stress_event
1662
  })
1663
 
1664
  stress_df = pd.DataFrame(stress_data)
1665
 
1666
- # آمار تنش‌ها
1667
  stress_stats = stress_df.groupby('farm_name').agg({
1668
  'water_stress': 'sum',
1669
  'disease_stress': 'sum'
@@ -1677,7 +1514,6 @@ with tabs[4]:
1677
  st.subheader("آمار تنش‌ها برای هر مزرعه")
1678
  st.dataframe(stress_stats, hide_index=True)
1679
 
1680
- # نمودار مقایسه‌ای تنش‌ها
1681
  fig_stress_compare = px.bar(
1682
  stress_stats,
1683
  x='farm_name',
@@ -1695,23 +1531,18 @@ with tabs[4]:
1695
 
1696
  st.plotly_chart(fig_stress_compare, use_container_width=True)
1697
 
1698
- # نمودار سری زمانی تنش‌ها
1699
  st.subheader("سری زمانی تنش‌ها")
1700
 
1701
- # انتخاب مزرعه برای نمایش سری زمانی
1702
  selected_farm_name = st.selectbox(
1703
  "انتخاب مزرعه",
1704
  options=selected_farms,
1705
  key="farm_select_stress_report"
1706
  )
1707
 
1708
- # فیلتر داده‌ها برای مزرعه انتخاب شده
1709
  farm_stress = stress_df[stress_df['farm_name'] == selected_farm_name]
1710
 
1711
- # نمودار سری زمانی
1712
  fig_stress_ts = go.Figure()
1713
 
1714
- # اضافه کردن NDWI
1715
  fig_stress_ts.add_trace(go.Scatter(
1716
  x=farm_stress['date'],
1717
  y=farm_stress['ndwi'],
@@ -1719,7 +1550,6 @@ with tabs[4]:
1719
  line=dict(color='blue')
1720
  ))
1721
 
1722
- # اضافه کردن CHL
1723
  fig_stress_ts.add_trace(go.Scatter(
1724
  x=farm_stress['date'],
1725
  y=farm_stress['chl'],
@@ -1728,28 +1558,25 @@ with tabs[4]:
1728
  yaxis='y2'
1729
  ))
1730
 
1731
- # اضافه کردن خط آستانه تنش آبی
1732
  fig_stress_ts.add_shape(
1733
  type='line',
1734
  x0=farm_stress['date'].min(),
1735
- y0=0.3, # water_stress_threshold
1736
  x1=farm_stress['date'].max(),
1737
  y1=0.3,
1738
  line=dict(color='red', dash='dash')
1739
  )
1740
 
1741
- # اضافه کردن خط آستانه تنش بیماری
1742
  fig_stress_ts.add_shape(
1743
  type='line',
1744
  x0=farm_stress['date'].min(),
1745
- y0=0.4, # disease_threshold
1746
  x1=farm_stress['date'].max(),
1747
  y1=0.4,
1748
  line=dict(color='orange', dash='dash'),
1749
  yaxis='y2'
1750
  )
1751
 
1752
- # تنظیمات نمودار
1753
  fig_stress_ts.update_layout(
1754
  title=f'سری زمانی تنش‌ها برای {selected_farm_name}',
1755
  font_family="Vazirmatn",
@@ -1780,10 +1607,8 @@ with tabs[4]:
1780
 
1781
  st.plotly_chart(fig_stress_ts, use_container_width=True)
1782
 
1783
- # جدول روزهای تنش
1784
  st.subheader("روزهای تنش")
1785
 
1786
- # فیلتر روزهای تنش
1787
  stress_days = farm_stress[(farm_stress['water_stress'] == 1) | (farm_stress['disease_stress'] == 1)]
1788
  stress_days = stress_days[['date', 'ndwi', 'chl', 'water_stress', 'disease_stress']]
1789
 
@@ -1802,16 +1627,13 @@ with tabs[4]:
1802
  else:
1803
  st.info("هیچ روز تنشی برای این مزرعه در بازه زمانی انتخاب شده یافت نشد.")
1804
 
1805
- # دانلود گزارش
1806
  st.subheader("دانلود گزارش")
1807
 
1808
- # ایجاد فایل PDF
1809
  figs = [fig_stress_compare, fig_stress_ts]
1810
  tables = [stress_stats, stress_days]
1811
 
1812
  pdf_buffer = create_report_pdf(figs, tables, "گزارش تنش‌ها")
1813
 
1814
- # دانلود PDF
1815
  st.download_button(
1816
  label="دانلود گزارش (PDF)",
1817
  data=pdf_buffer,
@@ -1819,7 +1641,6 @@ with tabs[4]:
1819
  mime="application/pdf"
1820
  )
1821
 
1822
- # دانلود Excel
1823
  excel_buffer = io.BytesIO()
1824
  stress_df.to_excel(excel_buffer, index=False)
1825
  excel_buffer.seek(0)
@@ -1834,13 +1655,10 @@ with tabs[4]:
1834
  elif report_type == "گزارش پیش‌بینی رشد":
1835
  st.subheader("گزارش پیش‌بینی رشد")
1836
 
1837
- # ایجاد مدل و داده‌های پیش‌بینی
1838
  model, fig_model, fig_ts, time_df = create_prediction_model(merged_data)
1839
 
1840
- # فیلتر داده‌ها برای مزارع انتخاب شده
1841
  filtered_time_df = time_df[time_df['farm_id'].isin(selected_farms)]
1842
 
1843
- # آمار رشد
1844
  growth_stats = filtered_time_df.groupby('farm_id').agg({
1845
  'height': ['first', 'last', lambda x: x.iloc[-1] - x.iloc[0]]
1846
  })
@@ -1860,7 +1678,6 @@ with tabs[4]:
1860
  hide_index=True
1861
  )
1862
 
1863
- # نمودار مقایسه‌ای رشد
1864
  fig_growth = px.bar(
1865
  growth_stats,
1866
  x='farm_id',
@@ -1879,20 +1696,16 @@ with tabs[4]:
1879
 
1880
  st.plotly_chart(fig_growth, use_container_width=True)
1881
 
1882
- # نمودار سری زمانی رشد
1883
  st.subheader("سری زمانی رشد")
1884
 
1885
- # انتخاب مزرعه برای نمایش سری زمانی
1886
  selected_farm_name = st.selectbox(
1887
  "انتخاب مزرعه",
1888
  options=selected_farms,
1889
  key="farm_select_growth_report"
1890
  )
1891
 
1892
- # فیلتر داده‌ها برای مزرعه انتخاب شده
1893
  farm_growth = filtered_time_df[filtered_time_df['farm_id'] == selected_farm_name]
1894
 
1895
- # نمودار سری زمانی
1896
  fig_growth_ts = px.line(
1897
  farm_growth,
1898
  x='date',
@@ -1909,16 +1722,13 @@ with tabs[4]:
1909
 
1910
  st.plotly_chart(fig_growth_ts, use_container_width=True)
1911
 
1912
- # دانلود گزارش
1913
  st.subheader("دانلود گزارش")
1914
 
1915
- # ایجاد فایل PDF
1916
  figs = [fig_growth, fig_growth_ts]
1917
  tables = [growth_stats]
1918
 
1919
  pdf_buffer = create_report_pdf(figs, tables, "گزارش پیش‌بینی رشد")
1920
 
1921
- # دانلود PDF
1922
  st.download_button(
1923
  label="دانلود گزارش (PDF)",
1924
  data=pdf_buffer,
@@ -1926,7 +1736,6 @@ with tabs[4]:
1926
  mime="application/pdf"
1927
  )
1928
 
1929
- # دانلود Excel
1930
  excel_buffer = io.BytesIO()
1931
  filtered_time_df.to_excel(excel_buffer, index=False)
1932
  excel_buffer.seek(0)
@@ -1938,7 +1747,6 @@ with tabs[4]:
1938
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1939
  )
1940
 
1941
- # تب تنظیمات
1942
  with tabs[5]:
1943
  st.header("تنظیمات")
1944
 
@@ -1947,7 +1755,6 @@ with tabs[5]:
1947
  with settings_tabs[0]:
1948
  st.subheader("تنظیمات عمومی")
1949
 
1950
- # تنظیمات ظاهری
1951
  st.write("### تنظیمات ظاهری")
1952
 
1953
  col1, col2 = st.columns(2)
@@ -1974,7 +1781,6 @@ with tabs[5]:
1974
  ["Viridis (پیش‌فرض)", "Plasma", "Inferno", "Magma", "Cividis"]
1975
  )
1976
 
1977
- # تنظیمات زبان
1978
  st.write("### تنظیمات زبان")
1979
 
1980
  language = st.radio(
@@ -1982,7 +1788,6 @@ with tabs[5]:
1982
  ["فارسی (پیش‌فرض)", "انگلیسی"]
1983
  )
1984
 
1985
- # تنظیمات واحدها
1986
  st.write("### تنظیمات واحدها")
1987
 
1988
  col1, col2 = st.columns(2)
@@ -2009,14 +1814,12 @@ with tabs[5]:
2009
  ["1,234.56 (پیش‌فرض)", "1.234,56"]
2010
  )
2011
 
2012
- # دکمه ذخیره تنظیمات
2013
  if st.button("ذخیره تنظیمات"):
2014
  st.success("تنظیمات با موفقیت ذخیره شد.")
2015
 
2016
  with settings_tabs[1]:
2017
  st.subheader("اتصال به Google Earth Engine")
2018
 
2019
- # توضیحات
2020
  st.write("""
2021
  برای اتصال به Google Earth Engine، از حساب سرویس زیر استفاده می‌شود:
2022
 
@@ -2024,23 +1827,19 @@ with tabs[5]:
2024
  - پروژه: `ee-esmaeilkiani13877`
2025
  """)
2026
 
2027
- # نمایش وضعیت اتصال
2028
  ee_connected = initialize_earth_engine()
2029
 
2030
  if ee_connected:
2031
  st.success("اتصال به Google Earth Engine برقرار است.")
2032
 
2033
- # دکمه تست اتصال
2034
  if st.button("تست اتصال"):
2035
  with st.spinner("در حال تست اتصال..."):
2036
- # شبیه‌سازی تست اتصال
2037
  import time
2038
  time.sleep(2)
2039
  st.success("اتصال با موفقیت برقرار شد.")
2040
  else:
2041
  st.error("اتصال به Google Earth Engine برقرار نشد. لطفاً تنظیمات را بررسی کنید.")
2042
 
2043
- # تنظیمات پیشرفته
2044
  st.write("### تنظیمات پیشرفته")
2045
 
2046
  col1, col2 = st.columns(2)
@@ -2058,12 +1857,11 @@ with tabs[5]:
2058
  with settings_tabs[2]:
2059
  st.subheader("مدیریت کاربران")
2060
 
2061
- # ایجاد داده‌های نمونه برای کاربران
2062
  users_data = {
2063
  'user_id': list(range(1, 6)),
2064
  'username': ['admin', 'user1', 'user2', 'user3', 'user4'],
2065
  'name': ['مدیر سیستم', 'کاربر یک', 'کاربر دو', 'کاربر سه', 'کاربر چهار'],
2066
- 'role': ['مدیر', 'کارشناس', 'کاربر', 'کاربر'],
2067
  'last_login': [
2068
  (datetime.now() - timedelta(hours=2)).strftime('%Y-%m-%d %H:%M'),
2069
  (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d %H:%M'),
@@ -2075,7 +1873,6 @@ with tabs[5]:
2075
 
2076
  users_df = pd.DataFrame(users_data)
2077
 
2078
- # نمایش لیست کاربران
2079
  st.write("### لیست کاربران")
2080
 
2081
  st.dataframe(
@@ -2090,7 +1887,6 @@ with tabs[5]:
2090
  hide_index=True
2091
  )
2092
 
2093
- # افزودن کاربر جدید
2094
  st.write("### افزودن کاربر جدید")
2095
 
2096
  col1, col2 = st.columns(2)
@@ -2109,10 +1905,8 @@ with tabs[5]:
2109
  else:
2110
  st.error("لطفاً تمام فیلدها را پر کنید.")
2111
 
2112
- # ویرایش دسترسی‌ها
2113
  st.write("### مدیریت دسترسی‌ها")
2114
 
2115
- # جدول دسترسی‌ها
2116
  access_data = {
2117
  'role': ['مدیر', 'کارشناس', 'کاربر'],
2118
  'dashboard': ['خواندن/نوشتن', 'خواندن/نوشتن', 'خواندن'],
@@ -2139,11 +1933,9 @@ with tabs[5]:
2139
  hide_index=True
2140
  )
2141
 
2142
- # دکمه ویرایش دسترسی‌ها
2143
  if st.button("ویرایش دسترسی‌ها"):
2144
  st.info("برای ویرایش دسترسی‌ها، لطفاً با مدیر سیستم تماس بگیرید.")
2145
 
2146
- # نمایش اطلاعات نسخه
2147
  st.sidebar.markdown("---")
2148
  st.sidebar.info("نسخه 1.0.0")
2149
  st.sidebar.info("توسعه داده شده با Streamlit")
 
244
  </div>
245
  """, unsafe_allow_html=True)
246
 
 
 
 
 
 
 
 
 
 
247
  # تابع برای بارگذاری داده‌های مزارع نیشکر
248
  def load_farm_data():
249
  try:
 
253
  # بارگذاری فایل پایگاه داده مزارع
254
  farm_database = pd.read_csv('پایگاه داده (1).csv')
255
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  # ادغام داده‌ها بر اساس ستون مزرعه
257
  merged_data = pd.merge(farm_database, farm_coordinates, on='مزرعه', how='inner')
258
 
259
+ # تبدیل ستون‌های سن به نوع عددی
260
+ if 'سن_x' in merged_data.columns:
261
+ merged_data['سن_x'] = pd.to_numeric(merged_data['سن_x'], errors='coerce')
262
+ if 'سن' in merged_data.columns:
263
+ merged_data['سن'] = pd.to_numeric(merged_data['سن'], errors='coerce')
264
+
265
  return farm_coordinates, farm_database, merged_data
266
  except Exception as e:
267
  st.error(f"خطا در بارگذاری داده‌ها: {e}")
268
 
269
  # ایجاد داده‌های نمونه در صورت عدم وجود فایل‌ها
 
270
  farm_coordinates = pd.DataFrame({
271
  'مزرعه': [f'مزرعه {i}' for i in range(1, 21)],
272
  'سن': np.random.randint(1, 10, 20),
 
275
  'عرض جغرافیایی': np.random.uniform(31.0, 32.0, 20)
276
  })
277
 
 
278
  days = ['شنبه', 'یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه']
279
  farm_database = pd.DataFrame({
280
  'مزرعه': [f'مزرعه {i}' for i in range(1, 21)],
 
291
  # ادغام داده‌ها
292
  merged_data = pd.merge(farm_database, farm_coordinates, on='مزرعه', how='inner')
293
 
294
+ # تبدیل ستون‌های سن به نوع عددی
295
+ if 'سن_x' in merged_data.columns:
296
+ merged_data['سن_x'] = pd.to_numeric(merged_data['سن_x'], errors='coerce')
297
+ if 'سن' in merged_data.columns:
298
+ merged_data['سن'] = pd.to_numeric(merged_data['سن'], errors='coerce')
299
+
300
  return farm_coordinates, farm_database, merged_data
301
 
302
  # تابع برای ایجاد نقشه مزارع
303
  def create_farm_map(df, selected_farms=None):
 
304
  center_lat = df['عرض جغرافیایی'].mean()
305
  center_lon = df['طول جغرافیایی'].mean()
306
  m = folium.Map(location=[center_lat, center_lon], zoom_start=10)
307
 
 
308
  folium.TileLayer('openstreetmap').add_to(m)
309
  folium.TileLayer('Stamen Terrain').add_to(m)
310
  folium.TileLayer('Stamen Toner').add_to(m)
311
  folium.TileLayer('Stamen Watercolor').add_to(m)
312
  folium.TileLayer('CartoDB positron').add_to(m)
313
 
 
314
  folium.LayerControl().add_to(m)
315
 
 
316
  for idx, row in df.iterrows():
317
  color = 'green'
318
  if selected_farms is not None and row['مزرعه'] in selected_farms:
 
341
 
342
  # تابع برای ایجاد نمودارهای تحلیلی
343
  def create_analysis_charts(df):
 
344
  variety_column = 'واریته' if 'واریته' in df.columns else 'تنوع'
345
  fig_variety = px.pie(
346
  df,
 
354
  title_font_size=20
355
  )
356
 
 
357
  age_column = 'سن_x' if 'سن_x' in df.columns else 'سن'
358
  fig_age = px.histogram(
359
  df,
 
369
  title_font_size=20
370
  )
371
 
 
372
  if 'مساحت' in df.columns:
373
  fig_area = px.scatter(
374
  df,
 
393
 
394
  # تابع برای ایجاد مدل پیش‌بینی
395
  def create_prediction_model(df):
 
396
  dates = pd.date_range(end=datetime.now(), periods=90)
397
  farm_ids = df['مزرعه'].unique()
398
 
 
402
  base_height = np.random.uniform(0.5, 3.0)
403
 
404
  for date in dates:
 
405
  day_of_year = date.dayofyear
406
  seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
407
  trend_factor = date.dayofyear / 365 * 0.3
408
 
409
  ndvi = base_ndvi + seasonal_factor + trend_factor + np.random.normal(0, 0.05)
410
+ ndvi = max(0, min(1, ndvi))
411
 
412
  height = base_height + seasonal_factor * 2 + trend_factor + np.random.normal(0, 0.1)
413
+ height = max(0, height)
414
 
415
  time_series_data.append({
416
  'farm_id': farm_id,
 
423
 
424
  time_df = pd.DataFrame(time_series_data)
425
 
 
426
  X = time_df[['ndvi', 'day_of_year', 'month']].values
427
  y = time_df['height'].values
428
 
 
429
  X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
430
 
 
431
  model = RandomForestRegressor(n_estimators=100, random_state=42)
432
  model.fit(X_train, y_train)
433
 
 
434
  y_pred = model.predict(X_test)
435
  mse = mean_squared_error(y_test, y_pred)
436
 
 
437
  results_df = pd.DataFrame({
438
  'Actual': y_test,
439
  'Predicted': y_pred
 
462
  title_font_size=20
463
  )
464
 
465
+ farm_id = farm_ids[0]
 
466
  farm_data = time_df[time_df['farm_id'] == farm_id]
467
 
468
  fig_ts = px.line(
 
488
  # منوی اصلی
489
  st.title("🌱 سامانه مانیتورینگ مزارع نیشکر")
490
 
 
491
  tabs = st.tabs([
492
  "📊 داشبورد",
493
  "🗺️ نقشه مزارع",
 
497
  "⚙️ تنظیمات"
498
  ])
499
 
 
500
  with tabs[0]:
501
  st.header("داشبورد مدیریت مزارع نیشکر")
502
 
 
503
  col1, col2, col3, col4 = st.columns(4)
504
 
505
  with col1:
 
510
  display_metric_card("تعداد واریته‌ها", unique_varieties, "🌿")
511
 
512
  with col3:
513
+ avg_age = merged_data['سن_x'].mean() if 'سن_x' in merged_data.columns else merged_data['سن'].mean()
514
+ display_metric_card("میانگین سن", f"{avg_age:.1f} سال", "📅")
 
 
 
 
 
 
 
 
 
 
 
 
515
 
516
  with col4:
517
  if 'مساحت' in merged_data.columns:
518
+ total_area = merged_data['مساحت'].sum()
519
+ display_metric_card("مساحت کل", f"{total_area:.1f} هکتار", "📏")
 
 
 
 
 
 
 
 
 
 
520
  else:
521
  display_metric_card("تعداد کانال‌ها", len(merged_data['کانال'].unique()) if 'کانال' in merged_data.columns else "نامشخص", "🚿")
522
 
523
  st.markdown("---")
524
 
 
525
  st.subheader("تحلیل کلی مزارع")
526
 
527
  col1, col2 = st.columns(2)
 
535
  if fig_area:
536
  st.plotly_chart(fig_area, use_container_width=True)
537
 
 
538
  st.subheader("اطلاعات مزارع")
539
 
 
540
  if 'مساحت' in merged_data.columns:
541
  display_columns = ['مزرعه', 'کانال', 'اداره', 'سن_x', 'واریته', 'مساحت', 'روز']
542
  column_config = {
 
564
  hide_index=True
565
  )
566
 
 
567
  with tabs[1]:
568
  st.header("نقشه مزارع نیشکر")
569
 
 
572
  with col1:
573
  st.subheader("فیلتر مزارع")
574
 
 
575
  days = ['همه روزها', 'شنبه', 'یکشنبه', 'دوشنبه', 'سه‌شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه']
576
  selected_day = st.selectbox("انتخاب روز هفته", days)
577
 
 
578
  variety_column = 'واریته' if 'واریته' in merged_data.columns else 'تنوع'
579
  varieties = ['همه واریته‌ها'] + list(merged_data[variety_column].unique())
580
  selected_variety = st.selectbox("انتخاب واریته", varieties)
581
 
 
582
  age_column = 'سن_x' if 'سن_x' in merged_data.columns else 'سن'
583
  min_age, max_age = int(merged_data[age_column].min()), int(merged_data[age_column].max())
584
  age_range = st.slider(
 
588
  value=(min_age, max_age)
589
  )
590
 
 
591
  filtered_df = merged_data.copy()
592
 
593
  if selected_day != 'همه روزها' and 'روز' in filtered_df.columns:
 
601
  st.write(f"تعداد مزارع نمایش داده شده: {len(filtered_df)}")
602
 
603
  with col2:
 
604
  if len(filtered_df) > 0:
605
  farm_map = create_farm_map(filtered_df)
606
  folium_static(farm_map, width=800, height=500)
607
  else:
608
  st.warning("هیچ مزرعه‌ای با فیلترهای انتخاب شده یافت نشد.")
609
 
 
610
  with tabs[2]:
611
  st.header("ورود و آپلود اطلاعات")
612
 
 
618
  col1, col2 = st.columns(2)
619
 
620
  with col1:
 
621
  option = st.radio("انتخاب گزینه", ["ویرایش مزرعه موجود", "ایجاد مزرعه جدید"])
622
 
623
  if option == "ویرایش مزرعه موجود":
624
  farm_name = st.selectbox("انتخاب مزرعه", merged_data['مزرعه'].tolist())
625
  selected_farm = merged_data[merged_data['مزرعه'] == farm_name].iloc[0]
626
 
 
627
  if 'کانال' in merged_data.columns:
628
  canal = st.text_input("کانال", value=selected_farm['کانال'])
629
  office = st.text_input("اداره", value=selected_farm['اداره'])
 
642
  if st.button("بروزرسانی اطلاعات"):
643
  st.success("اطلاعات مزرعه با موفقیت بروزرسانی شد.")
644
  else:
 
645
  farm_name = st.text_input("نام مزرعه", value=f"مزرعه جدید")
646
 
 
647
  if 'کانال' in merged_data.columns:
648
  canal = st.text_input("کانال")
649
  office = st.text_input("اداره")
 
661
  st.success("مزرعه جدید با موفقیت ثبت شد.")
662
 
663
  with col2:
 
664
  st.subheader("انتخاب موقعیت روی نقشه")
665
  st.write("برای انتخاب موقعیت دقیق، روی نقشه کلیک کنید.")
666
 
 
667
  center_lat = merged_data['عرض جغرافیایی'].mean()
668
  center_lon = merged_data['طول جغرافیایی'].mean()
669
  m = folium.Map(location=[center_lat, center_lon], zoom_start=10)
 
722
  except Exception as e:
723
  st.error(f"خطا در خواندن فایل: {e}")
724
 
 
725
  with tabs[3]:
726
  st.header("تحلیل داده‌ها")
727
 
 
730
  with analysis_tabs[0]:
731
  st.subheader("محاسبه و نمایش شاخص‌های گیاهی")
732
 
 
733
  ee_connected = initialize_earth_engine()
734
 
735
  if ee_connected:
 
736
  farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_indices")
737
  selected_farm = merged_data[merged_data['مزرعه'] == farm_name].iloc[0]
738
 
 
739
  date = st.date_input("انتخاب تاریخ", value=datetime.now() - timedelta(days=7))
740
 
741
  if st.button("محاسبه شاخص‌ها"):
742
  with st.spinner("در حال محاسبه شاخص‌ها..."):
 
743
  point = ee.Geometry.Point([selected_farm['طول جغرافیایی'], selected_farm['عرض جغرافیایی']])
744
+ buffer = point.buffer(100)
745
 
 
746
  indices = calculate_indices(date.strftime('%Y-%m-%d'), buffer)
747
 
748
  if indices is not None:
 
749
  st.success("شاخص‌ها با موفقیت محاسبه شدند.")
750
 
 
751
  Map = geemap.Map()
752
  Map.centerObject(buffer, 14)
753
 
 
754
  vis_params = {
755
  'min': 0,
756
  'max': 1,
 
764
  Map.addLayer(indices.select('LAI'), {'min': 0, 'max': 5, 'palette': ['red', 'yellow', 'green']}, 'LAI')
765
  Map.addLayer(indices.select('CHL'), {'min': 0, 'max': 3, 'palette': ['red', 'yellow', 'green']}, 'CHL')
766
 
 
767
  Map.addLayerControl()
768
 
 
769
  Map.to_streamlit(height=500)
770
 
 
771
  col1, col2, col3 = st.columns(3)
772
 
773
  with col1:
 
787
  with analysis_tabs[1]:
788
  st.subheader("تحلیل سری زمانی")
789
 
 
790
  model, fig_model, fig_ts, time_df = create_prediction_model(merged_data)
791
 
 
792
  farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_ts")
793
 
 
794
  farm_time_data = time_df[time_df['farm_id'] == farm_name]
795
 
 
796
  fig_farm_ts = px.line(
797
  farm_time_data,
798
  x='date',
 
810
 
811
  st.plotly_chart(fig_farm_ts, use_container_width=True)
812
 
 
813
  st.subheader("تحلیل روند")
814
 
815
  col1, col2 = st.columns(2)
816
 
817
  with col1:
 
818
  window_size = st.slider("اندازه پنجره میانگین متحرک", min_value=3, max_value=30, value=7)
819
 
820
  farm_time_data['ndvi_ma'] = farm_time_data['ndvi'].rolling(window=window_size).mean()
821
  farm_time_data['height_ma'] = farm_time_data['height'].rolling(window=window_size).mean()
822
 
 
823
  fig_ma = px.line(
824
  farm_time_data.dropna(),
825
  x='date',
 
838
  st.plotly_chart(fig_ma, use_container_width=True)
839
 
840
  with col2:
 
841
  monthly_data = farm_time_data.groupby('month').agg({
842
  'ndvi': 'mean',
843
  'height': 'mean'
 
864
  with analysis_tabs[2]:
865
  st.subheader("تشخیص تنش‌ها")
866
 
 
867
  farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_stress")
868
 
 
869
  dates = pd.date_range(end=datetime.now(), periods=90)
870
  stress_data = []
871
 
 
872
  water_stress_threshold = 0.3
873
  base_ndwi = np.random.uniform(0.2, 0.5)
874
 
 
875
  disease_threshold = 0.4
876
  base_chl = np.random.uniform(1.5, 2.5)
877
 
 
879
  day_of_year = date.dayofyear
880
  seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
881
 
 
882
  water_stress_event = False
883
  if 30 <= day_of_year <= 40 or 70 <= day_of_year <= 75:
884
  water_stress_event = True
 
886
  else:
887
  ndwi = base_ndwi + seasonal_factor + np.random.normal(0, 0.05)
888
 
 
889
  disease_event = False
890
  if 50 <= day_of_year <= 60:
891
  disease_event = True
 
893
  else:
894
  chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
895
 
 
896
  ndwi = max(-0.5, min(0.8, ndwi))
897
  chl = max(0.5, min(3.0, chl))
898
 
 
903
  'water_stress': 1 if ndwi < water_stress_threshold else 0,
904
  'disease_stress': 1 if chl < disease_threshold else 0,
905
  'water_stress_event': water_stress_event,
906
+ 'disease_stress_event': disease_event
907
  })
908
 
909
  stress_df = pd.DataFrame(stress_data)
910
 
 
911
  fig_stress = go.Figure()
912
 
 
913
  fig_stress.add_trace(go.Scatter(
914
  x=stress_df['date'],
915
  y=stress_df['ndwi'],
 
917
  line=dict(color='blue')
918
  ))
919
 
 
920
  fig_stress.add_trace(go.Scatter(
921
  x=stress_df['date'],
922
  y=stress_df['chl'],
 
925
  yaxis='y2'
926
  ))
927
 
 
928
  fig_stress.add_shape(
929
  type='line',
930
  x0=stress_df['date'].min(),
 
935
  name='آستانه تنش آبی'
936
  )
937
 
 
938
  fig_stress.add_shape(
939
  type='line',
940
  x0=stress_df['date'].min(),
 
946
  name='آستانه تنش بیماری'
947
  )
948
 
 
949
  fig_stress.update_layout(
950
  title='تشخیص تنش‌های آبی و بیماری',
951
  font_family="Vazirmatn",
 
976
 
977
  st.plotly_chart(fig_stress, use_container_width=True)
978
 
 
979
  st.subheader("هشدارهای تنش")
980
 
981
  col1, col2 = st.columns(2)
982
 
983
  with col1:
 
984
  water_stress_days = stress_df[stress_df['water_stress'] == 1]
985
 
986
  if len(water_stress_days) > 0:
 
998
  st.success("هیچ تنش آبی تشخیص داده نشده است.")
999
 
1000
  with col2:
 
1001
  disease_stress_days = stress_df[stress_df['disease_stress'] == 1]
1002
 
1003
  if len(disease_stress_days) > 0:
 
1017
  with analysis_tabs[3]:
1018
  st.subheader("پیش‌بینی رشد")
1019
 
 
1020
  farm_name = st.selectbox("انتخاب مزرعه برای پیش‌بینی", merged_data['مزرعه'].tolist(), key="farm_select_predict")
1021
 
 
1022
  farm_time_data = time_df[time_df['farm_id'] == farm_name]
1023
 
 
1024
  st.subheader("تنظیمات پ��ش‌بینی")
1025
 
1026
  col1, col2 = st.columns(2)
 
1033
 
1034
  if st.button("انجام پیش‌بینی"):
1035
  with st.spinner("در حال انجام پیش‌بینی..."):
 
1036
  last_date = farm_time_data['date'].max()
1037
  future_dates = pd.date_range(start=last_date + timedelta(days=1), periods=days_to_predict)
1038
 
 
1046
 
1047
  future_df = pd.DataFrame(future_data)
1048
 
 
1049
  base_ndvi = farm_time_data['ndvi'].iloc[-1]
1050
  future_ndvi = []
1051
 
 
1053
  seasonal_factor = np.sin(day / 365 * 2 * np.pi) * 0.2
1054
  trend_factor = day / 365 * 0.1
1055
  ndvi = base_ndvi + seasonal_factor + trend_factor
1056
+ ndvi = max(0, min(1, ndvi))
1057
  future_ndvi.append(ndvi)
1058
 
1059
  future_df['ndvi'] = future_ndvi
1060
 
 
1061
  X_future = future_df[['ndvi', 'day_of_year', 'month']].values
1062
  future_df['height'] = model.predict(X_future)
1063
 
1064
+ z_score = {90: 1.645, 95: 1.96, 99: 2.576}.get(confidence_interval, 1.96)
 
 
 
 
 
 
1065
  mse = mean_squared_error(y_test, y_pred)
1066
  std_dev = np.sqrt(mse)
1067
 
1068
  future_df['height_lower'] = future_df['height'] - z_score * std_dev
1069
  future_df['height_upper'] = future_df['height'] + z_score * std_dev
1070
 
 
1071
  combined_df = pd.concat([
1072
  farm_time_data[['date', 'ndvi', 'height']],
1073
  future_df[['date', 'ndvi', 'height', 'height_lower', 'height_upper']]
1074
  ])
1075
 
 
1076
  fig_predict = go.Figure()
1077
 
 
1078
  past_data = combined_df[combined_df['date'] <= last_date]
1079
 
1080
  fig_predict.add_trace(go.Scatter(
 
1084
  line=dict(color='blue')
1085
  ))
1086
 
 
1087
  future_data = combined_df[combined_df['date'] > last_date]
1088
 
1089
  fig_predict.add_trace(go.Scatter(
 
1093
  line=dict(color='red')
1094
  ))
1095
 
 
1096
  fig_predict.add_trace(go.Scatter(
1097
  x=future_data['date'],
1098
  y=future_data['height_upper'],
 
1111
  showlegend=True
1112
  ))
1113
 
 
1114
  fig_predict.update_layout(
1115
  title=f'پیش‌بینی ارتفاع برای {days_to_predict} روز آینده',
1116
  font_family="Vazirmatn",
 
1129
 
1130
  st.plotly_chart(fig_predict, use_container_width=True)
1131
 
 
1132
  st.subheader("جدول پیش‌بینی")
1133
 
1134
  st.dataframe(
 
1143
  hide_index=True
1144
  )
1145
 
 
1146
  st.subheader("خلاصه پیش‌بینی")
1147
 
1148
  col1, col2, col3 = st.columns(3)
 
1170
  f"{final_height:.2f} متر"
1171
  )
1172
 
 
1173
  with tabs[4]:
1174
  st.header("گزارش‌گیری")
1175
 
 
1176
  report_type = st.selectbox(
1177
  "انتخاب نوع گزارش",
1178
  ["گزارش وضعیت کلی مزارع", "گزارش تحلیل شاخص‌ها", "گزارش تنش‌ها", "گزارش پیش‌بینی رشد"]
1179
  )
1180
 
 
1181
  col1, col2 = st.columns(2)
1182
 
1183
  with col1:
 
1186
  with col2:
1187
  end_date = st.date_input("تاریخ پایان", value=datetime.now())
1188
 
 
1189
  selected_farms = st.multiselect(
1190
  "انتخاب مزارع",
1191
  options=merged_data['مزرعه'].tolist()
1192
  )
1193
 
1194
  if not selected_farms:
1195
+ selected_farms = merged_data['مزرعه'].tolist()
1196
 
 
1197
  filtered_farm_df = merged_data[merged_data['مزرعه'].isin(selected_farms)]
1198
 
1199
  if st.button("تولید گزارش"):
 
1201
  if report_type == "گزارش وضعیت کلی مزارع":
1202
  st.subheader("گزارش وضعیت کلی مزارع")
1203
 
 
1204
  fig_variety, fig_age, fig_area = create_analysis_charts(filtered_farm_df)
1205
 
1206
  col1, col2 = st.columns(2)
 
1214
  if fig_area:
1215
  st.plotly_chart(fig_area, use_container_width=True)
1216
 
 
1217
  st.subheader("اطلاعات مزارع")
1218
 
 
1219
  if 'مساحت' in filtered_farm_df.columns:
1220
  display_columns = ['مزرعه', 'کانال', 'اداره', 'سن_x', 'واریته', 'مساحت', 'روز']
1221
  column_config = {
 
1243
  hide_index=True
1244
  )
1245
 
 
1246
  st.subheader("آمار خلاصه")
1247
 
1248
  col1, col2, col3, col4 = st.columns(4)
 
1266
  else:
1267
  st.metric("تعداد مزارع", len(filtered_farm_df))
1268
 
 
1269
  st.subheader("نقشه مزارع")
1270
  farm_map = create_farm_map(filtered_farm_df)
1271
  folium_static(farm_map, width=800, height=500)
1272
 
 
1273
  st.subheader("دانلود گزارش")
1274
 
 
1275
  figs = [fig_variety, fig_age]
1276
  if fig_area:
1277
  figs.append(fig_area)
 
1280
 
1281
  pdf_buffer = create_report_pdf(figs, tables, "گزارش وضعیت کلی مزارع")
1282
 
 
1283
  st.download_button(
1284
  label="دانلود گزارش (PDF)",
1285
  data=pdf_buffer,
 
1287
  mime="application/pdf"
1288
  )
1289
 
 
1290
  excel_buffer = io.BytesIO()
1291
  filtered_farm_df.to_excel(excel_buffer, index=False)
1292
  excel_buffer.seek(0)
 
1301
  elif report_type == "گزارش تحلیل شاخص‌ها":
1302
  st.subheader("گزارش تحلیل شاخص‌ها")
1303
 
 
1304
  indices_data = []
1305
 
1306
  for farm_name in selected_farms:
 
1307
  base_ndvi = np.random.uniform(0.3, 0.8)
1308
  base_ndwi = np.random.uniform(0.1, 0.5)
1309
  base_evi = np.random.uniform(0.3, 0.7)
 
1311
  base_lai = np.random.uniform(1.0, 4.0)
1312
  base_chl = np.random.uniform(1.0, 3.0)
1313
 
 
1314
  date_range = pd.date_range(start=start_date, end=end_date)
1315
 
1316
  for date in date_range:
 
1317
  day_of_year = date.dayofyear
1318
  seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.1
1319
 
 
1324
  lai = base_lai + seasonal_factor * 2 + np.random.normal(0, 0.2)
1325
  chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
1326
 
 
1327
  ndvi = max(0, min(1, ndvi))
1328
  ndwi = max(-0.5, min(0.8, ndwi))
1329
  evi = max(0, min(1, evi))
 
1344
 
1345
  indices_df = pd.DataFrame(indices_data)
1346
 
 
1347
  avg_indices = indices_df.groupby('farm_name').agg({
1348
  'ndvi': 'mean',
1349
  'ndwi': 'mean',
 
1368
  hide_index=True
1369
  )
1370
 
 
1371
  st.subheader("مقایسه شاخص‌ها بین مزارع")
1372
 
 
1373
  selected_index = st.selectbox(
1374
  "انتخاب شاخص",
1375
  ["NDVI", "NDWI", "EVI", "NDMI", "LAI", "CHL"]
 
1384
  "CHL": "chl"
1385
  }
1386
 
 
1387
  fig_compare = px.bar(
1388
  avg_indices,
1389
  x='farm_name',
 
1402
 
1403
  st.plotly_chart(fig_compare, use_container_width=True)
1404
 
 
1405
  st.subheader("سری زمانی شاخص‌ها")
1406
 
 
1407
  selected_farm_name = st.selectbox(
1408
  "انتخاب مزرعه",
1409
  options=selected_farms
1410
  )
1411
 
 
1412
  farm_indices = indices_df[indices_df['farm_name'] == selected_farm_name]
1413
 
 
1414
  fig_ts = px.line(
1415
  farm_indices,
1416
  x='date',
 
1428
 
1429
  st.plotly_chart(fig_ts, use_container_width=True)
1430
 
 
1431
  st.subheader("دانلود گزارش")
1432
 
 
1433
  figs = [fig_compare, fig_ts]
1434
  tables = [avg_indices]
1435
 
1436
  pdf_buffer = create_report_pdf(figs, tables, "گزارش تحلیل شاخص‌ها")
1437
 
 
1438
  st.download_button(
1439
  label="دانلود گزارش (PDF)",
1440
  data=pdf_buffer,
 
1442
  mime="application/pdf"
1443
  )
1444
 
 
1445
  excel_buffer = io.BytesIO()
1446
  indices_df.to_excel(excel_buffer, index=False)
1447
  excel_buffer.seek(0)
 
1456
  elif report_type == "گزارش تنش‌ها":
1457
  st.subheader("گزارش تنش‌ها")
1458
 
 
1459
  stress_data = []
1460
 
1461
  for farm_name in selected_farms:
 
1462
  base_ndwi = np.random.uniform(0.2, 0.5)
1463
  base_chl = np.random.uniform(1.5, 2.5)
1464
 
 
1465
  water_stress_threshold = 0.3
1466
  disease_threshold = 0.4
1467
 
 
1468
  date_range = pd.date_range(start=start_date, end=end_date)
1469
 
1470
  for date in date_range:
1471
  day_of_year = date.dayofyear
1472
  seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
1473
 
 
1474
  water_stress_event = False
1475
+ if (day_of_year % 30) < 5:
1476
  water_stress_event = True
1477
  ndwi = base_ndwi - 0.3 + np.random.normal(0, 0.05)
1478
  else:
1479
  ndwi = base_ndwi + seasonal_factor + np.random.normal(0, 0.05)
1480
 
 
1481
  disease_event = False
1482
+ if (day_of_year % 45) < 7:
1483
  disease_event = True
1484
  chl = base_chl - 0.5 + np.random.normal(0, 0.1)
1485
  else:
1486
  chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
1487
 
 
1488
  ndwi = max(-0.5, min(0.8, ndwi))
1489
  chl = max(0.5, min(3.0, chl))
1490
 
 
1496
  'water_stress': 1 if ndwi < water_stress_threshold else 0,
1497
  'disease_stress': 1 if chl < disease_threshold else 0,
1498
  'water_stress_event': water_stress_event,
1499
+ 'disease_stress_event': disease_event
1500
  })
1501
 
1502
  stress_df = pd.DataFrame(stress_data)
1503
 
 
1504
  stress_stats = stress_df.groupby('farm_name').agg({
1505
  'water_stress': 'sum',
1506
  'disease_stress': 'sum'
 
1514
  st.subheader("آمار تنش‌ها برای هر مزرعه")
1515
  st.dataframe(stress_stats, hide_index=True)
1516
 
 
1517
  fig_stress_compare = px.bar(
1518
  stress_stats,
1519
  x='farm_name',
 
1531
 
1532
  st.plotly_chart(fig_stress_compare, use_container_width=True)
1533
 
 
1534
  st.subheader("سری زمانی تنش‌ها")
1535
 
 
1536
  selected_farm_name = st.selectbox(
1537
  "انتخاب مزرعه",
1538
  options=selected_farms,
1539
  key="farm_select_stress_report"
1540
  )
1541
 
 
1542
  farm_stress = stress_df[stress_df['farm_name'] == selected_farm_name]
1543
 
 
1544
  fig_stress_ts = go.Figure()
1545
 
 
1546
  fig_stress_ts.add_trace(go.Scatter(
1547
  x=farm_stress['date'],
1548
  y=farm_stress['ndwi'],
 
1550
  line=dict(color='blue')
1551
  ))
1552
 
 
1553
  fig_stress_ts.add_trace(go.Scatter(
1554
  x=farm_stress['date'],
1555
  y=farm_stress['chl'],
 
1558
  yaxis='y2'
1559
  ))
1560
 
 
1561
  fig_stress_ts.add_shape(
1562
  type='line',
1563
  x0=farm_stress['date'].min(),
1564
+ y0=0.3,
1565
  x1=farm_stress['date'].max(),
1566
  y1=0.3,
1567
  line=dict(color='red', dash='dash')
1568
  )
1569
 
 
1570
  fig_stress_ts.add_shape(
1571
  type='line',
1572
  x0=farm_stress['date'].min(),
1573
+ y0=0.4,
1574
  x1=farm_stress['date'].max(),
1575
  y1=0.4,
1576
  line=dict(color='orange', dash='dash'),
1577
  yaxis='y2'
1578
  )
1579
 
 
1580
  fig_stress_ts.update_layout(
1581
  title=f'سری زمانی تنش‌ها برای {selected_farm_name}',
1582
  font_family="Vazirmatn",
 
1607
 
1608
  st.plotly_chart(fig_stress_ts, use_container_width=True)
1609
 
 
1610
  st.subheader("روزهای تنش")
1611
 
 
1612
  stress_days = farm_stress[(farm_stress['water_stress'] == 1) | (farm_stress['disease_stress'] == 1)]
1613
  stress_days = stress_days[['date', 'ndwi', 'chl', 'water_stress', 'disease_stress']]
1614
 
 
1627
  else:
1628
  st.info("هیچ روز تنشی برای این مزرعه در بازه زمانی انتخاب شده یافت نشد.")
1629
 
 
1630
  st.subheader("دانلود گزارش")
1631
 
 
1632
  figs = [fig_stress_compare, fig_stress_ts]
1633
  tables = [stress_stats, stress_days]
1634
 
1635
  pdf_buffer = create_report_pdf(figs, tables, "گزارش تنش‌ها")
1636
 
 
1637
  st.download_button(
1638
  label="دانلود گزارش (PDF)",
1639
  data=pdf_buffer,
 
1641
  mime="application/pdf"
1642
  )
1643
 
 
1644
  excel_buffer = io.BytesIO()
1645
  stress_df.to_excel(excel_buffer, index=False)
1646
  excel_buffer.seek(0)
 
1655
  elif report_type == "گزارش پیش‌بینی رشد":
1656
  st.subheader("گزارش پیش‌بینی رشد")
1657
 
 
1658
  model, fig_model, fig_ts, time_df = create_prediction_model(merged_data)
1659
 
 
1660
  filtered_time_df = time_df[time_df['farm_id'].isin(selected_farms)]
1661
 
 
1662
  growth_stats = filtered_time_df.groupby('farm_id').agg({
1663
  'height': ['first', 'last', lambda x: x.iloc[-1] - x.iloc[0]]
1664
  })
 
1678
  hide_index=True
1679
  )
1680
 
 
1681
  fig_growth = px.bar(
1682
  growth_stats,
1683
  x='farm_id',
 
1696
 
1697
  st.plotly_chart(fig_growth, use_container_width=True)
1698
 
 
1699
  st.subheader("سری زمانی رشد")
1700
 
 
1701
  selected_farm_name = st.selectbox(
1702
  "انتخاب مزرعه",
1703
  options=selected_farms,
1704
  key="farm_select_growth_report"
1705
  )
1706
 
 
1707
  farm_growth = filtered_time_df[filtered_time_df['farm_id'] == selected_farm_name]
1708
 
 
1709
  fig_growth_ts = px.line(
1710
  farm_growth,
1711
  x='date',
 
1722
 
1723
  st.plotly_chart(fig_growth_ts, use_container_width=True)
1724
 
 
1725
  st.subheader("دانلود گزارش")
1726
 
 
1727
  figs = [fig_growth, fig_growth_ts]
1728
  tables = [growth_stats]
1729
 
1730
  pdf_buffer = create_report_pdf(figs, tables, "گزارش پیش‌بینی رشد")
1731
 
 
1732
  st.download_button(
1733
  label="دانلود گزارش (PDF)",
1734
  data=pdf_buffer,
 
1736
  mime="application/pdf"
1737
  )
1738
 
 
1739
  excel_buffer = io.BytesIO()
1740
  filtered_time_df.to_excel(excel_buffer, index=False)
1741
  excel_buffer.seek(0)
 
1747
  mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
1748
  )
1749
 
 
1750
  with tabs[5]:
1751
  st.header("تنظیمات")
1752
 
 
1755
  with settings_tabs[0]:
1756
  st.subheader("تنظیمات عمومی")
1757
 
 
1758
  st.write("### تنظیمات ظاهری")
1759
 
1760
  col1, col2 = st.columns(2)
 
1781
  ["Viridis (پیش‌فرض)", "Plasma", "Inferno", "Magma", "Cividis"]
1782
  )
1783
 
 
1784
  st.write("### تنظیمات زبان")
1785
 
1786
  language = st.radio(
 
1788
  ["فارسی (پیش‌فرض)", "انگلیسی"]
1789
  )
1790
 
 
1791
  st.write("### تنظیمات واحدها")
1792
 
1793
  col1, col2 = st.columns(2)
 
1814
  ["1,234.56 (پیش‌فرض)", "1.234,56"]
1815
  )
1816
 
 
1817
  if st.button("ذخیره تنظیمات"):
1818
  st.success("تنظیمات با موفقیت ذخیره شد.")
1819
 
1820
  with settings_tabs[1]:
1821
  st.subheader("اتصال به Google Earth Engine")
1822
 
 
1823
  st.write("""
1824
  برای اتصال به Google Earth Engine، از حساب سرویس زیر استفاده می‌شود:
1825
 
 
1827
  - پروژه: `ee-esmaeilkiani13877`
1828
  """)
1829
 
 
1830
  ee_connected = initialize_earth_engine()
1831
 
1832
  if ee_connected:
1833
  st.success("اتصال به Google Earth Engine برقرار است.")
1834
 
 
1835
  if st.button("تست اتصال"):
1836
  with st.spinner("در حال تست اتصال..."):
 
1837
  import time
1838
  time.sleep(2)
1839
  st.success("اتصال با موفقیت برقرار شد.")
1840
  else:
1841
  st.error("اتصال به Google Earth Engine برقرار نشد. لطفاً تنظیمات را بررسی کنید.")
1842
 
 
1843
  st.write("### تنظیمات پیشرفته")
1844
 
1845
  col1, col2 = st.columns(2)
 
1857
  with settings_tabs[2]:
1858
  st.subheader("مدیریت کاربران")
1859
 
 
1860
  users_data = {
1861
  'user_id': list(range(1, 6)),
1862
  'username': ['admin', 'user1', 'user2', 'user3', 'user4'],
1863
  'name': ['مدیر سیستم', 'کاربر یک', 'کاربر دو', 'کاربر سه', 'کاربر چهار'],
1864
+ 'role': ['مدیر', 'کارشناس', 'کارشناس', 'کاربر', 'کاربر'],
1865
  'last_login': [
1866
  (datetime.now() - timedelta(hours=2)).strftime('%Y-%m-%d %H:%M'),
1867
  (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d %H:%M'),
 
1873
 
1874
  users_df = pd.DataFrame(users_data)
1875
 
 
1876
  st.write("### لیست کاربران")
1877
 
1878
  st.dataframe(
 
1887
  hide_index=True
1888
  )
1889
 
 
1890
  st.write("### افزودن کاربر جدید")
1891
 
1892
  col1, col2 = st.columns(2)
 
1905
  else:
1906
  st.error("لطفاً تمام فیلدها را پر کنید.")
1907
 
 
1908
  st.write("### مدیریت دسترسی‌ها")
1909
 
 
1910
  access_data = {
1911
  'role': ['مدیر', 'کارشناس', 'کاربر'],
1912
  'dashboard': ['خواندن/نوشتن', 'خواندن/نوشتن', 'خواندن'],
 
1933
  hide_index=True
1934
  )
1935
 
 
1936
  if st.button("ویرایش دسترسی‌ها"):
1937
  st.info("برای ویرایش دسترسی‌ها، لطفاً با مدیر سیستم تماس بگیرید.")
1938
 
 
1939
  st.sidebar.markdown("---")
1940
  st.sidebar.info("نسخه 1.0.0")
1941
  st.sidebar.info("توسعه داده شده با Streamlit")