Spaces:
Configuration error
Configuration error
Update app.py
Browse files
app.py
CHANGED
|
@@ -18,6 +18,7 @@ from sklearn.model_selection import train_test_split
|
|
| 18 |
from sklearn.metrics import mean_squared_error
|
| 19 |
import os
|
| 20 |
import json
|
|
|
|
| 21 |
|
| 22 |
# تنظیمات صفحه
|
| 23 |
st.set_page_config(
|
|
@@ -111,6 +112,30 @@ st.markdown("""
|
|
| 111 |
</style>
|
| 112 |
""", unsafe_allow_html=True)
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
# تابع برای اتصال به Google Earth Engine با استفاده از اطلاعات حساب سرویس
|
| 115 |
@st.cache_resource
|
| 116 |
def initialize_earth_engine():
|
|
@@ -244,29 +269,50 @@ def display_metric_card(title, value, icon="📊"):
|
|
| 244 |
</div>
|
| 245 |
""", unsafe_allow_html=True)
|
| 246 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
# تابع برای بارگذاری دادههای مزارع نیشکر
|
| 248 |
def load_farm_data():
|
| 249 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
# بارگذاری فایل مختصات مزارع
|
| 251 |
farm_coordinates = pd.read_csv('farm_coordinates.csv')
|
| 252 |
|
| 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,6 +321,7 @@ def load_farm_data():
|
|
| 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,28 +338,26 @@ def load_farm_data():
|
|
| 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,6 +386,7 @@ def create_farm_map(df, selected_farms=None):
|
|
| 341 |
|
| 342 |
# تابع برای ایجاد نمودارهای تحلیلی
|
| 343 |
def create_analysis_charts(df):
|
|
|
|
| 344 |
variety_column = 'واریته' if 'واریته' in df.columns else 'تنوع'
|
| 345 |
fig_variety = px.pie(
|
| 346 |
df,
|
|
@@ -354,6 +400,7 @@ def create_analysis_charts(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,23 +416,41 @@ def create_analysis_charts(df):
|
|
| 369 |
title_font_size=20
|
| 370 |
)
|
| 371 |
|
|
|
|
| 372 |
if 'مساحت' in df.columns:
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 389 |
else:
|
| 390 |
fig_area = None
|
| 391 |
|
|
@@ -393,6 +458,7 @@ def create_analysis_charts(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,15 +468,16 @@ def create_prediction_model(df):
|
|
| 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,17 +490,22 @@ def create_prediction_model(df):
|
|
| 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,7 +534,8 @@ def create_prediction_model(df):
|
|
| 462 |
title_font_size=20
|
| 463 |
)
|
| 464 |
|
| 465 |
-
|
|
|
|
| 466 |
farm_data = time_df[time_df['farm_id'] == farm_id]
|
| 467 |
|
| 468 |
fig_ts = px.line(
|
|
@@ -480,7 +553,10 @@ def create_prediction_model(df):
|
|
| 480 |
yaxis_title='مقدار'
|
| 481 |
)
|
| 482 |
|
| 483 |
-
return model, fig, fig_ts, time_df
|
|
|
|
|
|
|
|
|
|
| 484 |
|
| 485 |
# بارگذاری دادههای مزارع نیشکر
|
| 486 |
farm_coordinates, farm_database, merged_data = load_farm_data()
|
|
@@ -488,6 +564,7 @@ farm_coordinates, farm_database, merged_data = load_farm_data()
|
|
| 488 |
# منوی اصلی
|
| 489 |
st.title("🌱 سامانه مانیتورینگ مزارع نیشکر")
|
| 490 |
|
|
|
|
| 491 |
tabs = st.tabs([
|
| 492 |
"📊 داشبورد",
|
| 493 |
"🗺️ نقشه مزارع",
|
|
@@ -497,9 +574,11 @@ tabs = st.tabs([
|
|
| 497 |
"⚙️ تنظیمات"
|
| 498 |
])
|
| 499 |
|
|
|
|
| 500 |
with tabs[0]:
|
| 501 |
st.header("داشبورد مدیریت مزارع نیشکر")
|
| 502 |
|
|
|
|
| 503 |
col1, col2, col3, col4 = st.columns(4)
|
| 504 |
|
| 505 |
with col1:
|
|
@@ -510,18 +589,41 @@ with tabs[0]:
|
|
| 510 |
display_metric_card("تعداد واریتهها", unique_varieties, "🌿")
|
| 511 |
|
| 512 |
with col3:
|
| 513 |
-
|
| 514 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
|
| 516 |
with col4:
|
| 517 |
if 'مساحت' in merged_data.columns:
|
| 518 |
-
|
| 519 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,8 +637,10 @@ with tabs[0]:
|
|
| 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,6 +668,7 @@ with tabs[0]:
|
|
| 564 |
hide_index=True
|
| 565 |
)
|
| 566 |
|
|
|
|
| 567 |
with tabs[1]:
|
| 568 |
st.header("نقشه مزارع نیشکر")
|
| 569 |
|
|
@@ -572,15 +677,19 @@ with tabs[1]:
|
|
| 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 |
-
|
|
|
|
| 584 |
age_range = st.slider(
|
| 585 |
"محدوده سن (سال)",
|
| 586 |
min_value=min_age,
|
|
@@ -588,6 +697,7 @@ with tabs[1]:
|
|
| 588 |
value=(min_age, max_age)
|
| 589 |
)
|
| 590 |
|
|
|
|
| 591 |
filtered_df = merged_data.copy()
|
| 592 |
|
| 593 |
if selected_day != 'همه روزها' and 'روز' in filtered_df.columns:
|
|
@@ -596,17 +706,21 @@ with tabs[1]:
|
|
| 596 |
if selected_variety != 'همه واریتهها':
|
| 597 |
filtered_df = filtered_df[filtered_df[variety_column] == selected_variety]
|
| 598 |
|
| 599 |
-
|
|
|
|
|
|
|
| 600 |
|
| 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,12 +732,14 @@ with tabs[2]:
|
|
| 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['اداره'])
|
|
@@ -634,7 +750,7 @@ with tabs[2]:
|
|
| 634 |
day = st.selectbox("روز", options=['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'], index=['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'].index(selected_farm['روز']))
|
| 635 |
|
| 636 |
age_column = 'سن_x' if 'سن_x' in selected_farm else 'سن'
|
| 637 |
-
age = st.
|
| 638 |
|
| 639 |
lat = st.number_input("عرض جغرافیایی", value=float(selected_farm['عرض جغرافیایی']), format="%.6f")
|
| 640 |
lon = st.number_input("طول جغرافیایی", value=float(selected_farm['طول جغرافیایی']), format="%.6f")
|
|
@@ -642,8 +758,10 @@ with tabs[2]:
|
|
| 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("اداره")
|
|
@@ -653,7 +771,7 @@ with tabs[2]:
|
|
| 653 |
group = st.text_input("گروه")
|
| 654 |
day = st.selectbox("روز", options=['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'])
|
| 655 |
|
| 656 |
-
age = st.
|
| 657 |
lat = st.number_input("عرض جغرافیایی", value=31.5, format="%.6f")
|
| 658 |
lon = st.number_input("طول جغرافیایی", value=48.5, format="%.6f")
|
| 659 |
|
|
@@ -661,9 +779,11 @@ with tabs[2]:
|
|
| 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,6 +842,7 @@ with tabs[2]:
|
|
| 722 |
except Exception as e:
|
| 723 |
st.error(f"خطا در خواندن فایل: {e}")
|
| 724 |
|
|
|
|
| 725 |
with tabs[3]:
|
| 726 |
st.header("تحلیل دادهها")
|
| 727 |
|
|
@@ -730,27 +851,35 @@ with tabs[3]:
|
|
| 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,10 +893,13 @@ with tabs[3]:
|
|
| 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,12 +919,16 @@ with tabs[3]:
|
|
| 787 |
with analysis_tabs[1]:
|
| 788 |
st.subheader("تحلیل سری زمانی")
|
| 789 |
|
| 790 |
-
|
|
|
|
| 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,16 +946,19 @@ with tabs[3]:
|
|
| 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,6 +977,7 @@ with tabs[3]:
|
|
| 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,14 +1004,18 @@ with tabs[3]:
|
|
| 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,6 +1023,7 @@ with tabs[3]:
|
|
| 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,6 +1031,7 @@ with tabs[3]:
|
|
| 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,6 +1039,7 @@ with tabs[3]:
|
|
| 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,13 +1050,15 @@ with tabs[3]:
|
|
| 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':
|
| 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,6 +1066,7 @@ with tabs[3]:
|
|
| 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,6 +1075,7 @@ with tabs[3]:
|
|
| 925 |
yaxis='y2'
|
| 926 |
))
|
| 927 |
|
|
|
|
| 928 |
fig_stress.add_shape(
|
| 929 |
type='line',
|
| 930 |
x0=stress_df['date'].min(),
|
|
@@ -935,6 +1086,7 @@ with tabs[3]:
|
|
| 935 |
name='آستانه تنش آبی'
|
| 936 |
)
|
| 937 |
|
|
|
|
| 938 |
fig_stress.add_shape(
|
| 939 |
type='line',
|
| 940 |
x0=stress_df['date'].min(),
|
|
@@ -946,6 +1098,7 @@ with tabs[3]:
|
|
| 946 |
name='آستانه تنش بیماری'
|
| 947 |
)
|
| 948 |
|
|
|
|
| 949 |
fig_stress.update_layout(
|
| 950 |
title='تشخیص تنشهای آبی و بیماری',
|
| 951 |
font_family="Vazirmatn",
|
|
@@ -976,11 +1129,13 @@ with tabs[3]:
|
|
| 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,6 +1153,7 @@ with tabs[3]:
|
|
| 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,10 +1173,13 @@ with tabs[3]:
|
|
| 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,6 +1192,7 @@ with tabs[3]:
|
|
| 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,6 +1206,7 @@ with tabs[3]:
|
|
| 1046 |
|
| 1047 |
future_df = pd.DataFrame(future_data)
|
| 1048 |
|
|
|
|
| 1049 |
base_ndvi = farm_time_data['ndvi'].iloc[-1]
|
| 1050 |
future_ndvi = []
|
| 1051 |
|
|
@@ -1053,28 +1214,38 @@ with tabs[3]:
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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,6 +1255,7 @@ with tabs[3]:
|
|
| 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,6 +1265,7 @@ with tabs[3]:
|
|
| 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,6 +1284,7 @@ with tabs[3]:
|
|
| 1111 |
showlegend=True
|
| 1112 |
))
|
| 1113 |
|
|
|
|
| 1114 |
fig_predict.update_layout(
|
| 1115 |
title=f'پیشبینی ارتفاع برای {days_to_predict} روز آینده',
|
| 1116 |
font_family="Vazirmatn",
|
|
@@ -1129,6 +1303,7 @@ with tabs[3]:
|
|
| 1129 |
|
| 1130 |
st.plotly_chart(fig_predict, use_container_width=True)
|
| 1131 |
|
|
|
|
| 1132 |
st.subheader("جدول پیشبینی")
|
| 1133 |
|
| 1134 |
st.dataframe(
|
|
@@ -1143,6 +1318,7 @@ with tabs[3]:
|
|
| 1143 |
hide_index=True
|
| 1144 |
)
|
| 1145 |
|
|
|
|
| 1146 |
st.subheader("خلاصه پیشبینی")
|
| 1147 |
|
| 1148 |
col1, col2, col3 = st.columns(3)
|
|
@@ -1170,14 +1346,17 @@ with tabs[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,14 +1365,16 @@ with tabs[4]:
|
|
| 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,6 +1382,7 @@ with tabs[4]:
|
|
| 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,8 +1396,10 @@ with tabs[4]:
|
|
| 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,6 +1427,7 @@ with tabs[4]:
|
|
| 1243 |
hide_index=True
|
| 1244 |
)
|
| 1245 |
|
|
|
|
| 1246 |
st.subheader("آمار خلاصه")
|
| 1247 |
|
| 1248 |
col1, col2, col3, col4 = st.columns(4)
|
|
@@ -1252,13 +1437,15 @@ with tabs[4]:
|
|
| 1252 |
|
| 1253 |
with col2:
|
| 1254 |
if 'مساحت' in filtered_farm_df.columns:
|
| 1255 |
-
|
|
|
|
| 1256 |
else:
|
| 1257 |
st.metric("تعداد واریتهها", len(filtered_farm_df['تنوع'].unique()))
|
| 1258 |
|
| 1259 |
with col3:
|
| 1260 |
age_column = 'سن_x' if 'سن_x' in filtered_farm_df.columns else 'سن'
|
| 1261 |
-
|
|
|
|
| 1262 |
|
| 1263 |
with col4:
|
| 1264 |
if 'روز' in filtered_farm_df.columns:
|
|
@@ -1266,12 +1453,15 @@ with tabs[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,6 +1470,7 @@ with tabs[4]:
|
|
| 1280 |
|
| 1281 |
pdf_buffer = create_report_pdf(figs, tables, "گزارش وضعیت کلی مزارع")
|
| 1282 |
|
|
|
|
| 1283 |
st.download_button(
|
| 1284 |
label="دانلود گزارش (PDF)",
|
| 1285 |
data=pdf_buffer,
|
|
@@ -1287,6 +1478,7 @@ with tabs[4]:
|
|
| 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,9 +1493,11 @@ with tabs[4]:
|
|
| 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,9 +1505,11 @@ with tabs[4]:
|
|
| 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,6 +1520,7 @@ with tabs[4]:
|
|
| 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,6 +1541,7 @@ with tabs[4]:
|
|
| 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,8 +1566,10 @@ with tabs[4]:
|
|
| 1368 |
hide_index=True
|
| 1369 |
)
|
| 1370 |
|
|
|
|
| 1371 |
st.subheader("مقایسه شاخصها بین مزارع")
|
| 1372 |
|
|
|
|
| 1373 |
selected_index = st.selectbox(
|
| 1374 |
"انتخاب شاخص",
|
| 1375 |
["NDVI", "NDWI", "EVI", "NDMI", "LAI", "CHL"]
|
|
@@ -1384,6 +1584,7 @@ with tabs[4]:
|
|
| 1384 |
"CHL": "chl"
|
| 1385 |
}
|
| 1386 |
|
|
|
|
| 1387 |
fig_compare = px.bar(
|
| 1388 |
avg_indices,
|
| 1389 |
x='farm_name',
|
|
@@ -1402,15 +1603,19 @@ with tabs[4]:
|
|
| 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,13 +1633,16 @@ with tabs[4]:
|
|
| 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,6 +1650,7 @@ with tabs[4]:
|
|
| 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,35 +1665,42 @@ with tabs[4]:
|
|
| 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,11 +1712,12 @@ with tabs[4]:
|
|
| 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':
|
| 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,6 +1731,7 @@ with tabs[4]:
|
|
| 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,18 +1749,23 @@ with tabs[4]:
|
|
| 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,6 +1773,7 @@ with tabs[4]:
|
|
| 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,25 +1782,28 @@ with tabs[4]:
|
|
| 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,8 +1834,10 @@ with tabs[4]:
|
|
| 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,13 +1856,16 @@ with tabs[4]:
|
|
| 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,6 +1873,7 @@ with tabs[4]:
|
|
| 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,10 +1888,13 @@ with tabs[4]:
|
|
| 1655 |
elif report_type == "گزارش پیشبینی رشد":
|
| 1656 |
st.subheader("گزارش پیشبینی رشد")
|
| 1657 |
|
| 1658 |
-
|
|
|
|
| 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,6 +1914,7 @@ with tabs[4]:
|
|
| 1678 |
hide_index=True
|
| 1679 |
)
|
| 1680 |
|
|
|
|
| 1681 |
fig_growth = px.bar(
|
| 1682 |
growth_stats,
|
| 1683 |
x='farm_id',
|
|
@@ -1696,16 +1933,20 @@ with tabs[4]:
|
|
| 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,13 +1963,16 @@ with tabs[4]:
|
|
| 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,6 +1980,7 @@ with tabs[4]:
|
|
| 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,6 +1992,7 @@ with tabs[4]:
|
|
| 1747 |
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
| 1748 |
)
|
| 1749 |
|
|
|
|
| 1750 |
with tabs[5]:
|
| 1751 |
st.header("تنظیمات")
|
| 1752 |
|
|
@@ -1755,6 +2001,7 @@ with tabs[5]:
|
|
| 1755 |
with settings_tabs[0]:
|
| 1756 |
st.subheader("تنظیمات عمومی")
|
| 1757 |
|
|
|
|
| 1758 |
st.write("### تنظیمات ظاهری")
|
| 1759 |
|
| 1760 |
col1, col2 = st.columns(2)
|
|
@@ -1781,6 +2028,7 @@ with tabs[5]:
|
|
| 1781 |
["Viridis (پیشفرض)", "Plasma", "Inferno", "Magma", "Cividis"]
|
| 1782 |
)
|
| 1783 |
|
|
|
|
| 1784 |
st.write("### تنظیمات زبان")
|
| 1785 |
|
| 1786 |
language = st.radio(
|
|
@@ -1788,6 +2036,7 @@ with tabs[5]:
|
|
| 1788 |
["فارسی (پیشفرض)", "انگلیسی"]
|
| 1789 |
)
|
| 1790 |
|
|
|
|
| 1791 |
st.write("### تنظیمات واحدها")
|
| 1792 |
|
| 1793 |
col1, col2 = st.columns(2)
|
|
@@ -1814,12 +2063,14 @@ with tabs[5]:
|
|
| 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,19 +2078,23 @@ with tabs[5]:
|
|
| 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,6 +2112,7 @@ with tabs[5]:
|
|
| 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'],
|
|
@@ -1873,6 +2129,7 @@ with tabs[5]:
|
|
| 1873 |
|
| 1874 |
users_df = pd.DataFrame(users_data)
|
| 1875 |
|
|
|
|
| 1876 |
st.write("### لیست کاربران")
|
| 1877 |
|
| 1878 |
st.dataframe(
|
|
@@ -1887,6 +2144,7 @@ with tabs[5]:
|
|
| 1887 |
hide_index=True
|
| 1888 |
)
|
| 1889 |
|
|
|
|
| 1890 |
st.write("### افزودن کاربر جدید")
|
| 1891 |
|
| 1892 |
col1, col2 = st.columns(2)
|
|
@@ -1905,8 +2163,10 @@ with tabs[5]:
|
|
| 1905 |
else:
|
| 1906 |
st.error("لطفاً تمام فیلدها را پر کنید.")
|
| 1907 |
|
|
|
|
| 1908 |
st.write("### مدیریت دسترسیها")
|
| 1909 |
|
|
|
|
| 1910 |
access_data = {
|
| 1911 |
'role': ['مدیر', 'کارشناس', 'کاربر'],
|
| 1912 |
'dashboard': ['خواندن/نوشتن', 'خواندن/نوشتن', 'خواندن'],
|
|
@@ -1933,9 +2193,14 @@ with tabs[5]:
|
|
| 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")
|
|
|
|
| 18 |
from sklearn.metrics import mean_squared_error
|
| 19 |
import os
|
| 20 |
import json
|
| 21 |
+
import requests
|
| 22 |
|
| 23 |
# تنظیمات صفحه
|
| 24 |
st.set_page_config(
|
|
|
|
| 112 |
</style>
|
| 113 |
""", unsafe_allow_html=True)
|
| 114 |
|
| 115 |
+
# تابع برای دانلود فایلهای داده
|
| 116 |
+
@st.cache_data
|
| 117 |
+
def download_data_files():
|
| 118 |
+
# دانلود فایل مختصات مزارع
|
| 119 |
+
farm_coordinates_url = "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/farm_coordinates%20%281%29-nEgA8Z2YT3t4IcKbVOOGI1mPp95QeM.csv"
|
| 120 |
+
farm_database_url = "https://hebbkx1anhila5yf.public.blob.vercel-storage.com/%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87%20%D8%AF%D8%A7%D8%AF%D9%87%20%281%29-nt53xRXa8kvJKe5wyYpUCNtd0wej9R.csv"
|
| 121 |
+
|
| 122 |
+
try:
|
| 123 |
+
# دانلود فایلها
|
| 124 |
+
farm_coordinates_response = requests.get(farm_coordinates_url)
|
| 125 |
+
farm_database_response = requests.get(farm_database_url)
|
| 126 |
+
|
| 127 |
+
# ذخیره فایلها
|
| 128 |
+
with open('farm_coordinates.csv', 'wb') as f:
|
| 129 |
+
f.write(farm_coordinates_response.content)
|
| 130 |
+
|
| 131 |
+
with open('پایگاه داده (1).csv', 'wb') as f:
|
| 132 |
+
f.write(farm_database_response.content)
|
| 133 |
+
|
| 134 |
+
return True
|
| 135 |
+
except Exception as e:
|
| 136 |
+
st.error(f"خطا در دانلود فایلهای داده: {e}")
|
| 137 |
+
return False
|
| 138 |
+
|
| 139 |
# تابع برای اتصال به Google Earth Engine با استفاده از اطلاعات حساب سرویس
|
| 140 |
@st.cache_resource
|
| 141 |
def initialize_earth_engine():
|
|
|
|
| 269 |
</div>
|
| 270 |
""", unsafe_allow_html=True)
|
| 271 |
|
| 272 |
+
# تابع برای تبدیل ستون به مقادیر عددی با مدیریت خطا
|
| 273 |
+
def convert_to_numeric(df, column_name):
|
| 274 |
+
try:
|
| 275 |
+
# تلاش برای تبدیل ستون به عدد و جایگزینی خطاها با NaN
|
| 276 |
+
return pd.to_numeric(df[column_name], errors='coerce')
|
| 277 |
+
except Exception as e:
|
| 278 |
+
st.warning(f"خطا در تبدیل ستون {column_name} به عدد: {e}")
|
| 279 |
+
return pd.Series([np.nan] * len(df))
|
| 280 |
+
|
| 281 |
# تابع برای بارگذاری دادههای مزارع نیشکر
|
| 282 |
def load_farm_data():
|
| 283 |
try:
|
| 284 |
+
# دانلود فایلهای داده اگر موجود نیستند
|
| 285 |
+
if not os.path.exists('farm_coordinates.csv') or not os.path.exists('پایگاه داده (1).csv'):
|
| 286 |
+
download_data_files()
|
| 287 |
+
|
| 288 |
# بارگذاری فایل مختصات مزارع
|
| 289 |
farm_coordinates = pd.read_csv('farm_coordinates.csv')
|
| 290 |
|
| 291 |
# بارگذاری فایل پایگاه داده مزارع
|
| 292 |
farm_database = pd.read_csv('پایگاه داده (1).csv')
|
| 293 |
|
| 294 |
+
# تبدیل ستونهای عددی به عدد با مدیریت خطا
|
| 295 |
+
if 'سن' in farm_coordinates.columns:
|
| 296 |
+
farm_coordinates['سن'] = convert_to_numeric(farm_coordinates, 'سن')
|
| 297 |
+
|
| 298 |
+
if 'سن' in farm_database.columns:
|
| 299 |
+
farm_database['سن'] = convert_to_numeric(farm_database, 'سن')
|
| 300 |
+
|
| 301 |
+
if 'مساحت' in farm_database.columns:
|
| 302 |
+
farm_database['مساحت'] = convert_to_numeric(farm_database, 'مساحت')
|
| 303 |
+
|
| 304 |
+
if 'مساحت زیرمجموعه' in farm_database.columns:
|
| 305 |
+
farm_database['مساحت زیرمجموعه'] = convert_to_numeric(farm_database, 'مساحت زیرمجموعه')
|
| 306 |
+
|
| 307 |
# ادغام دادهها بر اساس ستون مزرعه
|
| 308 |
merged_data = pd.merge(farm_database, farm_coordinates, on='مزرعه', how='inner')
|
| 309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
return farm_coordinates, farm_database, merged_data
|
| 311 |
except Exception as e:
|
| 312 |
st.error(f"خطا در بارگذاری دادهها: {e}")
|
| 313 |
|
| 314 |
# ایجاد دادههای نمونه در صورت عدم وجود فایلها
|
| 315 |
+
# دادههای نمونه برای مختصات مزارع
|
| 316 |
farm_coordinates = pd.DataFrame({
|
| 317 |
'مزرعه': [f'مزرعه {i}' for i in range(1, 21)],
|
| 318 |
'سن': np.random.randint(1, 10, 20),
|
|
|
|
| 321 |
'عرض جغرافیایی': np.random.uniform(31.0, 32.0, 20)
|
| 322 |
})
|
| 323 |
|
| 324 |
+
# دادههای نمونه برای پایگاه داده مزارع
|
| 325 |
days = ['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه']
|
| 326 |
farm_database = pd.DataFrame({
|
| 327 |
'مزرعه': [f'مزرعه {i}' for i in range(1, 21)],
|
|
|
|
| 338 |
# ادغام دادهها
|
| 339 |
merged_data = pd.merge(farm_database, farm_coordinates, on='مزرعه', how='inner')
|
| 340 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
return farm_coordinates, farm_database, merged_data
|
| 342 |
|
| 343 |
# تابع برای ایجاد نقشه مزارع
|
| 344 |
def create_farm_map(df, selected_farms=None):
|
| 345 |
+
# ایجاد نقشه با مرکزیت میانگین مختصات
|
| 346 |
center_lat = df['عرض جغرافیایی'].mean()
|
| 347 |
center_lon = df['طول جغرافیایی'].mean()
|
| 348 |
m = folium.Map(location=[center_lat, center_lon], zoom_start=10)
|
| 349 |
|
| 350 |
+
# اضافه کردن لایههای مختلف
|
| 351 |
folium.TileLayer('openstreetmap').add_to(m)
|
| 352 |
folium.TileLayer('Stamen Terrain').add_to(m)
|
| 353 |
folium.TileLayer('Stamen Toner').add_to(m)
|
| 354 |
folium.TileLayer('Stamen Watercolor').add_to(m)
|
| 355 |
folium.TileLayer('CartoDB positron').add_to(m)
|
| 356 |
|
| 357 |
+
# اضافه کردن کنترل لایهها
|
| 358 |
folium.LayerControl().add_to(m)
|
| 359 |
|
| 360 |
+
# اضافه کردن مارکرها برای هر مزرعه
|
| 361 |
for idx, row in df.iterrows():
|
| 362 |
color = 'green'
|
| 363 |
if selected_farms is not None and row['مزرعه'] in selected_farms:
|
|
|
|
| 386 |
|
| 387 |
# تابع برای ایجاد نمودارهای تحلیلی
|
| 388 |
def create_analysis_charts(df):
|
| 389 |
+
# نمودار توزیع واریته
|
| 390 |
variety_column = 'واریته' if 'واریته' in df.columns else 'تنوع'
|
| 391 |
fig_variety = px.pie(
|
| 392 |
df,
|
|
|
|
| 400 |
title_font_size=20
|
| 401 |
)
|
| 402 |
|
| 403 |
+
# نمودار سن مزارع
|
| 404 |
age_column = 'سن_x' if 'سن_x' in df.columns else 'سن'
|
| 405 |
fig_age = px.histogram(
|
| 406 |
df,
|
|
|
|
| 416 |
title_font_size=20
|
| 417 |
)
|
| 418 |
|
| 419 |
+
# نمودار مساحت مزارع
|
| 420 |
if 'مساحت' in df.columns:
|
| 421 |
+
# تبدیل ستونهای عددی به عدد با مدیریت خطا
|
| 422 |
+
area = pd.to_numeric(df['مساحت'], errors='coerce')
|
| 423 |
+
sub_area = pd.to_numeric(df['مساحت زیرمجموعه'], errors='coerce') if 'مساحت زیرمجموعه' in df.columns else area
|
| 424 |
+
age_values = pd.to_numeric(df[age_column], errors='coerce')
|
| 425 |
+
|
| 426 |
+
# ایجاد DataFrame جدید با مقادیر عددی
|
| 427 |
+
plot_df = df.copy()
|
| 428 |
+
plot_df['مساحت'] = area
|
| 429 |
+
plot_df['مساحت زیرمجموعه'] = sub_area
|
| 430 |
+
plot_df[age_column] = age_values
|
| 431 |
+
|
| 432 |
+
# حذف ردیفهای با مقادیر NaN
|
| 433 |
+
plot_df = plot_df.dropna(subset=['مساحت', 'مساحت زیرمجموعه', age_column])
|
| 434 |
+
|
| 435 |
+
if len(plot_df) > 0:
|
| 436 |
+
fig_area = px.scatter(
|
| 437 |
+
plot_df,
|
| 438 |
+
x='مساحت',
|
| 439 |
+
y='مساحت زیرمجموعه' if 'مساحت زیرمجموعه' in plot_df.columns else 'مساحت',
|
| 440 |
+
color=variety_column,
|
| 441 |
+
size=age_column,
|
| 442 |
+
hover_name='مزرعه',
|
| 443 |
+
title='رابطه بین مساحت و مساحت زیرمجموعه مزارع',
|
| 444 |
+
labels={'مساحت': 'مساحت کل (هکتار)', 'مساحت زیرمجموعه': 'مساحت زیرمجموعه (هکتار)'},
|
| 445 |
+
color_discrete_sequence=px.colors.sequential.Greens
|
| 446 |
+
)
|
| 447 |
+
fig_area.update_layout(
|
| 448 |
+
font_family="Vazirmatn",
|
| 449 |
+
title_font_family="Vazirmatn",
|
| 450 |
+
title_font_size=20
|
| 451 |
+
)
|
| 452 |
+
else:
|
| 453 |
+
fig_area = None
|
| 454 |
else:
|
| 455 |
fig_area = None
|
| 456 |
|
|
|
|
| 458 |
|
| 459 |
# تابع برای ایجاد مدل پیشبینی
|
| 460 |
def create_prediction_model(df):
|
| 461 |
+
# ایجاد دادههای نمونه برای سری زمانی
|
| 462 |
dates = pd.date_range(end=datetime.now(), periods=90)
|
| 463 |
farm_ids = df['مزرعه'].unique()
|
| 464 |
|
|
|
|
| 468 |
base_height = np.random.uniform(0.5, 3.0)
|
| 469 |
|
| 470 |
for date in dates:
|
| 471 |
+
# شبیهسازی رشد با الگوی سینوسی + روند
|
| 472 |
day_of_year = date.dayofyear
|
| 473 |
seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
|
| 474 |
trend_factor = date.dayofyear / 365 * 0.3
|
| 475 |
|
| 476 |
ndvi = base_ndvi + seasonal_factor + trend_factor + np.random.normal(0, 0.05)
|
| 477 |
+
ndvi = max(0, min(1, ndvi)) # محدود کردن به بازه 0 تا 1
|
| 478 |
|
| 479 |
height = base_height + seasonal_factor * 2 + trend_factor + np.random.normal(0, 0.1)
|
| 480 |
+
height = max(0, height) # ارتفاع نمیتواند منفی باشد
|
| 481 |
|
| 482 |
time_series_data.append({
|
| 483 |
'farm_id': farm_id,
|
|
|
|
| 490 |
|
| 491 |
time_df = pd.DataFrame(time_series_data)
|
| 492 |
|
| 493 |
+
# ایجاد ویژگیها برای مدل
|
| 494 |
X = time_df[['ndvi', 'day_of_year', 'month']].values
|
| 495 |
y = time_df['height'].values
|
| 496 |
|
| 497 |
+
# تقسیم دادهها به آموزش و آزمون
|
| 498 |
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
|
| 499 |
|
| 500 |
+
# آموزش مدل
|
| 501 |
model = RandomForestRegressor(n_estimators=100, random_state=42)
|
| 502 |
model.fit(X_train, y_train)
|
| 503 |
|
| 504 |
+
# ارزیابی مدل
|
| 505 |
y_pred = model.predict(X_test)
|
| 506 |
mse = mean_squared_error(y_test, y_pred)
|
| 507 |
|
| 508 |
+
# ایجاد نمودار برای نمایش نتایج
|
| 509 |
results_df = pd.DataFrame({
|
| 510 |
'Actual': y_test,
|
| 511 |
'Predicted': y_pred
|
|
|
|
| 534 |
title_font_size=20
|
| 535 |
)
|
| 536 |
|
| 537 |
+
# ایجاد نمودار سری زمانی
|
| 538 |
+
farm_id = farm_ids[0] # انتخاب اولین مزرعه برای نمایش
|
| 539 |
farm_data = time_df[time_df['farm_id'] == farm_id]
|
| 540 |
|
| 541 |
fig_ts = px.line(
|
|
|
|
| 553 |
yaxis_title='مقدار'
|
| 554 |
)
|
| 555 |
|
| 556 |
+
return model, fig, fig_ts, time_df, y_test, y_pred
|
| 557 |
+
|
| 558 |
+
# دانلود فایلهای داده
|
| 559 |
+
download_data_files()
|
| 560 |
|
| 561 |
# بارگذاری دادههای مزارع نیشکر
|
| 562 |
farm_coordinates, farm_database, merged_data = load_farm_data()
|
|
|
|
| 564 |
# منوی اصلی
|
| 565 |
st.title("🌱 سامانه مانیتورینگ مزارع نیشکر")
|
| 566 |
|
| 567 |
+
# منوی افقی
|
| 568 |
tabs = st.tabs([
|
| 569 |
"📊 داشبورد",
|
| 570 |
"🗺️ نقشه مزارع",
|
|
|
|
| 574 |
"⚙️ تنظیمات"
|
| 575 |
])
|
| 576 |
|
| 577 |
+
# تب داشبورد
|
| 578 |
with tabs[0]:
|
| 579 |
st.header("داشبورد مدیریت مزارع نیشکر")
|
| 580 |
|
| 581 |
+
# کارتهای اطلاعاتی
|
| 582 |
col1, col2, col3, col4 = st.columns(4)
|
| 583 |
|
| 584 |
with col1:
|
|
|
|
| 589 |
display_metric_card("تعداد واریتهها", unique_varieties, "🌿")
|
| 590 |
|
| 591 |
with col3:
|
| 592 |
+
# محاسبه میانگین سن با مدیریت خطا
|
| 593 |
+
try:
|
| 594 |
+
age_column = 'سن_x' if 'سن_x' in merged_data.columns else 'سن'
|
| 595 |
+
# تبدیل به عدد با مدیریت خطا
|
| 596 |
+
age_values = pd.to_numeric(merged_data[age_column], errors='coerce')
|
| 597 |
+
# محاسبه میانگین با نادیده گرفتن مقادیر NaN
|
| 598 |
+
avg_age = age_values.mean()
|
| 599 |
+
if pd.isna(avg_age):
|
| 600 |
+
display_metric_card("میانگین سن", "نامشخص", "📅")
|
| 601 |
+
else:
|
| 602 |
+
display_metric_card("میانگین سن", f"{avg_age:.1f} سال", "📅")
|
| 603 |
+
except Exception as e:
|
| 604 |
+
st.warning(f"خطا در محاسبه میانگین سن: {e}")
|
| 605 |
+
display_metric_card("میانگین سن", "نامشخص", "📅")
|
| 606 |
|
| 607 |
with col4:
|
| 608 |
if 'مساحت' in merged_data.columns:
|
| 609 |
+
try:
|
| 610 |
+
# تبدیل به عدد با مدیریت خطا
|
| 611 |
+
area_values = pd.to_numeric(merged_data['مساحت'], errors='coerce')
|
| 612 |
+
# محاسبه مجموع با نادیده گرفتن مقادیر NaN
|
| 613 |
+
total_area = area_values.sum()
|
| 614 |
+
if pd.isna(total_area):
|
| 615 |
+
display_metric_card("مساحت کل", "نامشخص", "📏")
|
| 616 |
+
else:
|
| 617 |
+
display_metric_card("مساحت کل", f"{total_area:.1f} هکتار", "📏")
|
| 618 |
+
except Exception as e:
|
| 619 |
+
st.warning(f"خطا در محاسبه مساحت کل: {e}")
|
| 620 |
+
display_metric_card("مساحت کل", "نامشخص", "📏")
|
| 621 |
else:
|
| 622 |
display_metric_card("تعداد کانالها", len(merged_data['کانال'].unique()) if 'کانال' in merged_data.columns else "نامشخص", "🚿")
|
| 623 |
|
| 624 |
st.markdown("---")
|
| 625 |
|
| 626 |
+
# نمودارهای تحلیلی
|
| 627 |
st.subheader("تحلیل کلی مزارع")
|
| 628 |
|
| 629 |
col1, col2 = st.columns(2)
|
|
|
|
| 637 |
if fig_area:
|
| 638 |
st.plotly_chart(fig_area, use_container_width=True)
|
| 639 |
|
| 640 |
+
# جدول اطلاعات مزارع
|
| 641 |
st.subheader("اطلاعات مزارع")
|
| 642 |
|
| 643 |
+
# انتخاب ستونهای مهم برای نمایش
|
| 644 |
if 'مساحت' in merged_data.columns:
|
| 645 |
display_columns = ['مزرعه', 'کانال', 'اداره', 'سن_x', 'واریته', 'مساحت', 'روز']
|
| 646 |
column_config = {
|
|
|
|
| 668 |
hide_index=True
|
| 669 |
)
|
| 670 |
|
| 671 |
+
# تب نقشه مزارع
|
| 672 |
with tabs[1]:
|
| 673 |
st.header("نقشه مزارع نیشکر")
|
| 674 |
|
|
|
|
| 677 |
with col1:
|
| 678 |
st.subheader("فیلتر مزارع")
|
| 679 |
|
| 680 |
+
# فیلتر بر اساس روز هفته
|
| 681 |
days = ['همه روزها', 'شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه']
|
| 682 |
selected_day = st.selectbox("انتخاب روز هفته", days)
|
| 683 |
|
| 684 |
+
# فیلتر بر اساس واریته
|
| 685 |
variety_column = 'واریته' if 'واریته' in merged_data.columns else 'تنوع'
|
| 686 |
varieties = ['همه واریتهها'] + list(merged_data[variety_column].unique())
|
| 687 |
selected_variety = st.selectbox("انتخاب واریته", varieties)
|
| 688 |
|
| 689 |
+
# فیلتر بر اساس سن
|
| 690 |
age_column = 'سن_x' if 'سن_x' in merged_data.columns else 'سن'
|
| 691 |
+
age_values = pd.to_numeric(merged_data[age_column], errors='coerce')
|
| 692 |
+
min_age, max_age = int(age_values.min()), int(age_values.max())
|
| 693 |
age_range = st.slider(
|
| 694 |
"محدوده سن (سال)",
|
| 695 |
min_value=min_age,
|
|
|
|
| 697 |
value=(min_age, max_age)
|
| 698 |
)
|
| 699 |
|
| 700 |
+
# اعمال فیلترها
|
| 701 |
filtered_df = merged_data.copy()
|
| 702 |
|
| 703 |
if selected_day != 'همه روزها' and 'روز' in filtered_df.columns:
|
|
|
|
| 706 |
if selected_variety != 'همه واریتهها':
|
| 707 |
filtered_df = filtered_df[filtered_df[variety_column] == selected_variety]
|
| 708 |
|
| 709 |
+
# تبدیل ستون سن به عددی برای فیلتر کردن
|
| 710 |
+
age_values = pd.to_numeric(filtered_df[age_column], errors='coerce')
|
| 711 |
+
filtered_df = filtered_df[(age_values >= age_range[0]) & (age_values <= age_range[1])]
|
| 712 |
|
| 713 |
st.write(f"تعداد مزارع نمایش داده شده: {len(filtered_df)}")
|
| 714 |
|
| 715 |
with col2:
|
| 716 |
+
# نمایش نقشه
|
| 717 |
if len(filtered_df) > 0:
|
| 718 |
farm_map = create_farm_map(filtered_df)
|
| 719 |
folium_static(farm_map, width=800, height=500)
|
| 720 |
else:
|
| 721 |
st.warning("هیچ مزرعهای با فیلترهای انتخاب شده یافت نشد.")
|
| 722 |
|
| 723 |
+
# تب ورود اطلاعات
|
| 724 |
with tabs[2]:
|
| 725 |
st.header("ورود و آپلود اطلاعات")
|
| 726 |
|
|
|
|
| 732 |
col1, col2 = st.columns(2)
|
| 733 |
|
| 734 |
with col1:
|
| 735 |
+
# انتخاب مزرعه موجود یا ایجاد مزرعه جدید
|
| 736 |
option = st.radio("انتخاب گزینه", ["ویرایش مزرعه موجود", "ایجاد مزرعه جدید"])
|
| 737 |
|
| 738 |
if option == "ویرایش مزرعه موجود":
|
| 739 |
farm_name = st.selectbox("انتخاب مزرعه", merged_data['مزرعه'].tolist())
|
| 740 |
selected_farm = merged_data[merged_data['مزرعه'] == farm_name].iloc[0]
|
| 741 |
|
| 742 |
+
# فیلدهای ورودی بر اساس ستونهای موجود
|
| 743 |
if 'کانال' in merged_data.columns:
|
| 744 |
canal = st.text_input("کانال", value=selected_farm['کانال'])
|
| 745 |
office = st.text_input("اداره", value=selected_farm['اداره'])
|
|
|
|
| 750 |
day = st.selectbox("روز", options=['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'], index=['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'].index(selected_farm['روز']))
|
| 751 |
|
| 752 |
age_column = 'سن_x' if 'سن_x' in selected_farm else 'سن'
|
| 753 |
+
age = st.text_input("سن", value=selected_farm[age_column])
|
| 754 |
|
| 755 |
lat = st.number_input("عرض جغرافیایی", value=float(selected_farm['عرض جغرافیایی']), format="%.6f")
|
| 756 |
lon = st.number_input("طول جغرافیایی", value=float(selected_farm['طول جغرافیایی']), format="%.6f")
|
|
|
|
| 758 |
if st.button("بروزرسانی اطلاعات"):
|
| 759 |
st.success("اطلاعات مزرعه با موفقیت بروزرسانی شد.")
|
| 760 |
else:
|
| 761 |
+
# ایجاد مزرعه جدید
|
| 762 |
farm_name = st.text_input("نام مزرعه", value=f"مزرعه جدید")
|
| 763 |
|
| 764 |
+
# فیلدهای ورودی بر اساس ستونهای موجود
|
| 765 |
if 'کانال' in merged_data.columns:
|
| 766 |
canal = st.text_input("کانال")
|
| 767 |
office = st.text_input("اداره")
|
|
|
|
| 771 |
group = st.text_input("گروه")
|
| 772 |
day = st.selectbox("روز", options=['شنبه', 'یکشنبه', 'دوشنبه', 'سهشنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه'])
|
| 773 |
|
| 774 |
+
age = st.text_input("سن", value="R1")
|
| 775 |
lat = st.number_input("عرض جغرافیایی", value=31.5, format="%.6f")
|
| 776 |
lon = st.number_input("طول جغرافیایی", value=48.5, format="%.6f")
|
| 777 |
|
|
|
|
| 779 |
st.success("مزرعه جدید با موفقیت ثبت شد.")
|
| 780 |
|
| 781 |
with col2:
|
| 782 |
+
# نمایش نقشه برای انتخاب موقعیت
|
| 783 |
st.subheader("انتخاب موقعیت روی نقشه")
|
| 784 |
st.write("برای انتخاب موقعیت دقیق، روی نقشه کلیک کنید.")
|
| 785 |
|
| 786 |
+
# نمایش نقشه
|
| 787 |
center_lat = merged_data['عرض جغرافیایی'].mean()
|
| 788 |
center_lon = merged_data['طول جغرافیایی'].mean()
|
| 789 |
m = folium.Map(location=[center_lat, center_lon], zoom_start=10)
|
|
|
|
| 842 |
except Exception as e:
|
| 843 |
st.error(f"خطا در خواندن فایل: {e}")
|
| 844 |
|
| 845 |
+
# تب تحلیل دادهها
|
| 846 |
with tabs[3]:
|
| 847 |
st.header("تحلیل دادهها")
|
| 848 |
|
|
|
|
| 851 |
with analysis_tabs[0]:
|
| 852 |
st.subheader("محاسبه و نمایش شاخصهای گیاهی")
|
| 853 |
|
| 854 |
+
# اتصال به Google Earth Engine
|
| 855 |
ee_connected = initialize_earth_engine()
|
| 856 |
|
| 857 |
if ee_connected:
|
| 858 |
+
# انتخاب مزرعه
|
| 859 |
farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_indices")
|
| 860 |
selected_farm = merged_data[merged_data['مزرعه'] == farm_name].iloc[0]
|
| 861 |
|
| 862 |
+
# انتخاب تاریخ
|
| 863 |
date = st.date_input("انتخاب تاریخ", value=datetime.now() - timedelta(days=7))
|
| 864 |
|
| 865 |
if st.button("محاسبه شاخصها"):
|
| 866 |
with st.spinner("در حال محاسبه شاخصها..."):
|
| 867 |
+
# ایجاد geometry برای مزرعه
|
| 868 |
point = ee.Geometry.Point([selected_farm['طول جغرافیایی'], selected_farm['عرض جغرافیایی']])
|
| 869 |
+
buffer = point.buffer(100) # بافر 100 متری
|
| 870 |
|
| 871 |
+
# محاسبه شاخصها
|
| 872 |
indices = calculate_indices(date.strftime('%Y-%m-%d'), buffer)
|
| 873 |
|
| 874 |
if indices is not None:
|
| 875 |
+
# نمایش نتایج
|
| 876 |
st.success("شاخصها با موفقیت محاسبه شدند.")
|
| 877 |
|
| 878 |
+
# ایجاد نقشه برای نمایش شاخصها
|
| 879 |
Map = geemap.Map()
|
| 880 |
Map.centerObject(buffer, 14)
|
| 881 |
|
| 882 |
+
# اضافه کردن لایههای مختلف
|
| 883 |
vis_params = {
|
| 884 |
'min': 0,
|
| 885 |
'max': 1,
|
|
|
|
| 893 |
Map.addLayer(indices.select('LAI'), {'min': 0, 'max': 5, 'palette': ['red', 'yellow', 'green']}, 'LAI')
|
| 894 |
Map.addLayer(indices.select('CHL'), {'min': 0, 'max': 3, 'palette': ['red', 'yellow', 'green']}, 'CHL')
|
| 895 |
|
| 896 |
+
# اضافه کردن کنترل لایهها
|
| 897 |
Map.addLayerControl()
|
| 898 |
|
| 899 |
+
# نمایش نقشه
|
| 900 |
Map.to_streamlit(height=500)
|
| 901 |
|
| 902 |
+
# نمایش مقادیر میانگین
|
| 903 |
col1, col2, col3 = st.columns(3)
|
| 904 |
|
| 905 |
with col1:
|
|
|
|
| 919 |
with analysis_tabs[1]:
|
| 920 |
st.subheader("تحلیل سری زمانی")
|
| 921 |
|
| 922 |
+
# ایجاد مدل و نمودارها
|
| 923 |
+
model, fig_model, fig_ts, time_df, y_test, y_pred = create_prediction_model(merged_data)
|
| 924 |
|
| 925 |
+
# انتخاب مزرعه
|
| 926 |
farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_ts")
|
| 927 |
|
| 928 |
+
# فیلتر دادهها برای مزرعه انتخاب شده
|
| 929 |
farm_time_data = time_df[time_df['farm_id'] == farm_name]
|
| 930 |
|
| 931 |
+
# نمایش نمودار سری زمانی
|
| 932 |
fig_farm_ts = px.line(
|
| 933 |
farm_time_data,
|
| 934 |
x='date',
|
|
|
|
| 946 |
|
| 947 |
st.plotly_chart(fig_farm_ts, use_container_width=True)
|
| 948 |
|
| 949 |
+
# تحلیل روند
|
| 950 |
st.subheader("تحلیل روند")
|
| 951 |
|
| 952 |
col1, col2 = st.columns(2)
|
| 953 |
|
| 954 |
with col1:
|
| 955 |
+
# محاسبه میانگین متحرک
|
| 956 |
window_size = st.slider("اندازه پنجره میانگین متحرک", min_value=3, max_value=30, value=7)
|
| 957 |
|
| 958 |
farm_time_data['ndvi_ma'] = farm_time_data['ndvi'].rolling(window=window_size).mean()
|
| 959 |
farm_time_data['height_ma'] = farm_time_data['height'].rolling(window=window_size).mean()
|
| 960 |
|
| 961 |
+
# نمایش نمودار میانگین متحرک
|
| 962 |
fig_ma = px.line(
|
| 963 |
farm_time_data.dropna(),
|
| 964 |
x='date',
|
|
|
|
| 977 |
st.plotly_chart(fig_ma, use_container_width=True)
|
| 978 |
|
| 979 |
with col2:
|
| 980 |
+
# تحلیل فصلی
|
| 981 |
monthly_data = farm_time_data.groupby('month').agg({
|
| 982 |
'ndvi': 'mean',
|
| 983 |
'height': 'mean'
|
|
|
|
| 1004 |
with analysis_tabs[2]:
|
| 1005 |
st.subheader("تشخیص تنشها")
|
| 1006 |
|
| 1007 |
+
# انتخاب مزرعه
|
| 1008 |
farm_name = st.selectbox("انتخاب مزرعه برای تحلیل", merged_data['مزرعه'].tolist(), key="farm_select_stress")
|
| 1009 |
|
| 1010 |
+
# ایجاد دادههای نمونه برای تنشها
|
| 1011 |
dates = pd.date_range(end=datetime.now(), periods=90)
|
| 1012 |
stress_data = []
|
| 1013 |
|
| 1014 |
+
# شبیهسازی تنش آبی
|
| 1015 |
water_stress_threshold = 0.3
|
| 1016 |
base_ndwi = np.random.uniform(0.2, 0.5)
|
| 1017 |
|
| 1018 |
+
# شبیهسازی تنش بیماری
|
| 1019 |
disease_threshold = 0.4
|
| 1020 |
base_chl = np.random.uniform(1.5, 2.5)
|
| 1021 |
|
|
|
|
| 1023 |
day_of_year = date.dayofyear
|
| 1024 |
seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
|
| 1025 |
|
| 1026 |
+
# شبیهسازی تنش آبی در روزهای خاص
|
| 1027 |
water_stress_event = False
|
| 1028 |
if 30 <= day_of_year <= 40 or 70 <= day_of_year <= 75:
|
| 1029 |
water_stress_event = True
|
|
|
|
| 1031 |
else:
|
| 1032 |
ndwi = base_ndwi + seasonal_factor + np.random.normal(0, 0.05)
|
| 1033 |
|
| 1034 |
+
# شبیهسازی تنش بیماری در روزهای خاص
|
| 1035 |
disease_event = False
|
| 1036 |
if 50 <= day_of_year <= 60:
|
| 1037 |
disease_event = True
|
|
|
|
| 1039 |
else:
|
| 1040 |
chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
|
| 1041 |
|
| 1042 |
+
# محدود کردن مقادیر
|
| 1043 |
ndwi = max(-0.5, min(0.8, ndwi))
|
| 1044 |
chl = max(0.5, min(3.0, chl))
|
| 1045 |
|
|
|
|
| 1050 |
'water_stress': 1 if ndwi < water_stress_threshold else 0,
|
| 1051 |
'disease_stress': 1 if chl < disease_threshold else 0,
|
| 1052 |
'water_stress_event': water_stress_event,
|
| 1053 |
+
'disease_stress_event': disease_stress_event
|
| 1054 |
})
|
| 1055 |
|
| 1056 |
stress_df = pd.DataFrame(stress_data)
|
| 1057 |
|
| 1058 |
+
# نمایش نمودار تنشها
|
| 1059 |
fig_stress = go.Figure()
|
| 1060 |
|
| 1061 |
+
# اضافه کردن NDWI
|
| 1062 |
fig_stress.add_trace(go.Scatter(
|
| 1063 |
x=stress_df['date'],
|
| 1064 |
y=stress_df['ndwi'],
|
|
|
|
| 1066 |
line=dict(color='blue')
|
| 1067 |
))
|
| 1068 |
|
| 1069 |
+
# اضافه کردن CHL
|
| 1070 |
fig_stress.add_trace(go.Scatter(
|
| 1071 |
x=stress_df['date'],
|
| 1072 |
y=stress_df['chl'],
|
|
|
|
| 1075 |
yaxis='y2'
|
| 1076 |
))
|
| 1077 |
|
| 1078 |
+
# اضافه کردن خط آستانه تنش آبی
|
| 1079 |
fig_stress.add_shape(
|
| 1080 |
type='line',
|
| 1081 |
x0=stress_df['date'].min(),
|
|
|
|
| 1086 |
name='آستانه تنش آبی'
|
| 1087 |
)
|
| 1088 |
|
| 1089 |
+
# اضافه کردن خط آستانه تنش بیماری
|
| 1090 |
fig_stress.add_shape(
|
| 1091 |
type='line',
|
| 1092 |
x0=stress_df['date'].min(),
|
|
|
|
| 1098 |
name='آستانه تنش بیماری'
|
| 1099 |
)
|
| 1100 |
|
| 1101 |
+
# تنظیمات نمودار
|
| 1102 |
fig_stress.update_layout(
|
| 1103 |
title='تشخیص تنشهای آبی و بیماری',
|
| 1104 |
font_family="Vazirmatn",
|
|
|
|
| 1129 |
|
| 1130 |
st.plotly_chart(fig_stress, use_container_width=True)
|
| 1131 |
|
| 1132 |
+
# نمایش هشدارها
|
| 1133 |
st.subheader("هشدارهای تنش")
|
| 1134 |
|
| 1135 |
col1, col2 = st.columns(2)
|
| 1136 |
|
| 1137 |
with col1:
|
| 1138 |
+
# هشدارهای تنش آبی
|
| 1139 |
water_stress_days = stress_df[stress_df['water_stress'] == 1]
|
| 1140 |
|
| 1141 |
if len(water_stress_days) > 0:
|
|
|
|
| 1153 |
st.success("هیچ تنش آبی تشخیص داده نشده است.")
|
| 1154 |
|
| 1155 |
with col2:
|
| 1156 |
+
# هشدارهای تنش بیماری
|
| 1157 |
disease_stress_days = stress_df[stress_df['disease_stress'] == 1]
|
| 1158 |
|
| 1159 |
if len(disease_stress_days) > 0:
|
|
|
|
| 1173 |
with analysis_tabs[3]:
|
| 1174 |
st.subheader("پیشبینی رشد")
|
| 1175 |
|
| 1176 |
+
# انتخاب مزرعه
|
| 1177 |
farm_name = st.selectbox("انتخاب مزرعه برای پیشبینی", merged_data['مزرعه'].tolist(), key="farm_select_predict")
|
| 1178 |
|
| 1179 |
+
# فیلتر دادهها برای مزرعه انتخاب شده
|
| 1180 |
farm_time_data = time_df[time_df['farm_id'] == farm_name]
|
| 1181 |
|
| 1182 |
+
# تنظیمات پیشبینی
|
| 1183 |
st.subheader("تنظیمات پیشبینی")
|
| 1184 |
|
| 1185 |
col1, col2 = st.columns(2)
|
|
|
|
| 1192 |
|
| 1193 |
if st.button("انجام پیشبینی"):
|
| 1194 |
with st.spinner("در حال انجام پیشبینی..."):
|
| 1195 |
+
# ایجاد دادههای آینده برای پیشبینی
|
| 1196 |
last_date = farm_time_data['date'].max()
|
| 1197 |
future_dates = pd.date_range(start=last_date + timedelta(days=1), periods=days_to_predict)
|
| 1198 |
|
|
|
|
| 1206 |
|
| 1207 |
future_df = pd.DataFrame(future_data)
|
| 1208 |
|
| 1209 |
+
# پیشبینی NDVI برای روزهای آینده (با الگوی سینوسی + روند)
|
| 1210 |
base_ndvi = farm_time_data['ndvi'].iloc[-1]
|
| 1211 |
future_ndvi = []
|
| 1212 |
|
|
|
|
| 1214 |
seasonal_factor = np.sin(day / 365 * 2 * np.pi) * 0.2
|
| 1215 |
trend_factor = day / 365 * 0.1
|
| 1216 |
ndvi = base_ndvi + seasonal_factor + trend_factor
|
| 1217 |
+
ndvi = max(0, min(1, ndvi)) # محدود کردن به بازه 0 تا 1
|
| 1218 |
future_ndvi.append(ndvi)
|
| 1219 |
|
| 1220 |
future_df['ndvi'] = future_ndvi
|
| 1221 |
|
| 1222 |
+
# پیشبینی ارتفاع با استفاده از مدل
|
| 1223 |
X_future = future_df[['ndvi', 'day_of_year', 'month']].values
|
| 1224 |
future_df['height'] = model.predict(X_future)
|
| 1225 |
|
| 1226 |
+
# محاسبه فاصله اطمینان
|
| 1227 |
+
z_score = {
|
| 1228 |
+
90: 1.645,
|
| 1229 |
+
95: 1.96,
|
| 1230 |
+
99: 2.576
|
| 1231 |
+
}.get(confidence_interval, 1.96)
|
| 1232 |
+
|
| 1233 |
mse = mean_squared_error(y_test, y_pred)
|
| 1234 |
std_dev = np.sqrt(mse)
|
| 1235 |
|
| 1236 |
future_df['height_lower'] = future_df['height'] - z_score * std_dev
|
| 1237 |
future_df['height_upper'] = future_df['height'] + z_score * std_dev
|
| 1238 |
|
| 1239 |
+
# ترکیب دادههای گذشته و آینده
|
| 1240 |
combined_df = pd.concat([
|
| 1241 |
farm_time_data[['date', 'ndvi', 'height']],
|
| 1242 |
future_df[['date', 'ndvi', 'height', 'height_lower', 'height_upper']]
|
| 1243 |
])
|
| 1244 |
|
| 1245 |
+
# نمایش نمودار پیشبینی
|
| 1246 |
fig_predict = go.Figure()
|
| 1247 |
|
| 1248 |
+
# دادههای گذشته
|
| 1249 |
past_data = combined_df[combined_df['date'] <= last_date]
|
| 1250 |
|
| 1251 |
fig_predict.add_trace(go.Scatter(
|
|
|
|
| 1255 |
line=dict(color='blue')
|
| 1256 |
))
|
| 1257 |
|
| 1258 |
+
# دادههای پیشبینی شده
|
| 1259 |
future_data = combined_df[combined_df['date'] > last_date]
|
| 1260 |
|
| 1261 |
fig_predict.add_trace(go.Scatter(
|
|
|
|
| 1265 |
line=dict(color='red')
|
| 1266 |
))
|
| 1267 |
|
| 1268 |
+
# فاصله اطمینان
|
| 1269 |
fig_predict.add_trace(go.Scatter(
|
| 1270 |
x=future_data['date'],
|
| 1271 |
y=future_data['height_upper'],
|
|
|
|
| 1284 |
showlegend=True
|
| 1285 |
))
|
| 1286 |
|
| 1287 |
+
# تنظیمات نمودار
|
| 1288 |
fig_predict.update_layout(
|
| 1289 |
title=f'پیشبینی ارتفاع برای {days_to_predict} روز آینده',
|
| 1290 |
font_family="Vazirmatn",
|
|
|
|
| 1303 |
|
| 1304 |
st.plotly_chart(fig_predict, use_container_width=True)
|
| 1305 |
|
| 1306 |
+
# نمایش جدول پیشبینی
|
| 1307 |
st.subheader("جدول پیشبینی")
|
| 1308 |
|
| 1309 |
st.dataframe(
|
|
|
|
| 1318 |
hide_index=True
|
| 1319 |
)
|
| 1320 |
|
| 1321 |
+
# نمایش خلاصه پیشبینی
|
| 1322 |
st.subheader("خلاصه پیشبینی")
|
| 1323 |
|
| 1324 |
col1, col2, col3 = st.columns(3)
|
|
|
|
| 1346 |
f"{final_height:.2f} متر"
|
| 1347 |
)
|
| 1348 |
|
| 1349 |
+
# تب گزارشگیری
|
| 1350 |
with tabs[4]:
|
| 1351 |
st.header("گزارشگیری")
|
| 1352 |
|
| 1353 |
+
# انتخاب نوع گزارش
|
| 1354 |
report_type = st.selectbox(
|
| 1355 |
"انتخاب نوع گزارش",
|
| 1356 |
["گزارش وضعیت کلی مزارع", "گزارش تحلیل شاخصها", "گزارش تنشها", "گزارش پیشبینی رشد"]
|
| 1357 |
)
|
| 1358 |
|
| 1359 |
+
# انتخاب بازه زمانی
|
| 1360 |
col1, col2 = st.columns(2)
|
| 1361 |
|
| 1362 |
with col1:
|
|
|
|
| 1365 |
with col2:
|
| 1366 |
end_date = st.date_input("تاریخ پایان", value=datetime.now())
|
| 1367 |
|
| 1368 |
+
# انتخاب مزارع
|
| 1369 |
selected_farms = st.multiselect(
|
| 1370 |
"انتخاب مزارع",
|
| 1371 |
options=merged_data['مزرعه'].tolist()
|
| 1372 |
)
|
| 1373 |
|
| 1374 |
if not selected_farms:
|
| 1375 |
+
selected_farms = merged_data['مزرعه'].tolist() # اگر هیچ مزرعهای انتخاب نشده باشد، همه مزارع انتخاب میشوند
|
| 1376 |
|
| 1377 |
+
# فیلتر دادهها بر اساس مزارع انتخاب شده
|
| 1378 |
filtered_farm_df = merged_data[merged_data['مزرعه'].isin(selected_farms)]
|
| 1379 |
|
| 1380 |
if st.button("تولید گزارش"):
|
|
|
|
| 1382 |
if report_type == "گزارش وضعیت کلی مزارع":
|
| 1383 |
st.subheader("گزارش وضعیت کلی مزارع")
|
| 1384 |
|
| 1385 |
+
# نمودارهای تحلیلی
|
| 1386 |
fig_variety, fig_age, fig_area = create_analysis_charts(filtered_farm_df)
|
| 1387 |
|
| 1388 |
col1, col2 = st.columns(2)
|
|
|
|
| 1396 |
if fig_area:
|
| 1397 |
st.plotly_chart(fig_area, use_container_width=True)
|
| 1398 |
|
| 1399 |
+
# جدول اطلاعات مزارع
|
| 1400 |
st.subheader("اطلاعات مزارع")
|
| 1401 |
|
| 1402 |
+
# انتخاب ستونهای مهم برای نمایش
|
| 1403 |
if 'مساحت' in filtered_farm_df.columns:
|
| 1404 |
display_columns = ['مزرعه', 'کانال', 'اداره', 'سن_x', 'واریته', 'مساحت', 'روز']
|
| 1405 |
column_config = {
|
|
|
|
| 1427 |
hide_index=True
|
| 1428 |
)
|
| 1429 |
|
| 1430 |
+
# آمار خلاصه
|
| 1431 |
st.subheader("آمار خلاصه")
|
| 1432 |
|
| 1433 |
col1, col2, col3, col4 = st.columns(4)
|
|
|
|
| 1437 |
|
| 1438 |
with col2:
|
| 1439 |
if 'مساحت' in filtered_farm_df.columns:
|
| 1440 |
+
area_values = pd.to_numeric(filtered_farm_df['مساحت'], errors='coerce')
|
| 1441 |
+
st.metric("میانگین مساحت", f"{area_values.mean():.2f} هکتار")
|
| 1442 |
else:
|
| 1443 |
st.metric("تعداد واریتهها", len(filtered_farm_df['تنوع'].unique()))
|
| 1444 |
|
| 1445 |
with col3:
|
| 1446 |
age_column = 'سن_x' if 'سن_x' in filtered_farm_df.columns else 'سن'
|
| 1447 |
+
age_values = pd.to_numeric(filtered_farm_df[age_column], errors='coerce')
|
| 1448 |
+
st.metric("میانگین سن", f"{age_values.mean():.1f} سال")
|
| 1449 |
|
| 1450 |
with col4:
|
| 1451 |
if 'روز' in filtered_farm_df.columns:
|
|
|
|
| 1453 |
else:
|
| 1454 |
st.metric("تعداد مزارع", len(filtered_farm_df))
|
| 1455 |
|
| 1456 |
+
# نقشه مزارع
|
| 1457 |
st.subheader("نقشه مزارع")
|
| 1458 |
farm_map = create_farm_map(filtered_farm_df)
|
| 1459 |
folium_static(farm_map, width=800, height=500)
|
| 1460 |
|
| 1461 |
+
# دانلود گزارش
|
| 1462 |
st.subheader("دانلود گزارش")
|
| 1463 |
|
| 1464 |
+
# ایجاد فایل PDF
|
| 1465 |
figs = [fig_variety, fig_age]
|
| 1466 |
if fig_area:
|
| 1467 |
figs.append(fig_area)
|
|
|
|
| 1470 |
|
| 1471 |
pdf_buffer = create_report_pdf(figs, tables, "گزارش وضعیت کلی مزارع")
|
| 1472 |
|
| 1473 |
+
# دانلود PDF
|
| 1474 |
st.download_button(
|
| 1475 |
label="دانلود گزارش (PDF)",
|
| 1476 |
data=pdf_buffer,
|
|
|
|
| 1478 |
mime="application/pdf"
|
| 1479 |
)
|
| 1480 |
|
| 1481 |
+
# دانلود Excel
|
| 1482 |
excel_buffer = io.BytesIO()
|
| 1483 |
filtered_farm_df.to_excel(excel_buffer, index=False)
|
| 1484 |
excel_buffer.seek(0)
|
|
|
|
| 1493 |
elif report_type == "گزارش تحلیل شاخصها":
|
| 1494 |
st.subheader("گزارش تحلیل شاخصها")
|
| 1495 |
|
| 1496 |
+
# ایجاد دادههای نمونه برای شاخصها
|
| 1497 |
indices_data = []
|
| 1498 |
|
| 1499 |
for farm_name in selected_farms:
|
| 1500 |
+
# مقادیر پایه برای هر مزرعه
|
| 1501 |
base_ndvi = np.random.uniform(0.3, 0.8)
|
| 1502 |
base_ndwi = np.random.uniform(0.1, 0.5)
|
| 1503 |
base_evi = np.random.uniform(0.3, 0.7)
|
|
|
|
| 1505 |
base_lai = np.random.uniform(1.0, 4.0)
|
| 1506 |
base_chl = np.random.uniform(1.0, 3.0)
|
| 1507 |
|
| 1508 |
+
# ایجاد دادهها برای بازه زمانی انتخاب شده
|
| 1509 |
date_range = pd.date_range(start=start_date, end=end_date)
|
| 1510 |
|
| 1511 |
for date in date_range:
|
| 1512 |
+
# شبیهسازی تغییرات با الگوی سینوسی
|
| 1513 |
day_of_year = date.dayofyear
|
| 1514 |
seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.1
|
| 1515 |
|
|
|
|
| 1520 |
lai = base_lai + seasonal_factor * 2 + np.random.normal(0, 0.2)
|
| 1521 |
chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
|
| 1522 |
|
| 1523 |
+
# محدود کردن مقادیر
|
| 1524 |
ndvi = max(0, min(1, ndvi))
|
| 1525 |
ndwi = max(-0.5, min(0.8, ndwi))
|
| 1526 |
evi = max(0, min(1, evi))
|
|
|
|
| 1541 |
|
| 1542 |
indices_df = pd.DataFrame(indices_data)
|
| 1543 |
|
| 1544 |
+
# نمایش میانگین شاخصها برای هر مزرعه
|
| 1545 |
avg_indices = indices_df.groupby('farm_name').agg({
|
| 1546 |
'ndvi': 'mean',
|
| 1547 |
'ndwi': 'mean',
|
|
|
|
| 1566 |
hide_index=True
|
| 1567 |
)
|
| 1568 |
|
| 1569 |
+
# نمودار مقایسهای شاخصها
|
| 1570 |
st.subheader("مقایسه شاخصها بین مزارع")
|
| 1571 |
|
| 1572 |
+
# انتخاب شاخص برای نمایش
|
| 1573 |
selected_index = st.selectbox(
|
| 1574 |
"انتخاب شاخص",
|
| 1575 |
["NDVI", "NDWI", "EVI", "NDMI", "LAI", "CHL"]
|
|
|
|
| 1584 |
"CHL": "chl"
|
| 1585 |
}
|
| 1586 |
|
| 1587 |
+
# نمودار مقایسهای
|
| 1588 |
fig_compare = px.bar(
|
| 1589 |
avg_indices,
|
| 1590 |
x='farm_name',
|
|
|
|
| 1603 |
|
| 1604 |
st.plotly_chart(fig_compare, use_container_width=True)
|
| 1605 |
|
| 1606 |
+
# نمودار سری زمانی
|
| 1607 |
st.subheader("سری زمانی شاخصها")
|
| 1608 |
|
| 1609 |
+
# انتخاب مزرعه برای نمایش سری زمانی
|
| 1610 |
selected_farm_name = st.selectbox(
|
| 1611 |
"انتخاب مزرعه",
|
| 1612 |
options=selected_farms
|
| 1613 |
)
|
| 1614 |
|
| 1615 |
+
# فیلتر دادهها برای مزرعه انتخاب شده
|
| 1616 |
farm_indices = indices_df[indices_df['farm_name'] == selected_farm_name]
|
| 1617 |
|
| 1618 |
+
# نمودار سری زمانی
|
| 1619 |
fig_ts = px.line(
|
| 1620 |
farm_indices,
|
| 1621 |
x='date',
|
|
|
|
| 1633 |
|
| 1634 |
st.plotly_chart(fig_ts, use_container_width=True)
|
| 1635 |
|
| 1636 |
+
# دانلود گزارش
|
| 1637 |
st.subheader("دانلود گزارش")
|
| 1638 |
|
| 1639 |
+
# ایجاد فایل PDF
|
| 1640 |
figs = [fig_compare, fig_ts]
|
| 1641 |
tables = [avg_indices]
|
| 1642 |
|
| 1643 |
pdf_buffer = create_report_pdf(figs, tables, "گزارش تحلیل شاخصها")
|
| 1644 |
|
| 1645 |
+
# دانلود PDF
|
| 1646 |
st.download_button(
|
| 1647 |
label="دانلود گزارش (PDF)",
|
| 1648 |
data=pdf_buffer,
|
|
|
|
| 1650 |
mime="application/pdf"
|
| 1651 |
)
|
| 1652 |
|
| 1653 |
+
# دانلود Excel
|
| 1654 |
excel_buffer = io.BytesIO()
|
| 1655 |
indices_df.to_excel(excel_buffer, index=False)
|
| 1656 |
excel_buffer.seek(0)
|
|
|
|
| 1665 |
elif report_type == "گزارش تنشها":
|
| 1666 |
st.subheader("گزارش تنشها")
|
| 1667 |
|
| 1668 |
+
# ایجاد دادههای نمونه برای تنشها
|
| 1669 |
stress_data = []
|
| 1670 |
|
| 1671 |
for farm_name in selected_farms:
|
| 1672 |
+
# مقادیر پایه برای هر مزرعه
|
| 1673 |
base_ndwi = np.random.uniform(0.2, 0.5)
|
| 1674 |
base_chl = np.random.uniform(1.5, 2.5)
|
| 1675 |
|
| 1676 |
+
# آستانههای تنش
|
| 1677 |
water_stress_threshold = 0.3
|
| 1678 |
disease_threshold = 0.4
|
| 1679 |
|
| 1680 |
+
# ایجاد دادهها برای بازه زمانی انتخاب شده
|
| 1681 |
date_range = pd.date_range(start=start_date, end=end_date)
|
| 1682 |
|
| 1683 |
for date in date_range:
|
| 1684 |
day_of_year = date.dayofyear
|
| 1685 |
seasonal_factor = np.sin(day_of_year / 365 * 2 * np.pi) * 0.2
|
| 1686 |
|
| 1687 |
+
# شبیهسازی تنش آبی در روزهای خاص
|
| 1688 |
water_stress_event = False
|
| 1689 |
+
if (day_of_year % 30) < 5: # هر 30 روز، 5 روز تنش آبی
|
| 1690 |
water_stress_event = True
|
| 1691 |
ndwi = base_ndwi - 0.3 + np.random.normal(0, 0.05)
|
| 1692 |
else:
|
| 1693 |
ndwi = base_ndwi + seasonal_factor + np.random.normal(0, 0.05)
|
| 1694 |
|
| 1695 |
+
# شبیهسازی تنش بیماری در روزهای خاص
|
| 1696 |
disease_event = False
|
| 1697 |
+
if (day_of_year % 45) < 7: # هر 45 روز، 7 روز تنش بیماری
|
| 1698 |
disease_event = True
|
| 1699 |
chl = base_chl - 0.5 + np.random.normal(0, 0.1)
|
| 1700 |
else:
|
| 1701 |
chl = base_chl + seasonal_factor + np.random.normal(0, 0.1)
|
| 1702 |
|
| 1703 |
+
# محدود کردن مقادیر
|
| 1704 |
ndwi = max(-0.5, min(0.8, ndwi))
|
| 1705 |
chl = max(0.5, min(3.0, chl))
|
| 1706 |
|
|
|
|
| 1712 |
'water_stress': 1 if ndwi < water_stress_threshold else 0,
|
| 1713 |
'disease_stress': 1 if chl < disease_threshold else 0,
|
| 1714 |
'water_stress_event': water_stress_event,
|
| 1715 |
+
'disease_stress_event': disease_stress_event
|
| 1716 |
})
|
| 1717 |
|
| 1718 |
stress_df = pd.DataFrame(stress_data)
|
| 1719 |
|
| 1720 |
+
# آمار تنشها
|
| 1721 |
stress_stats = stress_df.groupby('farm_name').agg({
|
| 1722 |
'water_stress': 'sum',
|
| 1723 |
'disease_stress': 'sum'
|
|
|
|
| 1731 |
st.subheader("آمار تنشها برای هر مزرعه")
|
| 1732 |
st.dataframe(stress_stats, hide_index=True)
|
| 1733 |
|
| 1734 |
+
# نمودار مقایسهای تنشها
|
| 1735 |
fig_stress_compare = px.bar(
|
| 1736 |
stress_stats,
|
| 1737 |
x='farm_name',
|
|
|
|
| 1749 |
|
| 1750 |
st.plotly_chart(fig_stress_compare, use_container_width=True)
|
| 1751 |
|
| 1752 |
+
# نمودار سری زمانی تنشها
|
| 1753 |
st.subheader("سری زمانی تنشها")
|
| 1754 |
|
| 1755 |
+
# انتخاب مزرعه برای نمایش سری زمانی
|
| 1756 |
selected_farm_name = st.selectbox(
|
| 1757 |
"انتخاب مزرعه",
|
| 1758 |
options=selected_farms,
|
| 1759 |
key="farm_select_stress_report"
|
| 1760 |
)
|
| 1761 |
|
| 1762 |
+
# فیلتر دادهها برای مزرعه انتخاب شده
|
| 1763 |
farm_stress = stress_df[stress_df['farm_name'] == selected_farm_name]
|
| 1764 |
|
| 1765 |
+
# نمودار سری زمانی
|
| 1766 |
fig_stress_ts = go.Figure()
|
| 1767 |
|
| 1768 |
+
# اضافه کردن NDWI
|
| 1769 |
fig_stress_ts.add_trace(go.Scatter(
|
| 1770 |
x=farm_stress['date'],
|
| 1771 |
y=farm_stress['ndwi'],
|
|
|
|
| 1773 |
line=dict(color='blue')
|
| 1774 |
))
|
| 1775 |
|
| 1776 |
+
# اضافه کردن CHL
|
| 1777 |
fig_stress_ts.add_trace(go.Scatter(
|
| 1778 |
x=farm_stress['date'],
|
| 1779 |
y=farm_stress['chl'],
|
|
|
|
| 1782 |
yaxis='y2'
|
| 1783 |
))
|
| 1784 |
|
| 1785 |
+
# اضافه کردن خط آستانه تنش آبی
|
| 1786 |
fig_stress_ts.add_shape(
|
| 1787 |
type='line',
|
| 1788 |
x0=farm_stress['date'].min(),
|
| 1789 |
+
y0=0.3, # water_stress_threshold
|
| 1790 |
x1=farm_stress['date'].max(),
|
| 1791 |
y1=0.3,
|
| 1792 |
line=dict(color='red', dash='dash')
|
| 1793 |
)
|
| 1794 |
|
| 1795 |
+
# اضافه کردن خط آستانه تنش بیماری
|
| 1796 |
fig_stress_ts.add_shape(
|
| 1797 |
type='line',
|
| 1798 |
x0=farm_stress['date'].min(),
|
| 1799 |
+
y0=0.4, # disease_threshold
|
| 1800 |
x1=farm_stress['date'].max(),
|
| 1801 |
y1=0.4,
|
| 1802 |
line=dict(color='orange', dash='dash'),
|
| 1803 |
yaxis='y2'
|
| 1804 |
)
|
| 1805 |
|
| 1806 |
+
# تنظیمات نمودار
|
| 1807 |
fig_stress_ts.update_layout(
|
| 1808 |
title=f'سری زمانی تنشها برای {selected_farm_name}',
|
| 1809 |
font_family="Vazirmatn",
|
|
|
|
| 1834 |
|
| 1835 |
st.plotly_chart(fig_stress_ts, use_container_width=True)
|
| 1836 |
|
| 1837 |
+
# جدول روزهای تنش
|
| 1838 |
st.subheader("روزهای تنش")
|
| 1839 |
|
| 1840 |
+
# فیلتر روزهای تنش
|
| 1841 |
stress_days = farm_stress[(farm_stress['water_stress'] == 1) | (farm_stress['disease_stress'] == 1)]
|
| 1842 |
stress_days = stress_days[['date', 'ndwi', 'chl', 'water_stress', 'disease_stress']]
|
| 1843 |
|
|
|
|
| 1856 |
else:
|
| 1857 |
st.info("هیچ روز تنشی برای این مزرعه در بازه زمانی انتخاب شده یافت نشد.")
|
| 1858 |
|
| 1859 |
+
# دانلود گزارش
|
| 1860 |
st.subheader("دانلود گزارش")
|
| 1861 |
|
| 1862 |
+
# ایجاد فایل PDF
|
| 1863 |
figs = [fig_stress_compare, fig_stress_ts]
|
| 1864 |
tables = [stress_stats, stress_days]
|
| 1865 |
|
| 1866 |
pdf_buffer = create_report_pdf(figs, tables, "گزارش تنشها")
|
| 1867 |
|
| 1868 |
+
# دانلود PDF
|
| 1869 |
st.download_button(
|
| 1870 |
label="دانلود گزارش (PDF)",
|
| 1871 |
data=pdf_buffer,
|
|
|
|
| 1873 |
mime="application/pdf"
|
| 1874 |
)
|
| 1875 |
|
| 1876 |
+
# دانلود Excel
|
| 1877 |
excel_buffer = io.BytesIO()
|
| 1878 |
stress_df.to_excel(excel_buffer, index=False)
|
| 1879 |
excel_buffer.seek(0)
|
|
|
|
| 1888 |
elif report_type == "گزارش پیشبینی رشد":
|
| 1889 |
st.subheader("گزارش پیشبینی رشد")
|
| 1890 |
|
| 1891 |
+
# ایجاد مدل و دادههای پیشبینی
|
| 1892 |
+
model, fig_model, fig_ts, time_df, y_test, y_pred = create_prediction_model(merged_data)
|
| 1893 |
|
| 1894 |
+
# فیلتر دادهها برای مزارع انتخاب شده
|
| 1895 |
filtered_time_df = time_df[time_df['farm_id'].isin(selected_farms)]
|
| 1896 |
|
| 1897 |
+
# آمار رشد
|
| 1898 |
growth_stats = filtered_time_df.groupby('farm_id').agg({
|
| 1899 |
'height': ['first', 'last', lambda x: x.iloc[-1] - x.iloc[0]]
|
| 1900 |
})
|
|
|
|
| 1914 |
hide_index=True
|
| 1915 |
)
|
| 1916 |
|
| 1917 |
+
# نمودار مقایسهای ر��د
|
| 1918 |
fig_growth = px.bar(
|
| 1919 |
growth_stats,
|
| 1920 |
x='farm_id',
|
|
|
|
| 1933 |
|
| 1934 |
st.plotly_chart(fig_growth, use_container_width=True)
|
| 1935 |
|
| 1936 |
+
# نمودار سری زمانی رشد
|
| 1937 |
st.subheader("سری زمانی رشد")
|
| 1938 |
|
| 1939 |
+
# انتخاب مزرعه برای نمایش سری زمانی
|
| 1940 |
selected_farm_name = st.selectbox(
|
| 1941 |
"انتخاب مزرعه",
|
| 1942 |
options=selected_farms,
|
| 1943 |
key="farm_select_growth_report"
|
| 1944 |
)
|
| 1945 |
|
| 1946 |
+
# فیلتر دادهها برای مزرعه انتخاب شده
|
| 1947 |
farm_growth = filtered_time_df[filtered_time_df['farm_id'] == selected_farm_name]
|
| 1948 |
|
| 1949 |
+
# نمودار سری زمانی
|
| 1950 |
fig_growth_ts = px.line(
|
| 1951 |
farm_growth,
|
| 1952 |
x='date',
|
|
|
|
| 1963 |
|
| 1964 |
st.plotly_chart(fig_growth_ts, use_container_width=True)
|
| 1965 |
|
| 1966 |
+
# دانلود گزارش
|
| 1967 |
st.subheader("دانلود گزارش")
|
| 1968 |
|
| 1969 |
+
# ایجاد فایل PDF
|
| 1970 |
figs = [fig_growth, fig_growth_ts]
|
| 1971 |
tables = [growth_stats]
|
| 1972 |
|
| 1973 |
pdf_buffer = create_report_pdf(figs, tables, "گزارش پیشبینی رشد")
|
| 1974 |
|
| 1975 |
+
# دانلود PDF
|
| 1976 |
st.download_button(
|
| 1977 |
label="دانلود گزارش (PDF)",
|
| 1978 |
data=pdf_buffer,
|
|
|
|
| 1980 |
mime="application/pdf"
|
| 1981 |
)
|
| 1982 |
|
| 1983 |
+
# دانلود Excel
|
| 1984 |
excel_buffer = io.BytesIO()
|
| 1985 |
filtered_time_df.to_excel(excel_buffer, index=False)
|
| 1986 |
excel_buffer.seek(0)
|
|
|
|
| 1992 |
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
| 1993 |
)
|
| 1994 |
|
| 1995 |
+
# تب تنظیمات
|
| 1996 |
with tabs[5]:
|
| 1997 |
st.header("تنظیمات")
|
| 1998 |
|
|
|
|
| 2001 |
with settings_tabs[0]:
|
| 2002 |
st.subheader("تنظیمات عمومی")
|
| 2003 |
|
| 2004 |
+
# تنظیمات ظاهری
|
| 2005 |
st.write("### تنظیمات ظاهری")
|
| 2006 |
|
| 2007 |
col1, col2 = st.columns(2)
|
|
|
|
| 2028 |
["Viridis (پیشفرض)", "Plasma", "Inferno", "Magma", "Cividis"]
|
| 2029 |
)
|
| 2030 |
|
| 2031 |
+
# تنظیمات زبان
|
| 2032 |
st.write("### تنظیمات زبان")
|
| 2033 |
|
| 2034 |
language = st.radio(
|
|
|
|
| 2036 |
["فارسی (پیشفرض)", "انگلیسی"]
|
| 2037 |
)
|
| 2038 |
|
| 2039 |
+
# تنظیمات واحدها
|
| 2040 |
st.write("### تنظیمات واحدها")
|
| 2041 |
|
| 2042 |
col1, col2 = st.columns(2)
|
|
|
|
| 2063 |
["1,234.56 (پیشفرض)", "1.234,56"]
|
| 2064 |
)
|
| 2065 |
|
| 2066 |
+
# دکمه ذخیره تنظیمات
|
| 2067 |
if st.button("ذخیره تنظیمات"):
|
| 2068 |
st.success("تنظیمات با موفقیت ذخیره شد.")
|
| 2069 |
|
| 2070 |
with settings_tabs[1]:
|
| 2071 |
st.subheader("اتصال به Google Earth Engine")
|
| 2072 |
|
| 2073 |
+
# توضیحات
|
| 2074 |
st.write("""
|
| 2075 |
برای اتصال به Google Earth Engine، از حساب سرویس زیر استفاده میشود:
|
| 2076 |
|
|
|
|
| 2078 |
- پروژه: `ee-esmaeilkiani13877`
|
| 2079 |
""")
|
| 2080 |
|
| 2081 |
+
# نمایش وضعیت اتصال
|
| 2082 |
ee_connected = initialize_earth_engine()
|
| 2083 |
|
| 2084 |
if ee_connected:
|
| 2085 |
st.success("اتصال به Google Earth Engine برقرار است.")
|
| 2086 |
|
| 2087 |
+
# دکمه تست اتصال
|
| 2088 |
if st.button("تست اتصال"):
|
| 2089 |
with st.spinner("در حال تست اتصال..."):
|
| 2090 |
+
# شبیهسازی تست اتصال
|
| 2091 |
import time
|
| 2092 |
time.sleep(2)
|
| 2093 |
st.success("اتصال با موفقیت برقرار شد.")
|
| 2094 |
else:
|
| 2095 |
st.error("اتصال به Google Earth Engine برقرار نشد. لطفاً تنظیمات را بررسی کنید.")
|
| 2096 |
|
| 2097 |
+
# تنظیمات پیشرفته
|
| 2098 |
st.write("### تنظیمات پیشرفته")
|
| 2099 |
|
| 2100 |
col1, col2 = st.columns(2)
|
|
|
|
| 2112 |
with settings_tabs[2]:
|
| 2113 |
st.subheader("مدیریت کاربران")
|
| 2114 |
|
| 2115 |
+
# ایجاد دادههای نمونه برای کاربران
|
| 2116 |
users_data = {
|
| 2117 |
'user_id': list(range(1, 6)),
|
| 2118 |
'username': ['admin', 'user1', 'user2', 'user3', 'user4'],
|
|
|
|
| 2129 |
|
| 2130 |
users_df = pd.DataFrame(users_data)
|
| 2131 |
|
| 2132 |
+
# نمایش لیست کاربران
|
| 2133 |
st.write("### لیست کاربران")
|
| 2134 |
|
| 2135 |
st.dataframe(
|
|
|
|
| 2144 |
hide_index=True
|
| 2145 |
)
|
| 2146 |
|
| 2147 |
+
# افزودن کاربر جدید
|
| 2148 |
st.write("### افزودن کاربر جدید")
|
| 2149 |
|
| 2150 |
col1, col2 = st.columns(2)
|
|
|
|
| 2163 |
else:
|
| 2164 |
st.error("لطفاً تمام فیلدها را پر کنید.")
|
| 2165 |
|
| 2166 |
+
# ویرایش دسترسیها
|
| 2167 |
st.write("### مدیریت دسترسیها")
|
| 2168 |
|
| 2169 |
+
# جدول دسترسیها
|
| 2170 |
access_data = {
|
| 2171 |
'role': ['مدیر', 'کارشناس', 'کاربر'],
|
| 2172 |
'dashboard': ['خواندن/نوشتن', 'خواندن/نوشتن', 'خواندن'],
|
|
|
|
| 2193 |
hide_index=True
|
| 2194 |
)
|
| 2195 |
|
| 2196 |
+
# دکمه ویرایش دسترسیها
|
| 2197 |
if st.button("ویرایش دسترسیها"):
|
| 2198 |
st.info("برای ویرایش دسترسیها، لطفاً با مدیر سیستم تماس بگیرید.")
|
| 2199 |
|
| 2200 |
+
# نمایش اطلاعات نسخه
|
| 2201 |
+
st.sidebar.markdown("---")
|
| 2202 |
+
st.sidebar.info("نسخه 1.0.0")
|
| 2203 |
+
st.sidebar.info("توسعه داده شده با Streamlit")
|
| 2204 |
st.sidebar.markdown("---")
|
| 2205 |
st.sidebar.info("نسخه 1.0.0")
|
| 2206 |
st.sidebar.info("توسعه داده شده با Streamlit")
|