Spaces:
Running
Running
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
import plotly.express as px | |
import plotly.graph_objects as go | |
from datetime import datetime, timedelta | |
import calendar | |
from PIL import Image | |
import base64 | |
import io | |
import os | |
import ee | |
import json | |
import folium | |
from streamlit_folium import folium_static | |
import time | |
# تنظیمات صفحه | |
st.set_page_config( | |
page_title="سامانه پایش مزارع نیشکر دهخدا", | |
page_icon="🌱", | |
layout="wide", | |
initial_sidebar_state="expanded" | |
) | |
# CSS برای فارسیسازی و بهبود ظاهر | |
st.markdown(""" | |
<style> | |
@font-face { | |
font-family: 'IRANSans'; | |
src: url('https://cdn.jsdelivr.net/gh/rastikerdar/[email protected]/dist/Sahel-FD.ttf') format('truetype'); | |
} | |
* { | |
font-family: 'IRANSans', 'Sahel', sans-serif !important; | |
} | |
.main { | |
direction: rtl; | |
text-align: right; | |
} | |
.stButton button { | |
background-color: #1e6b45; | |
color: white; | |
border-radius: 8px; | |
padding: 10px 24px; | |
transition: all 0.3s; | |
} | |
.stButton button:hover { | |
background-color: #2a8c5c; | |
transform: translateY(-2px); | |
box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
} | |
h1, h2, h3 { | |
color: #1e6b45; | |
} | |
.card { | |
background-color: #f8f9fa; | |
border-radius: 10px; | |
padding: 20px; | |
box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
margin-bottom: 20px; | |
} | |
.status-good { | |
background-color: rgba(0, 200, 83, 0.2); | |
color: #00694b; | |
padding: 5px 10px; | |
border-radius: 5px; | |
font-weight: bold; | |
} | |
.status-medium { | |
background-color: rgba(255, 214, 0, 0.2); | |
color: #b7940a; | |
padding: 5px 10px; | |
border-radius: 5px; | |
font-weight: bold; | |
} | |
.status-bad { | |
background-color: rgba(255, 61, 0, 0.2); | |
color: #c62828; | |
padding: 5px 10px; | |
border-radius: 5px; | |
font-weight: bold; | |
} | |
.metric-card { | |
padding: 20px; | |
border-radius: 10px; | |
text-align: center; | |
box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
transition: transform 0.3s; | |
} | |
.metric-card:hover { | |
transform: translateY(-5px); | |
} | |
.metric-green { | |
background-color: rgba(0, 200, 83, 0.2); | |
border-left: 5px solid #00c853; | |
} | |
.metric-yellow { | |
background-color: rgba(255, 214, 0, 0.2); | |
border-left: 5px solid #ffd600; | |
} | |
.metric-red { | |
background-color: rgba(255, 61, 0, 0.2); | |
border-left: 5px solid #ff3d00; | |
} | |
.metric-blue { | |
background-color: rgba(33, 150, 243, 0.2); | |
border-left: 5px solid #2196f3; | |
} | |
.stTabs [data-baseweb="tab-list"] { | |
gap: 10px; | |
} | |
.stTabs [data-baseweb="tab"] { | |
background-color: #f0f2f6; | |
border-radius: 8px 8px 0px 0px; | |
padding: 10px 20px; | |
margin-top: 5px; | |
} | |
.stTabs [aria-selected="true"] { | |
background-color: #1e6b45 !important; | |
color: white !important; | |
} | |
input[type="number"] { | |
direction: ltr !important; | |
text-align: left !important; | |
} | |
.dataframe { | |
direction: ltr !important; | |
} | |
.dataframe th, .dataframe td { | |
text-align: left !important; | |
} | |
.map-container { | |
height: 500px; | |
width: 100%; | |
border-radius: 10px; | |
box-shadow: 0 4px 10px rgba(0,0,0,0.1); | |
overflow: hidden; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# تنظیم Earth Engine (بدون تغییر) | |
def initialize_earth_engine(): | |
try: | |
credentials_path = "ee-esmaeilkiani13877-cfdea6eaf411.json" | |
if not os.path.exists(credentials_path): | |
credentials_dict = { | |
"type": "service_account", | |
"project_id": "ee-esmaeilkiani13877", | |
"private_key_id": "cfdea6eaf411", | |
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDJ/yKp8y/jQBPM\n4YVhfMJKTFMgpKqnHjFm/HYxs9JZ3TbYGcIm1B+p61mFzc0YeZ80K7it6C9I+PKe\n8n9HnVjzTKHkLIl38OKIv8sxB2Z8Imi/aZl1Xq+ilcJ7gXU2NwvfCPOLjqYZrYKX\ntV4SJAJ8BvnPi9NMlMIwYeI4q+S8e/2XtYs/91uFQVQcDzIBZHKj8QIzfRTUPkWS\nR8C+QyGXcWJ+Z0qbkH5re+h1+qR55XVkmAj3R7DHx+Z0D6KmPGCKlW+qp07KjDcK\nB0JfG1IZvbXlJD7HJ3A9mM8LAZotGI/1sFMIFSvUopV2QC6nffn8GYxeIGIiStO0\nUwCvxEtnAgMBAAECggEAGAHplcHENLgWfxNb0goGCFrUplS+Ub4AOtCyWIwIzz+4\nEYnwPSNXUQdWuG6JS9EqIzrD0UY/yVQNtPCK6QrkbNpXgyUPdCMvQToLBi+XrS0M\nq2/yIu1mzDL8J5khG1zRjdtHOfLm3xgEcJ6h4c5bjGiKlK5F9tfgYRBEBnTG3m9X\nvSu/j3wlR01XEE8rYOUbzlq0FLHLh+XEPPcsHqmUKY6dGP/8LrHPL+QBwzZ9RcFU\nOHhc0NBaxkRb4vF8UEjA9PpYEMBg90UfaF9Jnkf+A3CyTNkeKvmIcHgwIR3cyM/s\nBCu8p9MpaBcKKT5gQJvk2A4WOrJz5Ag1tYgp7OeOQQKBgQD5mwxh3k1PcZ9Ugubn\ndIrJ3aLRpdhbQYoCyzQqffNp9OX6NqNBu4dDMe4+QFBM6XTYNyj4vqLQk7NQ8kh5\nP/xnW4L3PBhPzkPIxP0wnqwTbgRREyCfVpWy6JHcUQP508VBaXQr5P6Tk/UDpCp7\nMx2FsQCeQhNPGVQmPzwYdTrdFwKBgQDPQb3+1DCRwY5NjKf6/LgIcO9LHvZ1mM0u\nK05iGDXd7XA3gQBQw+e5OXUy8Z75Ql0DD0evXws9liVYDkrxsyLJ1tBg+hmxbYnK\nO8anx4FyRCMO3evbDD/aJhZxiUbFNnwX3tvPOcDsWFQaAzWM2NAF9JUiK6ixSKSi\nzp/qbfJxIQKBgFx0QyFfEhPqxieyPezLJEXhY9ernxVMzxIEX7CnDQvj6nR2j4v0\nJw/l+rfji1ZvOV0N5vhlQylV1jdAv6XEVYGYFTZqW/d1g+1wLTdT4jxDWkVKFJXK\nodoeJvoyAgYTc8Khh3Pjp7BOFRmqmgzQlRwxIOjVDYwkQsIODhvbhnF3AoGAO3i3\n3QZCgzsVmNUZXUXgjpZ/PSfHAQ/zVwTAlTKCqzAmkCd93OBGjVk0AEBI5YK1D/Vx\nwK1Qj8USlD+UWFH+XmJlKzM0HKsrU1XT9+ssK/qTJImTiUzEzHSqqOgBSCJMHmYZ\nHnOBLfM1+jDxhjMRQKy5JFZyWXUNKNLZ9M9G6KECgYAsbrZKYCC6PaeN4ETP6zw7\nCB42mPH5pJDlzUy1b5CuYXJS9QbpyHsqTUhiWnMm7jJ7TOWxXiGK8UE3+aJFCygB\nxXHxLmqDc5yLqVyatk7xbpOLweUNbiPeSR8HBQjPxjqHupA3WRg1j7gNdxOcq/YH\n5r1SuNjhWf8sJJrZdK/V5A==\n-----END PRIVATE KEY-----\n", | |
"client_email": "dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com", | |
"client_id": "112129892578518408444", | |
"auth_uri": "https://accounts.google.com/o/oauth2/auth", | |
"token_uri": "https://oauth2.googleapis.com/token", | |
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", | |
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dehkhodamap-e9f0da4ce9f6514021%40ee-esmaeilkiani13877.iam.gserviceaccount.com" | |
} | |
with open(credentials_path, 'w') as f: | |
json.dump(credentials_dict, f) | |
credentials = ee.ServiceAccountCredentials( | |
"dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com", | |
credentials_path | |
) | |
ee.Initialize(credentials) | |
return True | |
except Exception as e: | |
st.error(f"خطا در اتصال به Earth Engine: {e}") | |
return False | |
earth_engine_initialized = initialize_earth_engine() | |
# نمونه داده | |
def generate_sample_data(): | |
farm_data = { | |
'Farm_ID': [f'{i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)], | |
'Farm_Name': [f'مزرعه {i:02d}-{j:02d}' for i in range(1, 5) for j in range(20, 30)], | |
'Channel_ID': [f'کانال {(i-1)*10 + j%10 + 1}' for i in range(1, 5) for j in range(20, 30)], | |
'Administration_ID': [i for i in range(1, 5) for _ in range(10)], | |
'Administration_Name': [f'اداره {i}' for i in range(1, 5) for _ in range(10)], | |
'Production_Section': [1 if i <= 2 else 2 for i in range(1, 5) for _ in range(10)], | |
'Area': [np.random.randint(50, 200) for _ in range(40)], | |
'Variety': [np.random.choice(['CP69', 'CP73']) for _ in range(40)], | |
'Crop_Age': [np.random.randint(1, 5) for _ in range(40)] | |
} | |
farms_df = pd.DataFrame(farm_data) | |
height_data = [] | |
today = datetime.now() | |
for week in range(1, 6): | |
date = today - timedelta(days=(5-week)*7) | |
for farm_id in farms_df['Farm_ID']: | |
stations = [np.random.randint(150, 220) for _ in range(5)] | |
avg_height = int(np.mean(stations)) | |
groundwater = [np.random.randint(50, 100) for _ in range(2)] | |
sheath_moisture = np.random.randint(60, 90) | |
nitrogen = np.random.randint(20, 40) | |
height_data.append({ | |
'Measurement_ID': f"{farm_id}-W{week}", | |
'Farm_ID': farm_id, | |
'Week': week, | |
'Measurement_Date': date.strftime('%Y-%m-%d'), | |
'Height': avg_height, | |
'Station1': stations[0], | |
'Station2': stations[1], | |
'Station3': stations[2], | |
'Station4': stations[3], | |
'Station5': stations[4], | |
'Groundwater1': groundwater[0], | |
'Groundwater2': groundwater[1], | |
'Sheath_Moisture': sheath_moisture, | |
'Nitrogen': nitrogen | |
}) | |
heights_df = pd.DataFrame(height_data) | |
weekly_report_data = [] | |
for farm_id in farms_df['Farm_ID']: | |
farm_heights = heights_df[heights_df['Farm_ID'] == farm_id].sort_values('Week') | |
for week in range(1, 6): | |
current_week_data = farm_heights[farm_heights['Week'] == week] | |
if current_week_data.empty: | |
continue | |
current_height = current_week_data['Height'].values[0] | |
if week > 1: | |
prev_week_data = farm_heights[farm_heights['Week'] == week-1] | |
if not prev_week_data.empty: | |
prev_height = prev_week_data['Height'].values[0] | |
growth_change = current_height - prev_height | |
else: | |
growth_change = 0 | |
else: | |
growth_change = 0 | |
all_farms_this_week = heights_df[heights_df['Week'] == week] | |
avg_height_all_farms = all_farms_this_week['Height'].mean() | |
if current_height > avg_height_all_farms + 5: | |
growth_status = 'خوب' | |
elif current_height < avg_height_all_farms - 5: | |
growth_status = 'ضعیف' | |
else: | |
growth_status = 'متوسط' | |
weekly_report_data.append({ | |
'Report_ID': f"{farm_id}-R{week}", | |
'Farm_ID': farm_id, | |
'Week': week, | |
'Average_Height': current_height, | |
'Growth_Change': growth_change, | |
'Growth_Status': growth_status, | |
'Regional_Average': int(avg_height_all_farms) | |
}) | |
weekly_report_df = pd.DataFrame(weekly_report_data) | |
return farms_df, heights_df, weekly_report_df | |
farms_df, heights_df, weekly_report_df = generate_sample_data() | |
report_with_details = pd.merge(weekly_report_df, farms_df, on='Farm_ID') | |
latest_week = heights_df['Week'].max() | |
latest_reports = report_with_details[report_with_details['Week'] == latest_week] | |
# مدیریت وضعیت برنامه | |
if 'selected_tab' not in st.session_state: | |
st.session_state.selected_tab = "ورود اطلاعات" | |
if 'selected_day' not in st.session_state: | |
st.session_state.selected_day = "شنبه" | |
if 'farm_data' not in st.session_state: | |
st.session_state.farm_data = {} | |
if 'ndvi_map' not in st.session_state: | |
st.session_state.ndvi_map = None | |
# تابع برای محاسبه میانگین ارتفاع | |
def calculate_average_height(row): | |
stations = [row[f'ایستگاه {i}'] for i in range(1, 6) if f'ایستگاه {i}' in row and row[f'ایستگاه {i}'] is not None] | |
valid_stations = [s for s in stations if s > 0] | |
return sum(valid_stations) / len(valid_stations) if valid_stations else 0 | |
# تابع برای ایجاد نقشه NDVI (بدون تغییر) | |
def create_ndvi_map(farm_id=None, date=None): | |
if not earth_engine_initialized: | |
st.warning("اتصال به Earth Engine برقرار نشد. نقشه NDVI نمایش داده نمیشود.") | |
return None | |
try: | |
center_point = [31.3183, 48.6706] | |
m = folium.Map(location=center_point, zoom_start=10) | |
if farm_id: | |
farm = farms_df[farms_df['Farm_ID'] == farm_id].iloc[0] | |
lat = 31.3183 + (int(farm_id.split('-')[0]) - 1) * 0.05 | |
lon = 48.6706 + (int(farm_id.split('-')[1]) - 20) * 0.05 | |
if date: | |
date_obj = datetime.strptime(date, '%Y-%m-%d') | |
start_date = date_obj - timedelta(days=5) | |
end_date = date_obj + timedelta(days=5) | |
start_date_str = start_date.strftime('%Y-%m-%d') | |
end_date_str = end_date.strftime('%Y-%m-%d') | |
else: | |
start_date_str = '2023-01-01' | |
end_date_str = '2023-12-31' | |
region = ee.Geometry.Point([lon, lat]).buffer(1000) | |
s2 = ee.ImageCollection('COPERNICUS/S2_SR') \ | |
.filterDate(start_date_str, end_date_str) \ | |
.filterBounds(region) \ | |
.sort('CLOUD_COVERAGE_ASSESSMENT') \ | |
.first() | |
ndvi = s2.normalizedDifference(['B8', 'B4']).rename('NDVI') | |
ndvi_viz = { | |
'min': -0.2, | |
'max': 0.8, | |
'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#d9ef8b', '#a6d96a', '#66bd63', '#1a9850'] | |
} | |
map_id_dict = ee.Image(ndvi).getMapId(ndvi_viz) | |
folium.TileLayer( | |
tiles=map_id_dict['tile_fetcher'].url_format, | |
attr='Google Earth Engine', | |
name='NDVI', | |
overlay=True, | |
control=True | |
).add_to(m) | |
folium.Marker( | |
location=[lat, lon], | |
popup=f'مزرعه {farm_id}', | |
icon=folium.Icon(color='green', icon='leaf') | |
).add_to(m) | |
m.fit_bounds([[lat-0.05, lon-0.05], [lat+0.05, lon+0.05]]) | |
folium.LayerControl().add_to(m) | |
return m | |
except Exception as e: | |
st.error(f"خطا در ایجاد نقشه NDVI: {e}") | |
return None | |
# منوی کناری (بدون تغییر) | |
with st.sidebar: | |
st.image("https://via.placeholder.com/150x150.png?text=لوگو", width=150) | |
st.title("سامانه پایش مزارع نیشکر") | |
st.subheader("شرکت کشت و صنعت نیشکر دهخدا") | |
menu = st.selectbox( | |
"انتخاب صفحه", | |
["داشبورد", "ورود اطلاعات", "گزارشگیری", "تحلیل دادهها", "تنظیمات"] | |
) | |
st.session_state.selected_tab = menu | |
st.markdown("---") | |
st.markdown("### راهنمای وضعیت رشد") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.markdown('<div class="status-good">خوب</div>', unsafe_allow_html=True) | |
st.markdown('<div class="status-medium">متوسط</div>', unsafe_allow_html=True) | |
st.markdown('<div class="status-bad">ضعیف</div>', unsafe_allow_html=True) | |
with col2: | |
st.write("بالاتر از میانگین") | |
st.write("نزدیک به میانگین") | |
st.write("پایینتر از میانگین") | |
st.markdown("---") | |
st.info("نسخه آزمایشی 1.0") | |
# داشبورد (بدون تغییر) | |
if st.session_state.selected_tab == "داشبورد": | |
st.title("داشبورد پایش مزارع نیشکر") | |
st.subheader("وضعیت کلی مزارع") | |
status_counts = latest_reports['Growth_Status'].value_counts() | |
good_count = status_counts.get('خوب', 0) | |
medium_count = status_counts.get('متوسط', 0) | |
bad_count = status_counts.get('ضعیف', 0) | |
col1, col2, col3, col4 = st.columns(4) | |
with col1: | |
st.markdown('<div class="metric-card metric-green">'f'<h2>{good_count}</h2>''<p>مزارع با وضعیت خوب</p>''</div>', unsafe_allow_html=True) | |
with col2: | |
st.markdown('<div class="metric-card metric-yellow">'f'<h2>{medium_count}</h2>''<p>مزارع با وضعیت متوسط</p>''</div>', unsafe_allow_html=True) | |
with col3: | |
st.markdown('<div class="metric-card metric-red">'f'<h2>{bad_count}</h2>''<p>مزارع با وضعیت ضعیف</p>''</div>', unsafe_allow_html=True) | |
with col4: | |
avg_height = int(latest_reports['Average_Height'].mean()) | |
st.markdown('<div class="metric-card metric-blue">'f'<h2>{avg_height} سانتیمتر</h2>''<p>میانگین ارتفاع هفته جاری</p>''</div>', unsafe_allow_html=True) | |
st.markdown("---") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.subheader("میانگین ارتفاع به تفکیک اداره") | |
admin_heights = latest_reports.groupby('Administration_Name')['Average_Height'].mean().reset_index() | |
admin_heights['Average_Height'] = admin_heights['Average_Height'].astype(int) | |
fig = px.bar( | |
admin_heights, | |
x='Administration_Name', | |
y='Average_Height', | |
color='Average_Height', | |
color_continuous_scale=[(0, "red"), (0.5, "yellow"), (1, "green")], | |
labels={'Administration_Name': 'اداره', 'Average_Height': 'میانگین ارتفاع (سانتیمتر)'} | |
) | |
fig.update_layout(height=400, xaxis_title="اداره", yaxis_title="میانگین ارتفاع (سانتیمتر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)') | |
st.plotly_chart(fig, use_container_width=True) | |
with col2: | |
st.subheader("مقایسه واریتهها") | |
variety_heights = latest_reports.groupby('Variety')['Average_Height'].mean().reset_index() | |
variety_heights['Average_Height'] = variety_heights['Average_Height'].astype(int) | |
fig = px.bar( | |
variety_heights, | |
x='Variety', | |
y='Average_Height', | |
color='Variety', | |
color_discrete_sequence=["#00c853", "#2196f3"], | |
labels={'Variety': 'واریته', 'Average_Height': 'میانگین ارتفاع (سانتیمتر)'} | |
) | |
fig.update_layout(height=400, xaxis_title="واریته", yaxis_title="میانگین ارتفاع (سانتیمتر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)') | |
st.plotly_chart(fig, use_container_width=True) | |
st.subheader("روند میانگین ارتفاع هفتگی") | |
weekly_avg = report_with_details.groupby('Week')['Average_Height'].mean().reset_index() | |
weekly_avg['Average_Height'] = weekly_avg['Average_Height'].astype(int) | |
fig = px.line( | |
weekly_avg, | |
x='Week', | |
y='Average_Height', | |
markers=True, | |
line_shape='spline', | |
labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتیمتر)'} | |
) | |
fig.update_traces(line=dict(width=3, color="#1e6b45"), marker=dict(size=10)) | |
fig.update_layout(height=400, xaxis_title="هفته", yaxis_title="میانگین ارتفاع (سانتیمتر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)') | |
st.plotly_chart(fig, use_container_width=True) | |
st.markdown("---") | |
st.subheader("نقشه NDVI") | |
col1, col2 = st.columns(2) | |
with col1: | |
selected_farm_for_map = st.selectbox( | |
"انتخاب مزرعه", | |
farms_df['Farm_ID'].tolist(), | |
format_func=lambda x: farms_df[farms_df['Farm_ID'] == x]['Farm_Name'].iloc[0] | |
) | |
with col2: | |
latest_date = heights_df[heights_df['Farm_ID'] == selected_farm_for_map].sort_values('Week', ascending=False)['Measurement_Date'].iloc[0] | |
selected_date_for_map = st.date_input( | |
"انتخاب تاریخ", | |
value=datetime.strptime(latest_date, '%Y-%m-%d'), | |
max_value=datetime.now() | |
) | |
if st.button("نمایش نقشه NDVI"): | |
with st.spinner("در حال تولید نقشه..."): | |
ndvi_map = create_ndvi_map(selected_farm_for_map, selected_date_for_map.strftime('%Y-%m-%d')) | |
st.session_state.ndvi_map = ndvi_map | |
if st.session_state.ndvi_map: | |
st.markdown('<div class="map-container">', unsafe_allow_html=True) | |
folium_static(st.session_state.ndvi_map, width=1200, height=500) | |
st.markdown('</div>', unsafe_allow_html=True) | |
farm_info = farms_df[farms_df['Farm_ID'] == selected_farm_for_map].iloc[0] | |
farm_measurements = heights_df[(heights_df['Farm_ID'] == selected_farm_for_map) & | |
(heights_df['Measurement_Date'] == latest_date)].iloc[0] | |
st.markdown(f""" | |
### اطلاعات مزرعه {farm_info['Farm_Name']} | |
**اداره:** {farm_info['Administration_Name']} | **کانال:** {farm_info['Channel_ID']} | **واریته:** {farm_info['Variety']} | **سن کشت:** {farm_info['Crop_Age']} سال | |
**آخرین اندازهگیری:** {latest_date} | |
- **میانگین ارتفاع:** {farm_measurements['Height']} سانتیمتر | |
- **رطوبت غلاف:** {farm_measurements['Sheath_Moisture']}% | |
- **نیتروژن:** {farm_measurements['Nitrogen']}% | |
""") | |
else: | |
st.info("برای نمایش نقشه NDVI، یک مزرعه و تاریخ انتخاب کنید.") | |
st.markdown("---") | |
st.subheader("مزارع با بیشترین و کمترین رشد") | |
col1, col2 = st.columns(2) | |
with col1: | |
st.markdown("#### بیشترین رشد هفتگی") | |
top_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change', ascending=False).head(5) | |
for _, row in top_growth.iterrows(): | |
growth_color = "green" if row['Growth_Change'] > 0 else "red" | |
st.markdown(f""" | |
<div style="border: 1px solid #eee; padding: 10px; border-radius: 5px; margin-bottom: 10px;"> | |
<h4 style="margin: 0;">{row['Farm_Name']} (اداره {row['Administration_ID']})</h4> | |
<p style="margin: 5px 0;">ارتفاع: {row['Average_Height']} سانتیمتر</p> | |
<p style="color: {growth_color}; font-weight: bold; margin: 0;"> | |
تغییر: {'+' if row['Growth_Change'] > 0 else ''}{row['Growth_Change']} سانتیمتر | |
</p> | |
</div> | |
""", unsafe_allow_html=True) | |
with col2: | |
st.markdown("#### کمترین رشد هفتگی") | |
bottom_growth = report_with_details[report_with_details['Week'] == latest_week].sort_values('Growth_Change').head(5) | |
for _, row in bottom_growth.iterrows(): | |
growth_color = "green" if row['Growth_Change'] > 0 else "red" | |
st.markdown(f""" | |
<div style="border: 1px solid #eee; padding: 10px; border-radius: 5px; margin-bottom: 10px;"> | |
<h4 style="margin: 0;">{row['Farm_Name']} (اداره {row['Administration_ID']})</h4> | |
<p style="margin: 5px 0;">ارتفاع: {row['Average_Height']} سانتیمتر</p> | |
<p style="color: {growth_color}; font-weight: bold; margin: 0;"> | |
تغییر: {'+' if row['Growth_Change'] > 0 else ''}{row['Growth_Change']} سانتیمتر | |
</p> | |
</div> | |
""", unsafe_allow_html=True) | |
# بخش ورود اطلاعات (بهبود یافته) | |
elif st.session_state.selected_tab == "ورود اطلاعات": | |
st.title("ورود اطلاعات روزانه") | |
# انتخاب روز هفته | |
days_of_week = ["شنبه", "یکشنبه", "دوشنبه", "سهشنبه", "چهارشنبه", "پنجشنبه"] | |
selected_day = st.selectbox("انتخاب روز", days_of_week, index=days_of_week.index(st.session_state.selected_day)) | |
st.session_state.selected_day = selected_day | |
# انتخاب اداره | |
selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique())) | |
# فیلتر مزارع بر اساس اداره انتخابشده | |
filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin].copy() | |
# تعریف ستونهای جدول | |
columns = { | |
'مزرعه': filtered_farms['Farm_Name'], | |
'ایستگاه 1': [0] * len(filtered_farms), | |
'ایستگاه 2': [0] * len(filtered_farms), | |
'ایستگاه 3': [0] * len(filtered_farms), | |
'ایستگاه 4': [0] * len(filtered_farms), | |
'ایستگاه 5': [0] * len(filtered_farms), | |
'چاهک 1': [0] * len(filtered_farms), | |
'چاهک 2': [0] * len(filtered_farms), | |
'رطوبت غلاف': [0] * len(filtered_farms), | |
'نیتروژن': [0] * len(filtered_farms), | |
'میانگین ارتفاع': [0] * len(filtered_farms) | |
} | |
data_df = pd.DataFrame(columns) | |
# بارگذاری دادههای قبلی (اگر وجود داشته باشد) | |
if selected_day in st.session_state.farm_data: | |
for idx, farm in filtered_farms.iterrows(): | |
farm_id = farm['Farm_ID'] | |
if farm_id in st.session_state.farm_data[selected_day]: | |
saved_data = st.session_state.farm_data[selected_day][farm_id] | |
for col in ['ایستگاه 1', 'ایستگاه 2', 'ایستگاه 3', 'ایستگاه 4', 'ایستگاه 5', 'چاهک 1', 'چاهک 2', 'رطوبت غلاف', 'نیتروژن']: | |
if col in saved_data: | |
data_df.at[idx, col] = saved_data[col] | |
data_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(data_df.iloc[idx]) | |
# نمایش جدول ورود دادهها | |
st.subheader(f"ورود دادههای روز {selected_day} - اداره {selected_admin}") | |
edited_df = st.data_editor( | |
data_df, | |
num_rows="fixed", | |
key=f"data_editor_{selected_day}_{selected_admin}", | |
hide_index=True, | |
column_config={ | |
"مزرعه": st.column_config.TextColumn("مزرعه", disabled=True), | |
"ایستگاه 1": st.column_config.NumberColumn("ایستگاه 1", min_value=0, max_value=300, step=1), | |
"ایستگاه 2": st.column_config.NumberColumn("ایستگاه 2", min_value=0, max_value=300, step=1), | |
"ایستگاه 3": st.column_config.NumberColumn("ایستگاه 3", min_value=0, max_value=300, step=1), | |
"ایستگاه 4": st.column_config.NumberColumn("ایستگاه 4", min_value=0, max_value=300, step=1), | |
"ایستگاه 5": st.column_config.NumberColumn("ایستگاه 5", min_value=0, max_value=300, step=1), | |
"چاهک 1": st.column_config.NumberColumn("چاهک 1", min_value=0, max_value=300, step=1), | |
"چاهک 2": st.column_config.NumberColumn("چاهک 2", min_value=0, max_value=300, step=1), | |
"رطوبت غلاف": st.column_config.NumberColumn("رطوبت غلاف", min_value=0, max_value=100, step=1), | |
"نیتروژن": st.column_config.NumberColumn("نیتروژن", min_value=0, max_value=100, step=1), | |
"میانگین ارتفاع": st.column_config.NumberColumn("میانگین ارتفاع", disabled=True) | |
}, | |
use_container_width=True | |
) | |
# محاسبه داینامیک میانگین ارتفاع | |
for idx, row in edited_df.iterrows(): | |
edited_df.at[idx, 'میانگین ارتفاع'] = calculate_average_height(row) | |
# نمایش خلاصه داینامیک | |
st.markdown("---") | |
st.subheader("خلاصه وضعیت دادههای واردشده") | |
col1, col2, col3, col4 = st.columns(4) | |
with col1: | |
total_avg = edited_df['میانگین ارتفاع'].mean() | |
st.metric("میانگین کل ارتفاع", f"{total_avg:.1f} سانتیمتر" if total_avg > 0 else "0") | |
with col2: | |
farms_entered = len(edited_df[edited_df['میانگین ارتفاع'] > 0]) | |
st.metric("تعداد مزارع واردشده", f"{farms_entered} از {len(filtered_farms)}") | |
with col3: | |
avg_moisture = edited_df['رطوبت غلاف'].mean() | |
st.metric("میانگین رطوبت غلاف", f"{avg_moisture:.1f}%" if avg_moisture > 0 else "0") | |
with col4: | |
avg_nitrogen = edited_df['نیتروژن'].mean() | |
st.metric("میانگین نیتروژن", f"{avg_nitrogen:.1f}%" if avg_nitrogen > 0 else "0") | |
# دکمه ذخیره | |
if st.button("ذخیره اطلاعات"): | |
if selected_day not in st.session_state.farm_data: | |
st.session_state.farm_data[selected_day] = {} | |
for idx, row in edited_df.iterrows(): | |
farm_id = filtered_farms.iloc[idx]['Farm_ID'] | |
st.session_state.farm_data[selected_day][farm_id] = { | |
'ایستگاه 1': row['ایستگاه 1'], | |
'ایستگاه 2': row['ایستگاه 2'], | |
'ایستگاه 3': row['ایستگاه 3'], | |
'ایستگاه 4': row['ایستگاه 4'], | |
'ایستگاه 5': row['ایستگاه 5'], | |
'چاهک 1': row['چاهک 1'], | |
'چاهک 2': row['چاهک 2'], | |
'رطوبت غلاف': row['رطوبت غلاف'], | |
'نیتروژن': row['نیتروژن'], | |
'میانگین ارتفاع': row['میانگین ارتفاع'] | |
} | |
st.success("دادهها با موفقیت ذخیره شدند.") | |
# گزارشگیری (بدون تغییر) | |
elif st.session_state.selected_tab == "گزارشگیری": | |
st.title("گزارشهای هفتگی") | |
report_type = st.radio( | |
"نوع گزارش", | |
["گزارش به تفکیک مزرعه", "گزارش به تفکیک اداره", "گزارش به تفکیک کانال", "مزارع با وضعیت ضعیف"] | |
) | |
st.markdown("---") | |
if report_type == "گزارش به تفکیک مزرعه": | |
st.subheader("گزارش هفتگی به تفکیک مزرعه") | |
selected_admin = st.selectbox("انتخاب اداره", sorted(farms_df['Administration_ID'].unique()), key="admin_select_farm_report") | |
filtered_farms = farms_df[farms_df['Administration_ID'] == selected_admin] | |
selected_farm_id = st.selectbox( | |
"انتخاب مزرعه", | |
filtered_farms['Farm_ID'].tolist(), | |
format_func=lambda x: filtered_farms[filtered_farms['Farm_ID'] == x]['Farm_Name'].iloc[0] | |
) | |
farm_reports = weekly_report_df[weekly_report_df['Farm_ID'] == selected_farm_id].sort_values('Week') | |
if not farm_reports.empty: | |
farm_info = filtered_farms[filtered_farms['Farm_ID'] == selected_farm_id].iloc[0] | |
col1, col2, col3 = st.columns(3) | |
col1.metric("واریته", farm_info['Variety']) | |
col2.metric("سن کشت", f"{farm_info['Crop_Age']} سال") | |
col3.metric("مساحت", f"{farm_info['Area']} هکتار") | |
st.markdown("---") | |
st.subheader("روند رشد هفتگی") | |
fig = go.Figure() | |
fig.add_trace(go.Scatter( | |
x=farm_reports['Week'], | |
y=farm_reports['Regional_Average'], | |
mode='lines+markers', | |
name='میانگین منطقه', | |
line=dict(color='gray', width=2, dash='dot'), | |
marker=dict(size=8) | |
)) | |
fig.add_trace(go.Scatter( | |
x=farm_reports['Week'], | |
y=farm_reports['Average_Height'], | |
mode='lines+markers', | |
name='ارتفاع مزرعه', | |
line=dict(color='#1e6b45', width=3), | |
marker=dict(size=10) | |
)) | |
fig.update_layout(height=400, xaxis_title="هفته", yaxis_title="ارتفاع (سانتیمتر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)') | |
st.plotly_chart(fig, use_container_width=True) | |
st.subheader("دادههای هفتگی") | |
report_table = pd.DataFrame({ | |
'هفته': farm_reports['Week'], | |
'تاریخ': farm_reports['Week'].apply(lambda x: heights_df[heights_df['Week'] == x]['Measurement_Date'].iloc[0]), | |
'ارتفاع': farm_reports['Average_Height'], | |
'میانگین منطقه': farm_reports['Regional_Average'], | |
'تغییرات': farm_reports['Growth_Change'], | |
'وضعیت': farm_reports['Growth_Status'] | |
}) | |
def status_with_color(status): | |
colors = {'خوب': '#00c853', 'متوسط': '#ffd600', 'ضعیف': '#ff3d00'} | |
return f'<span style="color: {colors.get(status, "black")};">{status}</span>' | |
report_table['وضعیت'] = report_table['وضعیت'].apply(status_with_color) | |
st.markdown(report_table.to_html(escape=False, index=False), unsafe_allow_html=True) | |
st.subheader("نقشه NDVI مزرعه") | |
if st.button("نمایش نقشه NDVI این مزرعه"): | |
with st.spinner("در حال تولید نقشه..."): | |
latest_date = farm_reports.iloc[-1]['Week'] | |
farm_date = heights_df[heights_df['Week'] == latest_date]['Measurement_Date'].iloc[0] | |
ndvi_map = create_ndvi_map(selected_farm_id, farm_date) | |
if ndvi_map: | |
st.markdown('<div class="map-container">', unsafe_allow_html=True) | |
folium_static(ndvi_map, width=1200, height=500) | |
st.markdown('</div>', unsafe_allow_html=True) | |
st.markdown("---") | |
st.subheader("توصیههای کارشناسی") | |
latest_report = farm_reports.iloc[-1] | |
latest_status = latest_report['Growth_Status'] | |
if latest_status == 'خوب': | |
st.success("وضعیت رشد مزرعه مطلوب است. ادامه برنامه فعلی توصیه میشود.") | |
elif latest_status == 'متوسط': | |
st.warning("وضعیت رشد مزرعه متوسط است. بررسی وضعیت آبیاری و تغذیه توصیه میشود.") | |
else: | |
st.error("وضعیت رشد مزرعه ضعیف است. مشکلات احتمالی:") | |
st.markdown(""" | |
- بررسی سیستم آبیاری و عدم گرفتگی | |
- بررسی وضعیت تغذیه و نیاز به کود | |
- بررسی آفات و بیماریهای احتمالی | |
- بررسی وضعیت زهکشی مزرعه | |
""") | |
# تحلیل دادهها (بدون تغییر) | |
elif st.session_state.selected_tab == "تحلیل دادهها": | |
st.title("تحلیل پیشرفته دادهها") | |
analysis_type = st.selectbox( | |
"انتخاب نوع تحلیل", | |
["تحلیل واریتهها", "تحلیل عوامل مؤثر بر رشد", "پیشبینی روند آینده", "مقایسه بخشهای تولیدی"] | |
) | |
st.markdown("---") | |
if analysis_type == "تحلیل واریتهها": | |
st.subheader("مقایسه واریتههای مختلف") | |
variety_weekly = report_with_details.groupby(['Variety', 'Week'])['Average_Height'].mean().reset_index() | |
variety_weekly['Average_Height'] = variety_weekly['Average_Height'].round().astype(int) | |
fig = px.line( | |
variety_weekly, | |
x='Week', | |
y='Average_Height', | |
color='Variety', | |
markers=True, | |
labels={'Week': 'هفته', 'Average_Height': 'میانگین ارتفاع (سانتیمتر)', 'Variety': 'واریته'} | |
) | |
fig.update_traces(line=dict(width=3), marker=dict(size=10)) | |
fig.update_layout(height=500, xaxis_title="هفته", yaxis_title="میانگین ارتفاع (سانتیمتر)", font=dict(family="IRANSans"), plot_bgcolor='rgba(0,0,0,0)') | |
st.plotly_chart(fig, use_container_width=True) |