Spaces:
Configuration error
Configuration error
Update app.py
Browse files
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))
|
| 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 |
-
|
| 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 |
-
|
| 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)
|
| 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':
|
| 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))
|
| 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:
|
| 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:
|
| 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':
|
| 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,
|
| 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,
|
| 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")
|