Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -29,7 +29,7 @@ import pydeck as pdk
|
|
29 |
import math
|
30 |
from sklearn.linear_model import LinearRegression
|
31 |
|
32 |
-
# Page configuration
|
33 |
st.set_page_config(
|
34 |
page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
|
35 |
page_icon="🌿",
|
@@ -37,7 +37,7 @@ st.set_page_config(
|
|
37 |
initial_sidebar_state="expanded"
|
38 |
)
|
39 |
|
40 |
-
# Custom CSS with
|
41 |
st.markdown("""
|
42 |
<style>
|
43 |
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;200;300;400;500;600;700;800;900&display=swap');
|
@@ -48,39 +48,38 @@ st.markdown("""
|
|
48 |
|
49 |
/* Main container styling */
|
50 |
.main {
|
51 |
-
background: linear-gradient(135deg, #
|
52 |
}
|
53 |
|
54 |
/* Header styling */
|
55 |
.main-header {
|
56 |
background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%);
|
57 |
-
padding:
|
58 |
-
border-radius:
|
59 |
-
box-shadow: 0
|
60 |
margin-bottom: 2rem;
|
61 |
position: relative;
|
62 |
overflow: hidden;
|
63 |
-
animation:
|
64 |
}
|
65 |
|
66 |
-
@keyframes
|
67 |
-
0% { box-shadow: 0
|
68 |
-
|
|
|
69 |
}
|
70 |
|
71 |
.main-header h1 {
|
72 |
color: white;
|
73 |
font-weight: 700;
|
74 |
margin: 0;
|
75 |
-
|
76 |
-
z-index: 1;
|
77 |
}
|
78 |
|
79 |
.main-header p {
|
80 |
-
color: rgba(255, 255, 255, 0.
|
81 |
margin: 0;
|
82 |
-
|
83 |
-
z-index: 1;
|
84 |
}
|
85 |
|
86 |
/* Metric card styling */
|
@@ -88,29 +87,66 @@ st.markdown("""
|
|
88 |
display: flex;
|
89 |
justify-content: space-around;
|
90 |
flex-wrap: wrap;
|
91 |
-
gap:
|
92 |
padding: 20px;
|
93 |
}
|
94 |
|
95 |
.metric-card {
|
96 |
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
|
97 |
-
border-radius:
|
98 |
-
padding:
|
99 |
-
width:
|
100 |
text-align: center;
|
101 |
color: white;
|
102 |
-
box-shadow: 0 10px
|
103 |
-
transition:
|
|
|
|
|
104 |
}
|
105 |
|
106 |
.metric-card:hover {
|
107 |
-
transform: translateY(-10px);
|
108 |
-
box-shadow: 0 15px
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
}
|
110 |
|
111 |
-
|
112 |
-
.
|
113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
|
115 |
/* Navigation menu styling */
|
116 |
.st-emotion-cache-1lcbz7b {
|
@@ -119,38 +155,59 @@ st.markdown("""
|
|
119 |
margin-bottom: 20px !important;
|
120 |
}
|
121 |
|
122 |
-
.st-emotion-cache-
|
123 |
--hover-color: #e9f7ef !important;
|
124 |
-
border-radius:
|
125 |
font-size: 16px !important;
|
126 |
text-align: center !important;
|
127 |
margin: 0 !important;
|
|
|
128 |
}
|
129 |
|
130 |
-
.st-emotion-cache-
|
131 |
background-color: #e9f7ef !important;
|
|
|
132 |
}
|
133 |
|
134 |
-
.st-emotion-cache-
|
135 |
background-color: #2ecc71 !important;
|
136 |
color: white !important;
|
137 |
font-weight: 600 !important;
|
|
|
138 |
}
|
139 |
|
140 |
/* Button styling */
|
141 |
.stButton>button {
|
142 |
border-radius: 50px;
|
143 |
-
padding: 0.
|
144 |
font-weight: 600;
|
145 |
-
transition: all 0.3s ease;
|
146 |
-
border: none;
|
147 |
background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%);
|
148 |
color: white;
|
|
|
|
|
149 |
}
|
150 |
|
151 |
.stButton>button:hover {
|
152 |
-
transform: translateY(-
|
153 |
-
box-shadow: 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
}
|
155 |
|
156 |
/* Footer styling */
|
@@ -159,10 +216,11 @@ st.markdown("""
|
|
159 |
left: 0;
|
160 |
bottom: 0;
|
161 |
width: 100%;
|
162 |
-
background
|
163 |
color: white;
|
164 |
text-align: center;
|
165 |
-
padding:
|
|
|
166 |
}
|
167 |
</style>
|
168 |
""", unsafe_allow_html=True)
|
@@ -186,7 +244,6 @@ def load_farm_data():
|
|
186 |
'رطوبت استاندارد قبلی': 'PreviousStandardMoisture', 'چاهک 1': 'Well1', 'تاریخ قرائت': 'Well1Date',
|
187 |
'چاهک 2': 'Well2', 'تاریخ قرائت.1': 'Well2Date'
|
188 |
}, inplace=True)
|
189 |
-
# Convert numeric columns to float and handle NaN
|
190 |
numeric_cols = ['Area', 'CurrentHeight', 'PreviousHeight', 'CurrentGrowth', 'PreviousGrowth',
|
191 |
'CurrentNitrogen', 'PreviousNitrogen', 'CurrentMoisture', 'PreviousMoisture',
|
192 |
'Station1', 'Station2', 'Station3', 'Station4', 'Station5', 'Well1', 'Well2']
|
@@ -278,6 +335,15 @@ def create_ee_map(farm_id, date_str, layer_type="NDVI"):
|
|
278 |
elif layer_type == "NDMI":
|
279 |
index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
|
280 |
viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
281 |
map_id_dict = ee.Image(index).getMapId(viz_params)
|
282 |
folium.TileLayer(
|
283 |
tiles=map_id_dict['tile_fetcher'].url_format,
|
@@ -301,9 +367,30 @@ def generate_real_growth_data(selected_variety="all", selected_age="all"):
|
|
301 |
if selected_age != "all":
|
302 |
filtered_farms = filtered_farms[filtered_farms['Age'] == selected_age]
|
303 |
|
|
|
304 |
weeks = filtered_farms['Week'].unique()
|
305 |
-
|
306 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
307 |
|
308 |
# Initialize Earth Engine and load data
|
309 |
ee_initialized = initialize_earth_engine()
|
@@ -323,7 +410,7 @@ if 'heights_df' not in st.session_state:
|
|
323 |
# Main header
|
324 |
st.markdown('<div class="main-header">', unsafe_allow_html=True)
|
325 |
st.markdown('<h1>سامانه هوشمند پایش مزارع نیشکر دهخدا</h1>', unsafe_allow_html=True)
|
326 |
-
st.markdown('<p>پلتفرم جامع مدیریت، پایش و تحلیل دادههای مزارع
|
327 |
st.markdown('</div>', unsafe_allow_html=True)
|
328 |
|
329 |
# Navigation menu
|
@@ -337,7 +424,7 @@ selected = option_menu(
|
|
337 |
styles={
|
338 |
"container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "20px"},
|
339 |
"icon": {"color": "#2ecc71", "font-size": "18px"},
|
340 |
-
"nav-link": {"font-size": "16px", "text-align": "center", "margin":"0px", "--hover-color": "#e9f7ef", "border-radius": "
|
341 |
"nav-link-selected": {"background-color": "#2ecc71", "color": "white", "font-weight": "600"},
|
342 |
}
|
343 |
)
|
@@ -380,25 +467,61 @@ if selected == "داشبورد":
|
|
380 |
|
381 |
st.markdown('</div>', unsafe_allow_html=True)
|
382 |
|
383 |
-
st.
|
384 |
-
|
385 |
-
|
386 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
387 |
|
388 |
# Map Page
|
389 |
elif selected == "نقشه مزارع":
|
390 |
st.markdown("## نقشه مزارع با شاخصهای ماهوارهای")
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
|
|
|
|
|
|
402 |
|
403 |
# Data Entry Page
|
404 |
elif selected == "ورود اطلاعات":
|
@@ -406,14 +529,22 @@ elif selected == "ورود اطلاعات":
|
|
406 |
tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"])
|
407 |
|
408 |
with tab1:
|
409 |
-
|
410 |
-
|
|
|
|
|
|
|
411 |
filtered_farms = farm_df[farm_df['Week'] == int(week)]
|
412 |
if not filtered_farms.empty:
|
413 |
data_key = f"data_{week}_{day}"
|
414 |
if data_key not in st.session_state:
|
415 |
st.session_state[data_key] = pd.DataFrame({
|
416 |
'Farm_ID': filtered_farms['Farm_ID'],
|
|
|
|
|
|
|
|
|
|
|
417 |
'CurrentHeight': [0.0] * len(filtered_farms),
|
418 |
'CurrentMoisture': [0.0] * len(filtered_farms)
|
419 |
})
|
@@ -427,7 +558,7 @@ elif selected == "ورود اطلاعات":
|
|
427 |
if uploaded_file:
|
428 |
try:
|
429 |
df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
|
430 |
-
numeric_cols = ['CurrentHeight', 'CurrentMoisture']
|
431 |
for col in numeric_cols:
|
432 |
if col in df.columns:
|
433 |
df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
|
@@ -441,17 +572,48 @@ elif selected == "ورود اطلاعات":
|
|
441 |
# Data Analysis Page
|
442 |
elif selected == "تحلیل دادهها":
|
443 |
st.markdown("## تحلیل هوشمند دادهها")
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
449 |
|
450 |
# Report Generation Page
|
451 |
elif selected == "گزارشگیری":
|
452 |
st.markdown("## گزارشگیری")
|
453 |
-
report_week = st.selectbox("انتخاب هفته", [str(i) for i in range(1, 23)])
|
454 |
-
report_day = st.selectbox("انتخاب روز", day_df['Day'].unique())
|
455 |
report_df = st.session_state.heights_df[
|
456 |
(st.session_state.heights_df['Week'] == int(report_week)) &
|
457 |
(st.session_state.heights_df['Farm_ID'].isin(day_df[day_df['Day'] == report_day]['Farm_ID']))
|
@@ -459,9 +621,10 @@ elif selected == "گزارشگیری":
|
|
459 |
if not report_df.empty:
|
460 |
st.dataframe(report_df)
|
461 |
csv = report_df.to_csv(index=False).encode('utf-8')
|
462 |
-
st.download_button(label="دانلود گزارش", data=csv, file_name=f"report_week_{report_week}.csv")
|
463 |
else:
|
464 |
-
st.warning(f"دادهای برای هفته {report_week} یافت نشد.")
|
|
|
465 |
|
466 |
# Settings Page
|
467 |
elif selected == "تنظیمات":
|
@@ -469,10 +632,20 @@ elif selected == "تنظیمات":
|
|
469 |
if st.button("بارگذاری مجدد دادهها"):
|
470 |
st.session_state.heights_df = load_farm_data()
|
471 |
st.success("دادهها بهروزرسانی شدند.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
472 |
|
473 |
# Footer
|
474 |
st.markdown("""
|
475 |
<footer>
|
476 |
-
<p
|
477 |
</footer>
|
478 |
""", unsafe_allow_html=True)
|
|
|
29 |
import math
|
30 |
from sklearn.linear_model import LinearRegression
|
31 |
|
32 |
+
# Page configuration
|
33 |
st.set_page_config(
|
34 |
page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
|
35 |
page_icon="🌿",
|
|
|
37 |
initial_sidebar_state="expanded"
|
38 |
)
|
39 |
|
40 |
+
# Custom CSS with cascading panels and eye-catching reflexes
|
41 |
st.markdown("""
|
42 |
<style>
|
43 |
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;200;300;400;500;600;700;800;900&display=swap');
|
|
|
48 |
|
49 |
/* Main container styling */
|
50 |
.main {
|
51 |
+
background: linear-gradient(135deg, #f0f4f8 0%, #d9e2ec 100%);
|
52 |
}
|
53 |
|
54 |
/* Header styling */
|
55 |
.main-header {
|
56 |
background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%);
|
57 |
+
padding: 2rem;
|
58 |
+
border-radius: 15px;
|
59 |
+
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
60 |
margin-bottom: 2rem;
|
61 |
position: relative;
|
62 |
overflow: hidden;
|
63 |
+
animation: headerPulse 3s infinite ease-in-out;
|
64 |
}
|
65 |
|
66 |
+
@keyframes headerPulse {
|
67 |
+
0% { box-shadow: 0 10px 30px rgba(46, 204, 113, 0.2); }
|
68 |
+
50% { box-shadow: 0 15px 40px rgba(46, 204, 113, 0.4); }
|
69 |
+
100% { box-shadow: 0 10px 30px rgba(46, 204, 113, 0.2); }
|
70 |
}
|
71 |
|
72 |
.main-header h1 {
|
73 |
color: white;
|
74 |
font-weight: 700;
|
75 |
margin: 0;
|
76 |
+
text-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
|
|
|
77 |
}
|
78 |
|
79 |
.main-header p {
|
80 |
+
color: rgba(255, 255, 255, 0.9);
|
81 |
margin: 0;
|
82 |
+
font-size: 1.1rem;
|
|
|
83 |
}
|
84 |
|
85 |
/* Metric card styling */
|
|
|
87 |
display: flex;
|
88 |
justify-content: space-around;
|
89 |
flex-wrap: wrap;
|
90 |
+
gap: 25px;
|
91 |
padding: 20px;
|
92 |
}
|
93 |
|
94 |
.metric-card {
|
95 |
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
|
96 |
+
border-radius: 20px;
|
97 |
+
padding: 25px;
|
98 |
+
width: 240px;
|
99 |
text-align: center;
|
100 |
color: white;
|
101 |
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.15);
|
102 |
+
transition: all 0.4s ease;
|
103 |
+
position: relative;
|
104 |
+
overflow: hidden;
|
105 |
}
|
106 |
|
107 |
.metric-card:hover {
|
108 |
+
transform: translateY(-10px) scale(1.05);
|
109 |
+
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.25);
|
110 |
+
}
|
111 |
+
|
112 |
+
.metric-card::before {
|
113 |
+
content: '';
|
114 |
+
position: absolute;
|
115 |
+
top: -50%;
|
116 |
+
left: -50%;
|
117 |
+
width: 200%;
|
118 |
+
height: 200%;
|
119 |
+
background: rgba(255, 255, 255, 0.1);
|
120 |
+
transform: rotate(30deg);
|
121 |
+
transition: all 0.4s ease;
|
122 |
+
}
|
123 |
+
|
124 |
+
.metric-card:hover::before {
|
125 |
+
top: 100%;
|
126 |
+
left: 100%;
|
127 |
+
}
|
128 |
+
|
129 |
+
.metric-icon { font-size: 2.8rem; margin-bottom: 15px; animation: iconBounce 2s infinite; }
|
130 |
+
.metric-value { font-size: 2.2rem; font-weight: 700; text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.2); }
|
131 |
+
.metric-label { font-size: 1.1rem; opacity: 0.9; }
|
132 |
+
|
133 |
+
@keyframes iconBounce {
|
134 |
+
0%, 100% { transform: translateY(0); }
|
135 |
+
50% { transform: translateY(-10px); }
|
136 |
}
|
137 |
|
138 |
+
/* Dropdown menu styling */
|
139 |
+
.stSelectbox {
|
140 |
+
background: white;
|
141 |
+
border-radius: 12px;
|
142 |
+
padding: 10px;
|
143 |
+
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
144 |
+
transition: all 0.3s ease;
|
145 |
+
}
|
146 |
+
|
147 |
+
.stSelectbox:hover {
|
148 |
+
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
|
149 |
+
}
|
150 |
|
151 |
/* Navigation menu styling */
|
152 |
.st-emotion-cache-1lcbz7b {
|
|
|
155 |
margin-bottom: 20px !important;
|
156 |
}
|
157 |
|
158 |
+
.st-emotion-cache-1j7d69d {
|
159 |
--hover-color: #e9f7ef !important;
|
160 |
+
border-radius: 12px !important;
|
161 |
font-size: 16px !important;
|
162 |
text-align: center !important;
|
163 |
margin: 0 !important;
|
164 |
+
transition: all 0.3s ease;
|
165 |
}
|
166 |
|
167 |
+
.st-emotion-cache-1j7d69d:hover {
|
168 |
background-color: #e9f7ef !important;
|
169 |
+
transform: translateY(-2px);
|
170 |
}
|
171 |
|
172 |
+
.st-emotion-cache-1j7d69d[data-selected="true"] {
|
173 |
background-color: #2ecc71 !important;
|
174 |
color: white !important;
|
175 |
font-weight: 600 !important;
|
176 |
+
box-shadow: 0 5px 15px rgba(46, 204, 113, 0.3);
|
177 |
}
|
178 |
|
179 |
/* Button styling */
|
180 |
.stButton>button {
|
181 |
border-radius: 50px;
|
182 |
+
padding: 0.7rem 2rem;
|
183 |
font-weight: 600;
|
|
|
|
|
184 |
background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%);
|
185 |
color: white;
|
186 |
+
border: none;
|
187 |
+
transition: all 0.3s ease;
|
188 |
}
|
189 |
|
190 |
.stButton>button:hover {
|
191 |
+
transform: translateY(-3px);
|
192 |
+
box-shadow: 0 8px 20px rgba(46, 204, 113, 0.3);
|
193 |
+
}
|
194 |
+
|
195 |
+
/* Tabs styling */
|
196 |
+
.stTabs [data-baseweb="tab-list"] {
|
197 |
+
gap: 10px;
|
198 |
+
}
|
199 |
+
|
200 |
+
.stTabs [data-baseweb="tab"] {
|
201 |
+
border-radius: 8px 8px 0 0;
|
202 |
+
padding: 12px 20px;
|
203 |
+
background-color: #f8f9fa;
|
204 |
+
transition: all 0.3s ease;
|
205 |
+
}
|
206 |
+
|
207 |
+
.stTabs [aria-selected="true"] {
|
208 |
+
background-color: #2ecc71 !important;
|
209 |
+
color: white !important;
|
210 |
+
box-shadow: 0 5px 15px rgba(46, 204, 113, 0.2);
|
211 |
}
|
212 |
|
213 |
/* Footer styling */
|
|
|
216 |
left: 0;
|
217 |
bottom: 0;
|
218 |
width: 100%;
|
219 |
+
background: linear-gradient(90deg, #2ecc71 0%, #27ae60 100%);
|
220 |
color: white;
|
221 |
text-align: center;
|
222 |
+
padding: 15px 0;
|
223 |
+
box-shadow: 0 -5px 15px rgba(0, 0, 0, 0.1);
|
224 |
}
|
225 |
</style>
|
226 |
""", unsafe_allow_html=True)
|
|
|
244 |
'رطوبت استاندارد قبلی': 'PreviousStandardMoisture', 'چاهک 1': 'Well1', 'تاریخ قرائت': 'Well1Date',
|
245 |
'چاهک 2': 'Well2', 'تاریخ قرائت.1': 'Well2Date'
|
246 |
}, inplace=True)
|
|
|
247 |
numeric_cols = ['Area', 'CurrentHeight', 'PreviousHeight', 'CurrentGrowth', 'PreviousGrowth',
|
248 |
'CurrentNitrogen', 'PreviousNitrogen', 'CurrentMoisture', 'PreviousMoisture',
|
249 |
'Station1', 'Station2', 'Station3', 'Station4', 'Station5', 'Well1', 'Well2']
|
|
|
335 |
elif layer_type == "NDMI":
|
336 |
index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
|
337 |
viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
|
338 |
+
elif layer_type == "EVI":
|
339 |
+
nir = s2.select('B8')
|
340 |
+
red = s2.select('B4')
|
341 |
+
blue = s2.select('B2')
|
342 |
+
index = nir.subtract(red).multiply(2.5).divide(nir.add(red.multiply(6)).subtract(blue.multiply(7.5)).add(1)).rename('EVI')
|
343 |
+
viz_params = {'min': 0, 'max': 1, 'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#4caf50']}
|
344 |
+
elif layer_type == "NDWI":
|
345 |
+
index = s2.normalizedDifference(['B3', 'B8']).rename('NDWI')
|
346 |
+
viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#00008b', '#00b7eb', '#add8e6', '#fdae61', '#d73027']}
|
347 |
map_id_dict = ee.Image(index).getMapId(viz_params)
|
348 |
folium.TileLayer(
|
349 |
tiles=map_id_dict['tile_fetcher'].url_format,
|
|
|
367 |
if selected_age != "all":
|
368 |
filtered_farms = filtered_farms[filtered_farms['Age'] == selected_age]
|
369 |
|
370 |
+
farm_growth_data = []
|
371 |
weeks = filtered_farms['Week'].unique()
|
372 |
+
for farm_id in filtered_farms['Farm_ID'].unique():
|
373 |
+
farm_data = filtered_farms[filtered_farms['Farm_ID'] == farm_id]
|
374 |
+
growth_data = {
|
375 |
+
'farm_id': farm_id,
|
376 |
+
'variety': farm_data['Variety'].iloc[0] if not farm_data.empty else 'Unknown',
|
377 |
+
'age': farm_data['Age'].iloc[0] if not farm_data.empty else 'Unknown',
|
378 |
+
'weeks': weeks,
|
379 |
+
'heights': [farm_data[farm_data['Week'] == week]['CurrentHeight'].mean() if not farm_data[farm_data['Week'] == week].empty else 0 for week in weeks]
|
380 |
+
}
|
381 |
+
farm_growth_data.append(growth_data)
|
382 |
+
|
383 |
+
if farm_growth_data:
|
384 |
+
avg_heights = []
|
385 |
+
for week in weeks:
|
386 |
+
week_heights = [farm['heights'][list(weeks).index(week)] for farm in farm_growth_data if farm['heights'][list(weeks).index(week)] > 0]
|
387 |
+
avg_heights.append(round(sum(week_heights) / len(week_heights)) if week_heights else 0)
|
388 |
+
avg_growth_data = {'farm_id': 'میانگین', 'variety': 'همه', 'age': 'همه', 'weeks': weeks, 'heights': avg_heights}
|
389 |
+
return {'individual': farm_growth_data, 'average': avg_growth_data}
|
390 |
+
return {
|
391 |
+
'individual': [],
|
392 |
+
'average': {'farm_id': 'میانگین', 'variety': 'همه', 'age': 'همه', 'weeks': weeks, 'heights': [0] * len(weeks)}
|
393 |
+
}
|
394 |
|
395 |
# Initialize Earth Engine and load data
|
396 |
ee_initialized = initialize_earth_engine()
|
|
|
410 |
# Main header
|
411 |
st.markdown('<div class="main-header">', unsafe_allow_html=True)
|
412 |
st.markdown('<h1>سامانه هوشمند پایش مزارع نیشکر دهخدا</h1>', unsafe_allow_html=True)
|
413 |
+
st.markdown('<p>پلتفرم جامع مدیریت، پایش و تحلیل دادههای مزارع نیشکر با فناوری پیشرفته</p>', unsafe_allow_html=True)
|
414 |
st.markdown('</div>', unsafe_allow_html=True)
|
415 |
|
416 |
# Navigation menu
|
|
|
424 |
styles={
|
425 |
"container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "20px"},
|
426 |
"icon": {"color": "#2ecc71", "font-size": "18px"},
|
427 |
+
"nav-link": {"font-size": "16px", "text-align": "center", "margin":"0px", "--hover-color": "#e9f7ef", "border-radius": "12px"},
|
428 |
"nav-link-selected": {"background-color": "#2ecc71", "color": "white", "font-weight": "600"},
|
429 |
}
|
430 |
)
|
|
|
467 |
|
468 |
st.markdown('</div>', unsafe_allow_html=True)
|
469 |
|
470 |
+
tab1, tab2, tab3, tab4 = st.tabs(["نمای کلی", "نقشه مزارع", "نمودارها", "دادهها"])
|
471 |
+
|
472 |
+
with tab1:
|
473 |
+
st.subheader("توزیع واریتهها و سن محصول")
|
474 |
+
col1, col2 = st.columns(2)
|
475 |
+
with col1:
|
476 |
+
fig = px.pie(farm_df['Variety'].value_counts().reset_index(), values='count', names='Variety', title='توزیع واریتهها')
|
477 |
+
st.plotly_chart(fig, use_container_width=True)
|
478 |
+
with col2:
|
479 |
+
fig = px.pie(farm_df['Age'].value_counts().reset_index(), values='count', names='Age', title='توزیع سن محصول')
|
480 |
+
st.plotly_chart(fig, use_container_width=True)
|
481 |
+
st_lottie(lottie_farm, height=200, key="farm_animation")
|
482 |
+
|
483 |
+
with tab2:
|
484 |
+
if not coordinates_df.empty:
|
485 |
+
m = folium.Map(location=[31.45, 48.72], zoom_start=12)
|
486 |
+
for _, farm in coordinates_df.iterrows():
|
487 |
+
folium.Marker([farm['Latitude'], farm['Longitude']], popup=f'مزرعه {farm["Farm_ID"]}').add_to(m)
|
488 |
+
folium_static(m, width=1000, height=600)
|
489 |
+
|
490 |
+
with tab3:
|
491 |
+
st.subheader("نمودار رشد")
|
492 |
+
col1, col2 = st.columns(2)
|
493 |
+
with col1:
|
494 |
+
variety = st.selectbox("انتخاب واریته", ["all"] + list(farm_df['Variety'].unique()), key="variety_chart")
|
495 |
+
with col2:
|
496 |
+
age = st.selectbox("انتخاب سن", ["all"] + list(farm_df['Age'].unique()), key="age_chart")
|
497 |
+
growth_data = generate_real_growth_data(variety, age)
|
498 |
+
fig = go.Figure()
|
499 |
+
for farm_data in growth_data['individual'][:5]:
|
500 |
+
fig.add_trace(go.Scatter(x=farm_data['weeks'], y=farm_data['heights'], mode='lines+markers', name=f"مزرعه {farm_data['farm_id']}"))
|
501 |
+
fig.update_layout(title='رشد هفتگی مزارع', xaxis_title='هفته', yaxis_title='ارتفاع (سانتیمتر)')
|
502 |
+
st.plotly_chart(fig, use_container_width=True)
|
503 |
+
|
504 |
+
with tab4:
|
505 |
+
st.subheader("دادههای مزارع")
|
506 |
+
st.dataframe(farm_df, use_container_width=True)
|
507 |
|
508 |
# Map Page
|
509 |
elif selected == "نقشه مزارع":
|
510 |
st.markdown("## نقشه مزارع با شاخصهای ماهوارهای")
|
511 |
+
col1, col2 = st.columns([1, 3])
|
512 |
+
with col1:
|
513 |
+
farm_id = st.selectbox("انتخاب مزرعه", coordinates_df['Farm_ID'].tolist())
|
514 |
+
date = st.date_input("انتخاب تاریخ", datetime.now())
|
515 |
+
layer_type = st.selectbox("انتخاب شاخص", ["NDVI", "NDMI", "EVI", "NDWI"])
|
516 |
+
if st.button("تولید نقشه"):
|
517 |
+
with st.spinner('در حال تولید نقشه...'):
|
518 |
+
m = create_ee_map(farm_id, date.strftime('%Y-%m-%d'), layer_type)
|
519 |
+
if m:
|
520 |
+
st.session_state['last_map'] = m
|
521 |
+
st.success(f"نقشه {layer_type} تولید شد.")
|
522 |
+
with col2:
|
523 |
+
if 'last_map' in st.session_state:
|
524 |
+
folium_static(st.session_state['last_map'], width=800, height=600)
|
525 |
|
526 |
# Data Entry Page
|
527 |
elif selected == "ورود اطلاعات":
|
|
|
529 |
tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"])
|
530 |
|
531 |
with tab1:
|
532 |
+
col1, col2 = st.columns(2)
|
533 |
+
with col1:
|
534 |
+
week = st.selectbox("انتخاب هفته", [str(i) for i in range(1, 23)], key="week_entry")
|
535 |
+
with col2:
|
536 |
+
day = st.selectbox("انتخاب روز", day_df['Day'].unique(), key="day_entry")
|
537 |
filtered_farms = farm_df[farm_df['Week'] == int(week)]
|
538 |
if not filtered_farms.empty:
|
539 |
data_key = f"data_{week}_{day}"
|
540 |
if data_key not in st.session_state:
|
541 |
st.session_state[data_key] = pd.DataFrame({
|
542 |
'Farm_ID': filtered_farms['Farm_ID'],
|
543 |
+
'Station1': [0] * len(filtered_farms),
|
544 |
+
'Station2': [0] * len(filtered_farms),
|
545 |
+
'Station3': [0] * len(filtered_farms),
|
546 |
+
'Station4': [0] * len(filtered_farms),
|
547 |
+
'Station5': [0] * len(filtered_farms),
|
548 |
'CurrentHeight': [0.0] * len(filtered_farms),
|
549 |
'CurrentMoisture': [0.0] * len(filtered_farms)
|
550 |
})
|
|
|
558 |
if uploaded_file:
|
559 |
try:
|
560 |
df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
|
561 |
+
numeric_cols = ['CurrentHeight', 'CurrentMoisture', 'Station1', 'Station2', 'Station3', 'Station4', 'Station5']
|
562 |
for col in numeric_cols:
|
563 |
if col in df.columns:
|
564 |
df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
|
|
|
572 |
# Data Analysis Page
|
573 |
elif selected == "تحلیل دادهها":
|
574 |
st.markdown("## تحلیل هوشمند دادهها")
|
575 |
+
tab1, tab2, tab3, tab4 = st.tabs(["تحلیل رشد", "مقایسه واریتهها", "تحلیل رطوبت", "پیشبینی"])
|
576 |
+
|
577 |
+
with tab1:
|
578 |
+
col1, col2 = st.columns(2)
|
579 |
+
with col1:
|
580 |
+
variety = st.selectbox("انتخاب واریته", ["all"] + list(farm_df['Variety'].unique()), key="variety_analysis")
|
581 |
+
with col2:
|
582 |
+
age = st.selectbox("انتخاب سن", ["all"] + list(farm_df['Age'].unique()), key="age_analysis")
|
583 |
+
growth_data = generate_real_growth_data(variety, age)
|
584 |
+
fig = go.Figure()
|
585 |
+
for farm_data in growth_data['individual'][:5]:
|
586 |
+
fig.add_trace(go.Scatter(x=farm_data['weeks'], y=farm_data['heights'], mode='lines+markers', name=f"مزرعه {farm_data['farm_id']}"))
|
587 |
+
fig.update_layout(title='رشد هفتگی', xaxis_title='هفته', yaxis_title='ارتفاع (سانتیمتر)')
|
588 |
+
st.plotly_chart(fig, use_container_width=True)
|
589 |
+
|
590 |
+
with tab2:
|
591 |
+
fig = px.box(farm_df, x='Variety', y='CurrentHeight', title='مقایسه ارتفاع بر اساس واریته')
|
592 |
+
st.plotly_chart(fig, use_container_width=True)
|
593 |
+
|
594 |
+
with tab3:
|
595 |
+
fig = px.scatter(farm_df, x='CurrentMoisture', y='CurrentHeight', color='Farm_ID', title='همبستگی رطوبت و ارتفاع')
|
596 |
+
st.plotly_chart(fig, use_container_width=True)
|
597 |
+
|
598 |
+
with tab4:
|
599 |
+
farm_id = st.selectbox("انتخاب مزرعه", farm_df['Farm_ID'].tolist(), key="predict_farm")
|
600 |
+
farm_data = farm_df[farm_df['Farm_ID'] == farm_id]
|
601 |
+
if len(farm_data) > 1:
|
602 |
+
model = LinearRegression()
|
603 |
+
model.fit(farm_data['Week'].values.reshape(-1, 1), farm_data['CurrentHeight'].values)
|
604 |
+
future_weeks = np.array(range(max(farm_data['Week']) + 1, 30)).reshape(-1, 1)
|
605 |
+
future_heights = model.predict(future_weeks)
|
606 |
+
fig = go.Figure()
|
607 |
+
fig.add_trace(go.Scatter(x=farm_data['Week'], y=farm_data['CurrentHeight'], mode='lines+markers', name='دادههای واقعی'))
|
608 |
+
fig.add_trace(go.Scatter(x=future_weeks.flatten(), y=future_heights, mode='lines', name='پیشبینی'))
|
609 |
+
fig.update_layout(title=f'پیشبینی رشد مزرعه {farm_id}', xaxis_title='هفته', yaxis_title='ارتفاع (سانتیمتر)')
|
610 |
+
st.plotly_chart(fig, use_container_width=True)
|
611 |
|
612 |
# Report Generation Page
|
613 |
elif selected == "گزارشگیری":
|
614 |
st.markdown("## گزارشگیری")
|
615 |
+
report_week = st.selectbox("انتخاب هفته", [str(i) for i in range(1, 23)], key="report_week")
|
616 |
+
report_day = st.selectbox("انتخاب روز", day_df['Day'].unique(), key="report_day")
|
617 |
report_df = st.session_state.heights_df[
|
618 |
(st.session_state.heights_df['Week'] == int(report_week)) &
|
619 |
(st.session_state.heights_df['Farm_ID'].isin(day_df[day_df['Day'] == report_day]['Farm_ID']))
|
|
|
621 |
if not report_df.empty:
|
622 |
st.dataframe(report_df)
|
623 |
csv = report_df.to_csv(index=False).encode('utf-8')
|
624 |
+
st.download_button(label="دانلود گزارش", data=csv, file_name=f"report_week_{report_week}_day_{report_day}.csv")
|
625 |
else:
|
626 |
+
st.warning(f"دادهای برای هفته {report_week} و روز {report_day} یافت نشد.")
|
627 |
+
st_lottie(lottie_report, height=200, key="report_animation")
|
628 |
|
629 |
# Settings Page
|
630 |
elif selected == "تنظیمات":
|
|
|
632 |
if st.button("بارگذاری مجدد دادهها"):
|
633 |
st.session_state.heights_df = load_farm_data()
|
634 |
st.success("دادهها بهروزرسانی شدند.")
|
635 |
+
theme = st.selectbox("انتخاب تم", ["سبز (پیشفرض)", "آبی"], key="theme_select")
|
636 |
+
if theme == "آبی":
|
637 |
+
st.markdown("""
|
638 |
+
<style>
|
639 |
+
.main-header {background: linear-gradient(90deg, #3498db 0%, #2980b9 100%);}
|
640 |
+
.metric-card {background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);}
|
641 |
+
.stButton>button {background: linear-gradient(90deg, #3498db 0%, #2980b9 100%);}
|
642 |
+
footer {background: linear-gradient(90deg, #3498db 0%, #2980b9 100%);}
|
643 |
+
</style>
|
644 |
+
""", unsafe_allow_html=True)
|
645 |
|
646 |
# Footer
|
647 |
st.markdown("""
|
648 |
<footer>
|
649 |
+
<p>© 2025 سامانه هوشمند پایش مزارع نیشکر دهخدا. تمامی حقوق محفوظ است.</p>
|
650 |
</footer>
|
651 |
""", unsafe_allow_html=True)
|