Spaces:
Configuration error
Configuration error
Update app.py
Browse files
app.py
CHANGED
@@ -5,14 +5,35 @@ import folium
|
|
5 |
from streamlit_folium import folium_static
|
6 |
import ee
|
7 |
import os
|
8 |
-
import
|
|
|
9 |
from datetime import datetime, timedelta
|
10 |
import plotly.express as px
|
11 |
import plotly.graph_objects as go
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
from streamlit_lottie import st_lottie
|
13 |
-
import
|
14 |
-
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
st.set_page_config(
|
17 |
page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
|
18 |
page_icon="🌿",
|
@@ -20,295 +41,797 @@ st.set_page_config(
|
|
20 |
initial_sidebar_state="expanded"
|
21 |
)
|
22 |
|
23 |
-
#
|
24 |
st.markdown("""
|
25 |
<style>
|
26 |
-
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@400;700&display=swap');
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
</style>
|
35 |
""", unsafe_allow_html=True)
|
36 |
|
37 |
-
#
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
JfpSU7ZjYZmVc81M/7iFofGUSJoHYpxhyt3rjp53huxJNNW5e12TFnLkyg1Ja/9X
|
47 |
-
GMTt+vjVcO4XhQCIlaGVdSKS2sHlHgzpzE6KtuUKjDMEBqPkWF4xc16YavYltwPd
|
48 |
-
qULCu2/t6dczhYL4NEFj8wL+KJqOojfsyoWmzqPFx1Bbxk4BVPk/lslq9+m9p5kq
|
49 |
-
SCG0/9W9AgMBAAECggEAEGchw+x3uu8rFv+79PIMzXxtyj+w3RYo5E/EN2TB1VLB
|
50 |
-
qAcXT/ibBgyfCMyIxamF/zx+4XKx+zfbnDWlodi8F/qvUiYO+4ZuqwUMras1orNX
|
51 |
-
DqQx+If5h2EJtF3L4NFVVwAuggjnLREm5sEIzRn5Qx+X+ZcVEpTWPxJw2yAt1G+3
|
52 |
-
k311KqD+zR7jQfchXU4xQQ1ZoHkdAJ/DPGun6x+HUOq7Gus73A6IzLp12ZoiHN3n
|
53 |
-
kY+lG8cMs039QAe/OhZFEo5I9cNSaI688HmsLRivZ26WoPEnwcN0MHQGtXqGmMUI
|
54 |
-
CcpgJqllqdWMuBlYcpSadn7rZzPujSlzIxkvieLeAQKBgQDNTYUWZdGbA2sHcBpJ
|
55 |
-
rqKwDYF/AwZtjx+hXHVBRbR6DJ1bO2P9n51ioTMP/H9K61OBAMZ7w71xJ2I+9Snv
|
56 |
-
cYumPWoiUwiOhTh3O7nYz6mR7sK0HuUCZfYdaxJVnLgNCgj+w9AxYnkzOyL9/QvJ
|
57 |
-
knrlMKf4H59NbapBqy5spilq1QKBgQDL1wkGHhoTuLb5Xp3X3CX4S7WMke4T01bO
|
58 |
-
PpMmlewVgH5lK5wTcZjB8QRO2QFQtUZTP/Ghv6ZH4h/3P9/ZIF3hV5CSsUkr/eFf
|
59 |
-
MY+fQL1K/puwfZbSDcH1GtDToOyoLFIvPXBJo0Llg/oF2TK1zGW3cPszeDf/Tm6x
|
60 |
-
UwUMw2BjSQKBgEJzAMyLEBi4NoAlzJxkpcuN04gkloQHexljL6B8yzlls9i/lFGW
|
61 |
-
w/4UZs6ZzymUmWZ7tcKBTGO/d5EhEP2rJqQb5KpPbcmTXP9amYCPVjchrGtYRI9O
|
62 |
-
KSbEbR7ApuGxic/L2Sri0I/AaEcFDDel7ZkY8oTg11LcV+sBWPlZnrYxAoGBALXj
|
63 |
-
/DlpQvu2KA/9TfwAhiE57Zax4S/vtdX0IHqd7TyCnEbK00rGYvksiBuTqIjMOSSw
|
64 |
-
On2K9mXOcZe/d4/YQe2CpY9Ag3qt4R2ArBf/POpep66lYp+thxWgCBfP0V1/rxZY
|
65 |
-
TIppFJiZW9E8LvPqoBlAx+b1r4IyCrRQ0IDDFo+BAoGBAMCff4XKXHlV2SDOL5uh
|
66 |
-
V/f9ApEdF4leuo+hoMryKuSQ9Y/H0A/Lzw6KP5FLvVtqc0Kw2D1oLy8O72a1xwfY
|
67 |
-
8dpZMNzKAWWS7viN0oC+Ebj2Foc2Mn/J6jdhtP/YRLTqvoTWCa2rVcn4R1BurMIf
|
68 |
-
La4DJE9BagGdVNTDtynBhKhZ
|
69 |
-
-----END PRIVATE KEY-----
|
70 |
-
""",
|
71 |
-
"client_email": "dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com",
|
72 |
-
"client_id": "113062529451626176784",
|
73 |
-
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
74 |
-
"token_uri": "https://oauth2.googleapis.com/token",
|
75 |
-
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
76 |
-
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dehkhodamap-e9f0da4ce9f6514021%40ee-esmaeilkiani13877.iam.gserviceaccount.com",
|
77 |
-
"universe_domain": "googleapis.com"
|
78 |
-
}
|
79 |
|
80 |
-
#
|
81 |
@st.cache_resource
|
82 |
def initialize_earth_engine():
|
83 |
try:
|
84 |
-
ee.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
85 |
return True
|
86 |
except Exception as e:
|
87 |
st.error(f"خطا در اتصال به Earth Engine: {e}")
|
88 |
return False
|
89 |
|
90 |
-
#
|
91 |
@st.cache_data
|
92 |
def load_farm_data():
|
93 |
try:
|
94 |
df = pd.read_csv("پایگاه داده (1).csv")
|
95 |
-
if df.empty:
|
96 |
-
st.warning("فایل داده خالی است.")
|
97 |
return df
|
98 |
-
except FileNotFoundError:
|
99 |
-
st.error("فایل پایگاه داده (1).csv یافت نشد.")
|
100 |
-
return pd.DataFrame()
|
101 |
except Exception as e:
|
102 |
-
st.error(f"خطا در بارگذاری
|
103 |
return pd.DataFrame()
|
104 |
|
105 |
-
# تابع بارگذاری مختصات مزارع از فایل CSV
|
106 |
@st.cache_data
|
107 |
def load_coordinates_data():
|
108 |
try:
|
109 |
df = pd.read_csv("farm_coordinates.csv")
|
110 |
-
if df.empty:
|
111 |
-
st.warning("فایل مختصات خالی است.")
|
112 |
return df
|
113 |
-
except FileNotFoundError:
|
114 |
-
st.error("فایل farm_coordinates.csv یافت نشد.")
|
115 |
-
return pd.DataFrame()
|
116 |
except Exception as e:
|
117 |
-
st.error(f"خطا در بارگذاری مختصات: {e}")
|
118 |
return pd.DataFrame()
|
119 |
|
120 |
-
#
|
121 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
122 |
url = f"http://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={api_key}&units=metric"
|
123 |
-
|
124 |
-
|
125 |
-
response.raise_for_status()
|
126 |
return response.json()
|
127 |
-
|
128 |
-
st.error(f"خطا در دریافت دادههای هواشناسی: {e}")
|
129 |
-
return None
|
130 |
|
131 |
-
#
|
132 |
def estimate_water_requirement(farm_id, date_str):
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
return
|
142 |
return None
|
143 |
|
144 |
-
#
|
145 |
-
|
146 |
-
def calculate_indices(farm_id, date_str):
|
147 |
try:
|
148 |
-
farm_row = coordinates_df[coordinates_df[
|
149 |
-
lat, lon = farm_row[
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
.filterBounds(region)
|
158 |
-
.filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 20))
|
159 |
-
.sort("CLOUDY_PIXEL_PERCENTAGE")
|
160 |
-
.first())
|
161 |
-
|
162 |
-
if not s2.getInfo():
|
163 |
-
st.warning(f"تصویری برای مزرعه {farm_id} یافت نشد.")
|
164 |
-
return {"NDVI": 0, "NDWI": 0, "LAI": 0, "CHL": 0}
|
165 |
-
|
166 |
-
ndvi = s2.normalizedDifference(["B8", "B4"]).rename("NDVI")
|
167 |
-
ndwi = s2.normalizedDifference(["B3", "B8"]).rename("NDWI")
|
168 |
-
|
169 |
-
ndvi_value = ndvi.reduceRegion(reducer=ee.Reducer.mean(), geometry=region, scale=10).get("NDVI").getInfo()
|
170 |
-
ndwi_value = ndwi.reduceRegion(reducer=ee.Reducer.mean(), geometry=region, scale=10).get("NDWI").getInfo()
|
171 |
-
|
172 |
-
lai_value = 6 * ndvi_value if ndvi_value else 0 # تخمین LAI
|
173 |
-
chl_value = 50 * ndvi_value if ndvi_value else 0 # تخمین کلروفیل
|
174 |
-
|
175 |
-
return {
|
176 |
-
"NDVI": ndvi_value or 0,
|
177 |
-
"NDWI": ndwi_value or 0,
|
178 |
-
"LAI": min(max(lai_value, 0), 6),
|
179 |
-
"CHL": chl_value
|
180 |
-
}
|
181 |
-
except Exception as e:
|
182 |
-
st.error(f"خطا در محاسبه شاخصها: {e}")
|
183 |
-
return {"NDVI": 0, "NDWI": 0, "LAI": 0, "CHL": 0}
|
184 |
-
|
185 |
-
# تابع ایجاد نقشه NDVI با استفاده از Earth Engine و Folium
|
186 |
-
def create_ee_map(farm_id, date_str):
|
187 |
-
try:
|
188 |
-
farm_row = coordinates_df[coordinates_df["مزرعه"] == farm_id].iloc[0]
|
189 |
-
lat, lon = farm_row["عرض جغرافیایی"], farm_row["طول جغرافیایی"]
|
190 |
-
m = folium.Map(location=[lat, lon], zoom_start=14)
|
191 |
-
|
192 |
-
date_obj = datetime.strptime(date_str, "%Y-%m-%d")
|
193 |
-
start_date = (date_obj - timedelta(days=7)).strftime("%Y-%m-%d")
|
194 |
-
end_date = date_obj.strftime("%Y-%m-%d")
|
195 |
-
|
196 |
region = ee.Geometry.Point([lon, lat]).buffer(1500)
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
if
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
228 |
</div>
|
229 |
-
|
230 |
m.get_root().html.add_child(folium.Element(legend_html))
|
231 |
-
|
232 |
return m
|
233 |
except Exception as e:
|
234 |
st.error(f"خطا در ایجاد نقشه: {e}")
|
235 |
return None
|
236 |
|
237 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
ee_initialized = initialize_earth_engine()
|
239 |
farm_df = load_farm_data()
|
240 |
coordinates_df = load_coordinates_data()
|
241 |
|
242 |
-
#
|
243 |
-
|
244 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
245 |
|
246 |
-
#
|
247 |
if selected == "داشبورد":
|
248 |
-
st.markdown(
|
249 |
-
col1, col2, col3 = st.columns(
|
|
|
250 |
with col1:
|
251 |
-
st.markdown(
|
|
|
|
|
|
|
|
|
252 |
with col2:
|
253 |
-
|
|
|
|
|
|
|
|
|
|
|
254 |
with col3:
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
from streamlit_folium import folium_static
|
6 |
import ee
|
7 |
import os
|
8 |
+
import json
|
9 |
+
import time
|
10 |
from datetime import datetime, timedelta
|
11 |
import plotly.express as px
|
12 |
import plotly.graph_objects as go
|
13 |
+
from PIL import Image
|
14 |
+
import base64
|
15 |
+
from io import BytesIO
|
16 |
+
import matplotlib.pyplot as plt
|
17 |
+
import seaborn as sns
|
18 |
+
import altair as alt
|
19 |
+
from streamlit_option_menu import option_menu
|
20 |
from streamlit_lottie import st_lottie
|
21 |
+
import requests
|
22 |
+
import hydralit_components as hc
|
23 |
+
from streamlit_extras.colored_header import colored_header
|
24 |
+
from streamlit_extras.metric_cards import style_metric_cards
|
25 |
+
from streamlit_extras.chart_container import chart_container
|
26 |
+
from streamlit_extras.add_vertical_space import add_vertical_space
|
27 |
+
from streamlit_card import card
|
28 |
+
import pydeck as pdk
|
29 |
+
import math
|
30 |
+
from sklearn.ensemble import RandomForestRegressor
|
31 |
+
from sklearn.cluster import KMeans
|
32 |
+
from tensorflow.keras.models import Sequential
|
33 |
+
from tensorflow.keras.layers import LSTM, Dense
|
34 |
+
from sklearn.preprocessing import MinMaxScaler
|
35 |
+
|
36 |
+
# Page configuration with custom theme
|
37 |
st.set_page_config(
|
38 |
page_title="سامانه هوشمند پایش مزارع نیشکر دهخدا",
|
39 |
page_icon="🌿",
|
|
|
41 |
initial_sidebar_state="expanded"
|
42 |
)
|
43 |
|
44 |
+
# Custom CSS with modern design, animations, and theme support
|
45 |
st.markdown("""
|
46 |
<style>
|
47 |
+
@import url('https://fonts.googleapis.com/css2?family=Vazirmatn:wght@100;200;300;400;500;600;700;800;900&display=swap');
|
48 |
+
|
49 |
+
* {
|
50 |
+
font-family: 'Vazirmatn', sans-serif !important;
|
51 |
+
transition: all 0.3s ease;
|
52 |
+
}
|
53 |
+
|
54 |
+
.main {
|
55 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #e4e9f2 100%);
|
56 |
+
}
|
57 |
+
|
58 |
+
.dark-theme .main {
|
59 |
+
background: linear-gradient(135deg, #2c3e50 0%, #1a252f 100%);
|
60 |
+
color: #ecf0f1;
|
61 |
+
}
|
62 |
+
|
63 |
+
.main-header {
|
64 |
+
background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
|
65 |
+
padding: 1.5rem;
|
66 |
+
border-radius: 12px;
|
67 |
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
68 |
+
margin-bottom: 2rem;
|
69 |
+
position: relative;
|
70 |
+
overflow: hidden;
|
71 |
+
animation: header-glow 3s infinite alternate;
|
72 |
+
}
|
73 |
+
|
74 |
+
@keyframes header-glow {
|
75 |
+
0% { box-shadow: 0 8px 32px rgba(26, 135, 84, 0.1); }
|
76 |
+
100% { box-shadow: 0 8px 32px rgba(26, 135, 84, 0.3); }
|
77 |
+
}
|
78 |
+
|
79 |
+
.main-header h1, .main-header p {
|
80 |
+
color: white;
|
81 |
+
z-index: 1;
|
82 |
+
position: relative;
|
83 |
+
}
|
84 |
+
|
85 |
+
.stcard {
|
86 |
+
border-radius: 12px;
|
87 |
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
88 |
+
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
89 |
+
}
|
90 |
+
|
91 |
+
.stcard:hover {
|
92 |
+
transform: translateY(-5px);
|
93 |
+
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.1);
|
94 |
+
}
|
95 |
+
|
96 |
+
.dark-theme .stcard {
|
97 |
+
background: #34495e;
|
98 |
+
color: #ecf0f1;
|
99 |
+
}
|
100 |
+
|
101 |
+
.stButton>button {
|
102 |
+
border-radius: 50px;
|
103 |
+
padding: 0.5rem 1.5rem;
|
104 |
+
font-weight: 600;
|
105 |
+
transition: all 0.3s ease;
|
106 |
+
}
|
107 |
+
|
108 |
+
.primary-btn {
|
109 |
+
background: linear-gradient(90deg, #1a8754 0%, #115740 100%);
|
110 |
+
color: white;
|
111 |
+
}
|
112 |
+
|
113 |
+
.dark-theme .primary-btn {
|
114 |
+
background: linear-gradient(90deg, #27ae60 0%, #219653 100%);
|
115 |
+
}
|
116 |
+
|
117 |
+
.metric-card {
|
118 |
+
background: white;
|
119 |
+
border-radius: 12px;
|
120 |
+
padding: 1.5rem;
|
121 |
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
122 |
+
text-align: center;
|
123 |
+
}
|
124 |
+
|
125 |
+
.dark-theme .metric-card {
|
126 |
+
background: #34495e;
|
127 |
+
color: #ecf0f1;
|
128 |
+
}
|
129 |
+
|
130 |
+
.map-container {
|
131 |
+
border-radius: 12px;
|
132 |
+
overflow: hidden;
|
133 |
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
|
134 |
+
}
|
135 |
+
|
136 |
+
.animate-load {
|
137 |
+
animation: fadeIn 0.5s ease forwards;
|
138 |
+
}
|
139 |
+
|
140 |
+
@keyframes fadeIn {
|
141 |
+
0% { opacity: 0; transform: translateY(20px); }
|
142 |
+
100% { opacity: 1; transform: translateY(0); }
|
143 |
+
}
|
144 |
+
|
145 |
+
.rtl {
|
146 |
+
direction: rtl;
|
147 |
+
text-align: right;
|
148 |
+
}
|
149 |
</style>
|
150 |
""", unsafe_allow_html=True)
|
151 |
|
152 |
+
# Theme selection
|
153 |
+
if 'theme' not in st.session_state:
|
154 |
+
st.session_state.theme = 'light'
|
155 |
+
|
156 |
+
theme = st.sidebar.selectbox("انتخاب تم", ["روشن", "تیره"], index=0 if st.session_state.theme == 'light' else 1)
|
157 |
+
st.session_state.theme = 'light' if theme == "روشن" else 'dark'
|
158 |
+
theme_class = "dark-theme" if st.session_state.theme == 'dark' else ""
|
159 |
+
|
160 |
+
st.markdown(f'<body class="{theme_class}">', unsafe_allow_html=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
|
162 |
+
# Earth Engine Initialization
|
163 |
@st.cache_resource
|
164 |
def initialize_earth_engine():
|
165 |
try:
|
166 |
+
service_account = 'dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com'
|
167 |
+
credentials_dict = {
|
168 |
+
"type": "service_account",
|
169 |
+
"project_id": "ee-esmaeilkiani13877",
|
170 |
+
"private_key_id": "cfdea6eaf4115cb6462626743e4b15df85fd0c7f",
|
171 |
+
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCjeOvgKi+gWK6k\n2/0RXOA3LAo51DVxA1ja9v0qFOn4FNOStxkwlKvcK8yDQNb53FPORHFIUHvev3y7\niHr/UEUqnn5Rzjbf0k3qWB/fS377/UP4VznMsFpKiHNxCBtaNS8KLk6Rat6Y7Xfm\nJfpSU7ZjYZmVc81M/7iFofGUSJoHYpxhyt3rjp53huxJNNW5e12TFnLkyg1Ja/9X\nGMTt+vjVcO4XhQCIlaGVdSKS2sHlHgzpzE6KtuUKjDMEBqPkWF4xc16YavYltwPd\nqULCu2/t6dczhYL4NEFj8wL+KJqOojfsyoWmzqPFx1Bbxk4BVPk/lslq9+m9p5kq\nSCG0/9W9AgMBAAECggEAEGchw+x3uu8rFv+79PIMzXxtyj+w3RYo5E/EN2TB1VLB\nqAcXT/ibBgyfCMyIxamF/zx+4XKx+zfbnDWlodi8F/qvUiYO+4ZuqwUMras1orNX\nDqQx+If5h2EJtF3L4NFVVwAuggjnLREm5sEIzRn5Qx+X+ZcVEpTWPxJw2yAt1G+3\nk311KqD+zR7jQfchXU4xQQ1ZoHkdAJ/DPGun6x+HUOq7Gus73A6IzLp12ZoiHN3n\nkY+lG8cMs039QAe/OhZFEo5I9cNSaI688HmsLRivZ26WoPEnwcN0MHQGtXqGmMUI\nCcpgJqllqdWMuBlYcpSadn7rZzPujSlzIxkvieLeAQKBgQDNTYUWZdGbA2sHcBpJ\nrqKwDYF/AwZtjx+hXHVBRbR6DJ1bO2P9n51ioTMP/H9K61OBAMZ7w71xJ2I+9Snv\ncYumPWoiUwiOhTh3O7nYz6mR7sK0HuUCZfYdaxJVnLgNCgj+w9AxYnkzOyL9/QvJ\nknrlMKf4H59NbapBqy5spilq1QKBgQDL1wkGHhoTuLb5Xp3X3CX4S7WMke4T01bO\nPpMmlewVgH5lK5wTcZjB8QRO2QFQtUZTP/Ghv6ZH4h/3P9/ZIF3hV5CSsUkr/eFf\nMY+fQL1K/puwfZbSDcH1GtDToOyoLFIvPXBJo0Llg/oF2TK1zGW3cPszeDf/Tm6x\nUwUMw2BjSQKBgEJzAMyLEBi4NoAlzJxkpcuN04gkloQHexljL6B8yzlls9i/lFGW\nw/4UZs6ZzymUmWZ7tcKBTGO/d5EhEP2rJqQb5KpPbcmTXP9amYCPVjchrGtYRI9O\nKSbEbR7ApuGxic/L2Sri0I/AaEcFDDel7ZkY8oTg11LcV+sBWPlZnrYxAoGBALXj\n/DlpQvu2KA/9TfwAhiE57Zax4S/vtdX0IHqd7TyCnEbK00rGYvksiBuTqIjMOSSw\nOn2K9mXOcZe/d4/YQe2CpY9Ag3qt4R2ArBf/POpep66lYp+thxWgCBfP0V1/rxZY\nTIppFJiZW9E8LvPqoBlAx+b1r4IyCrRQ0IDDFo+BAoGBAMCff4XKXHlV2SDOL5uh\nV/f9ApEdF4leuo+hoMryKuSQ9Y/H0A/Lzw6KP5FLvVtqc0Kw2D1oLy8O72a1xwfY\n8dpZMNzKAWWS7viN0oC+Ebj2Foc2Mn/J6jdhtP/YRLTqvoTWCa2rVcn4R1BurMIf\nLa4DJE9BagGdVNTDtynBhKhZ\n-----END PRIVATE KEY-----\n",
|
172 |
+
"client_email": "dehkhodamap-e9f0da4ce9f6514021@ee-esmaeilkiani13877.iam.gserviceaccount.com",
|
173 |
+
"client_id": "113062529451626176784",
|
174 |
+
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
175 |
+
"token_uri": "https://oauth2.googleapis.com/token",
|
176 |
+
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
|
177 |
+
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/dehkhodamap-e9f0da4ce9f6514021%40ee-esmaeilkiani13877.iam.gserviceaccount.com",
|
178 |
+
"universe_domain": "googleapis.com"
|
179 |
+
}
|
180 |
+
|
181 |
+
credentials_file = 'ee-esmaeilkiani13877-cfdea6eaf411.json'
|
182 |
+
with open(credentials_file, 'w') as f:
|
183 |
+
json.dump(credentials_dict, f)
|
184 |
+
|
185 |
+
credentials = ee.ServiceAccountCredentials(service_account, credentials_file)
|
186 |
+
ee.Initialize(credentials)
|
187 |
+
|
188 |
+
os.remove(credentials_file)
|
189 |
return True
|
190 |
except Exception as e:
|
191 |
st.error(f"خطا در اتصال به Earth Engine: {e}")
|
192 |
return False
|
193 |
|
194 |
+
# Load data
|
195 |
@st.cache_data
|
196 |
def load_farm_data():
|
197 |
try:
|
198 |
df = pd.read_csv("پایگاه داده (1).csv")
|
|
|
|
|
199 |
return df
|
|
|
|
|
|
|
200 |
except Exception as e:
|
201 |
+
st.error(f"خطا در بارگذاری دادههای مزارع: {e}")
|
202 |
return pd.DataFrame()
|
203 |
|
|
|
204 |
@st.cache_data
|
205 |
def load_coordinates_data():
|
206 |
try:
|
207 |
df = pd.read_csv("farm_coordinates.csv")
|
|
|
|
|
208 |
return df
|
|
|
|
|
|
|
209 |
except Exception as e:
|
210 |
+
st.error(f"خطا در بارگذاری دادههای مختصات: {e}")
|
211 |
return pd.DataFrame()
|
212 |
|
213 |
+
# Load animation JSON
|
214 |
+
@st.cache_data
|
215 |
+
def load_lottie_url(url: str):
|
216 |
+
r = requests.get(url)
|
217 |
+
if r.status_code != 200:
|
218 |
+
return None
|
219 |
+
return r.json()
|
220 |
+
|
221 |
+
# Weather data function
|
222 |
+
def get_weather_data(lat, lon, api_key):
|
223 |
url = f"http://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&appid={api_key}&units=metric"
|
224 |
+
response = requests.get(url)
|
225 |
+
if response.status_code == 200:
|
|
|
226 |
return response.json()
|
227 |
+
return None
|
|
|
|
|
228 |
|
229 |
+
# Estimate water requirement
|
230 |
def estimate_water_requirement(farm_id, date_str):
|
231 |
+
api_key = "ed47316a45379e2221a75f813229fb46"
|
232 |
+
farm_row = coordinates_df[coordinates_df['مزرعه'] == farm_id].iloc[0]
|
233 |
+
lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی']
|
234 |
+
weather_data = get_weather_data(lat, lon, api_key)
|
235 |
+
if weather_data:
|
236 |
+
temperature = weather_data['main']['temp']
|
237 |
+
humidity = weather_data['main']['humidity']
|
238 |
+
water_requirement = (temperature - 20) * 0.5 + (100 - humidity) * 0.1
|
239 |
+
return max(0, water_requirement)
|
240 |
return None
|
241 |
|
242 |
+
# Create Earth Engine map with heatmap option
|
243 |
+
def create_ee_map(farm_id, date_str, layer_type="NDVI", heatmap=False):
|
|
|
244 |
try:
|
245 |
+
farm_row = coordinates_df[coordinates_df['مزرعه'] == farm_id].iloc[0]
|
246 |
+
lat, lon = farm_row['عرض جغرافیایی'], farm_row['طول جغرافیایی']
|
247 |
+
|
248 |
+
m = folium.Map(location=[lat, lon], zoom_start=14, tiles='CartoDB positron')
|
249 |
+
|
250 |
+
date_obj = datetime.strptime(date_str, '%Y-%m-%d')
|
251 |
+
start_date = (date_obj - timedelta(days=5)).strftime('%Y-%m-%d')
|
252 |
+
end_date = (date_obj + timedelta(days=5)).strftime('%Y-%m-%d')
|
253 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
254 |
region = ee.Geometry.Point([lon, lat]).buffer(1500)
|
255 |
+
|
256 |
+
s2 = ee.ImageCollection('COPERNICUS/S2_SR') \
|
257 |
+
.filterDate(start_date, end_date) \
|
258 |
+
.filterBounds(region) \
|
259 |
+
.sort('CLOUDY_PIXEL_PERCENTAGE') \
|
260 |
+
.first()
|
261 |
+
|
262 |
+
if layer_type == "NDVI":
|
263 |
+
index = s2.normalizedDifference(['B8', 'B4']).rename('NDVI')
|
264 |
+
viz_params = {'min': -0.2, 'max': 0.8, 'palette': ['#ff0000', '#ff4500', '#ffd700', '#32cd32', '#006400']}
|
265 |
+
legend_title = 'شاخص پوشش گیاهی (NDVI)'
|
266 |
+
elif layer_type == "NDMI":
|
267 |
+
index = s2.normalizedDifference(['B8', 'B11']).rename('NDMI')
|
268 |
+
viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#8b0000', '#ff8c00', '#00ced1', '#00b7eb', '#00008b']}
|
269 |
+
legend_title = 'شاخص رطوبت (NDMI)'
|
270 |
+
elif layer_type == "EVI":
|
271 |
+
nir = s2.select('B8')
|
272 |
+
red = s2.select('B4')
|
273 |
+
blue = s2.select('B2')
|
274 |
+
index = nir.subtract(red).multiply(2.5).divide(nir.add(red.multiply(6)).subtract(blue.multiply(7.5)).add(1)).rename('EVI')
|
275 |
+
viz_params = {'min': 0, 'max': 1, 'palette': ['#d73027', '#f46d43', '#fdae61', '#fee08b', '#4caf50']}
|
276 |
+
legend_title = 'شاخص پیشرفته گیاهی (EVI)'
|
277 |
+
elif layer_type == "NDWI":
|
278 |
+
index = s2.normalizedDifference(['B3', 'B8']).rename('NDWI')
|
279 |
+
viz_params = {'min': -0.5, 'max': 0.5, 'palette': ['#00008b', '#00b7eb', '#add8e6', '#fdae61', '#d73027']}
|
280 |
+
legend_title = 'شاخص آب (NDWI)'
|
281 |
+
elif layer_type == "SoilMoisture":
|
282 |
+
s1 = ee.ImageCollection('COPERNICUS/S1_GRD') \
|
283 |
+
.filterDate(start_date, end_date) \
|
284 |
+
.filterBounds(region) \
|
285 |
+
.sort('system:time_start') \
|
286 |
+
.first()
|
287 |
+
index = s1.select('VV').rename('SoilMoisture')
|
288 |
+
viz_params = {'min': -25, 'max': -5, 'palette': ['#00008b', '#add8e6', '#ffffff']}
|
289 |
+
legend_title = 'رطوبت خاک (Soil Moisture)'
|
290 |
+
|
291 |
+
if heatmap:
|
292 |
+
map_id_dict = ee.Image(index).getMapId({'min': viz_params['min'], 'max': viz_params['max'], 'palette': viz_params['palette'], 'opacity': 0.7})
|
293 |
+
folium.TileLayer(
|
294 |
+
tiles=map_id_dict['tile_fetcher'].url_format,
|
295 |
+
attr='Google Earth Engine Heatmap',
|
296 |
+
name=f'{layer_type} Heatmap',
|
297 |
+
overlay=True,
|
298 |
+
control=True
|
299 |
+
).add_to(m)
|
300 |
+
else:
|
301 |
+
map_id_dict = ee.Image(index).getMapId(viz_params)
|
302 |
+
folium.TileLayer(
|
303 |
+
tiles=map_id_dict['tile_fetcher'].url_format,
|
304 |
+
attr='Google Earth Engine',
|
305 |
+
name=layer_type,
|
306 |
+
overlay=True,
|
307 |
+
control=True
|
308 |
+
).add_to(m)
|
309 |
+
|
310 |
+
folium.Marker([lat, lon], popup=f'مزرعه {farm_id}', tooltip=f'مزرعه {farm_id}', icon=folium.Icon(color='green', icon='leaf')).add_to(m)
|
311 |
+
folium.Circle([lat, lon], radius=1500, color='green', fill=True, fill_color='green', fill_opacity=0.1).add_to(m)
|
312 |
+
folium.LayerControl().add_to(m)
|
313 |
+
|
314 |
+
legend_html = f'''
|
315 |
+
<div style="position: fixed; bottom: 50px; right: 50px; border: 2px solid grey; z-index: 9999; background-color: white; padding: 10px; border-radius: 5px; direction: rtl;">
|
316 |
+
<div style="font-size: 14px; font-weight: bold; margin-bottom: 5px;">{legend_title}</div>
|
317 |
+
<div style="display: flex; align-items: center; margin-bottom: 5px;">
|
318 |
+
<div style="background: {viz_params['palette'][0]}; width: 20px; height: 20px; margin-left: 5px;"></div>
|
319 |
+
<span>کم</span>
|
320 |
+
</div>
|
321 |
+
<div style="display: flex; align-items: center; margin-bottom: 5px;">
|
322 |
+
<div style="background: {viz_params['palette'][2]}; width: 20px; height: 20px; margin-left: 5px;"></div>
|
323 |
+
<span>متوسط</span>
|
324 |
+
</div>
|
325 |
+
<div style="display: flex; align-items: center;">
|
326 |
+
<div style="background: {viz_params['palette'][-1]}; width: 20px; height: 20px; margin-left: 5px;"></div>
|
327 |
+
<span>زیاد</span>
|
328 |
+
</div>
|
329 |
</div>
|
330 |
+
'''
|
331 |
m.get_root().html.add_child(folium.Element(legend_html))
|
332 |
+
|
333 |
return m
|
334 |
except Exception as e:
|
335 |
st.error(f"خطا در ایجاد نقشه: {e}")
|
336 |
return None
|
337 |
|
338 |
+
# Generate mock growth data
|
339 |
+
def generate_mock_growth_data(farm_data, selected_variety="all", selected_age="all"):
|
340 |
+
weeks = list(range(1, 23))
|
341 |
+
filtered_farms = farm_data
|
342 |
+
if selected_variety != "all":
|
343 |
+
filtered_farms = filtered_farms[filtered_farms['واریته'] == selected_variety]
|
344 |
+
if selected_age != "all":
|
345 |
+
filtered_farms = filtered_farms[filtered_farms['سن'] == selected_age]
|
346 |
+
|
347 |
+
farm_growth_data = []
|
348 |
+
for _, farm in filtered_farms.iterrows():
|
349 |
+
base_height = np.random.uniform(50, 100)
|
350 |
+
growth_rate = np.random.uniform(5, 15)
|
351 |
+
growth_data = {
|
352 |
+
'farm_id': farm['مزرعه'],
|
353 |
+
'variety': farm['واریته'],
|
354 |
+
'age': farm['سن'],
|
355 |
+
'weeks': weeks,
|
356 |
+
'heights': [round(base_height + growth_rate * week) for week in weeks]
|
357 |
+
}
|
358 |
+
farm_growth_data.append(growth_data)
|
359 |
+
|
360 |
+
if farm_growth_data:
|
361 |
+
avg_heights = [round(sum(farm['heights'][week-1] for farm in farm_growth_data) / len(farm_growth_data)) for week in weeks]
|
362 |
+
avg_growth_data = {
|
363 |
+
'farm_id': 'میانگین',
|
364 |
+
'variety': 'همه',
|
365 |
+
'age': 'همه',
|
366 |
+
'weeks': weeks,
|
367 |
+
'heights': avg_heights
|
368 |
+
}
|
369 |
+
return {'individual': farm_growth_data, 'average': avg_growth_data}
|
370 |
+
return {
|
371 |
+
'individual': [],
|
372 |
+
'average': {'farm_id': 'میانگین', 'variety': 'همه', 'age': 'همه', 'weeks': weeks, 'heights': [0] * len(weeks)}
|
373 |
+
}
|
374 |
+
|
375 |
+
# Calculate farm statistics and detect stress
|
376 |
+
def calculate_farm_stats(farm_id, layer_type="NDVI"):
|
377 |
+
if layer_type == "NDVI":
|
378 |
+
mean = round(np.random.uniform(0.6, 0.8), 2)
|
379 |
+
min_val = round(mean - np.random.uniform(0.2, 0.3), 2)
|
380 |
+
max_val = round(mean + np.random.uniform(0.1, 0.2), 2)
|
381 |
+
std_dev = round(np.random.uniform(0.05, 0.15), 2)
|
382 |
+
elif layer_type == "NDMI":
|
383 |
+
mean = round(np.random.uniform(0.3, 0.5), 2)
|
384 |
+
min_val = round(mean - np.random.uniform(0.2, 0.3), 2)
|
385 |
+
max_val = round(mean + np.random.uniform(0.1, 0.2), 2)
|
386 |
+
std_dev = round(np.random.uniform(0.05, 0.15), 2)
|
387 |
+
elif layer_type == "EVI":
|
388 |
+
mean = round(np.random.uniform(0.4, 0.6), 2)
|
389 |
+
min_val = round(mean - np.random.uniform(0.2, 0.3), 2)
|
390 |
+
max_val = round(mean + np.random.uniform(0.1, 0.2), 2)
|
391 |
+
std_dev = round(np.random.uniform(0.05, 0.15), 2)
|
392 |
+
elif layer_type == "NDWI":
|
393 |
+
mean = round(np.random.uniform(-0.1, 0.1), 2)
|
394 |
+
min_val = round(mean - np.random.uniform(0.2, 0.3), 2)
|
395 |
+
max_val = round(mean + np.random.uniform(0.1, 0.2), 2)
|
396 |
+
std_dev = round(np.random.uniform(0.05, 0.15), 2)
|
397 |
+
elif layer_type == "SoilMoisture":
|
398 |
+
mean = round(np.random.uniform(-20, -10), 2)
|
399 |
+
min_val = round(mean - np.random.uniform(5, 10), 2)
|
400 |
+
max_val = round(mean + np.random.uniform(5, 10), 2)
|
401 |
+
std_dev = round(np.random.uniform(2, 5), 2)
|
402 |
+
|
403 |
+
hist_data = np.random.normal(mean, std_dev, 1000)
|
404 |
+
hist_data = np.clip(hist_data, min_val, max_val)
|
405 |
+
|
406 |
+
stress_alerts = []
|
407 |
+
if layer_type == "NDVI" and mean < 0.3:
|
408 |
+
stress_alerts.append("تنش سلامتی: NDVI پایین نشاندهنده پوشش گیاهی ضعیف است.")
|
409 |
+
if layer_type == "NDMI" and mean < 0.1:
|
410 |
+
stress_alerts.append("تنش آبی: NDMI پایین نشاندهنده کمبود رطوبت است.")
|
411 |
+
if layer_type == "SoilMoisture" and mean < -20:
|
412 |
+
stress_alerts.append("تنش خاک: رطوبت خاک بسیار پایین است.")
|
413 |
+
|
414 |
+
return {
|
415 |
+
'mean': mean, 'min': min_val, 'max': max_val, 'std_dev': std_dev,
|
416 |
+
'histogram_data': hist_data, 'stress_alerts': stress_alerts
|
417 |
+
}
|
418 |
+
|
419 |
+
# Time-series data generation
|
420 |
+
def generate_time_series_data(farm_id, layer_type, period="weekly"):
|
421 |
+
dates = pd.date_range(end=datetime.now(), periods=30 if period == "weekly" else 90 if period == "monthly" else 365, freq='W' if period == "weekly" else 'M' if period == "monthly" else 'Q')
|
422 |
+
if layer_type == "NDVI":
|
423 |
+
values = np.random.normal(0.7, 0.1, len(dates))
|
424 |
+
elif layer_type == "NDMI":
|
425 |
+
values = np.random.normal(0.4, 0.1, len(dates))
|
426 |
+
elif layer_type == "EVI":
|
427 |
+
values = np.random.normal(0.5, 0.1, len(dates))
|
428 |
+
elif layer_type == "NDWI":
|
429 |
+
values = np.random.normal(0, 0.1, len(dates))
|
430 |
+
elif layer_type == "SoilMoisture":
|
431 |
+
values = np.random.normal(-15, 5, len(dates))
|
432 |
+
return pd.DataFrame({'Date': dates, layer_type: values})
|
433 |
+
|
434 |
+
# Advanced prediction with Random Forest and LSTM
|
435 |
+
def predict_growth_advanced(farm_id, historical_data):
|
436 |
+
weeks = historical_data['Week'].values.reshape(-1, 1)
|
437 |
+
heights = historical_data['Height'].values
|
438 |
+
|
439 |
+
# Random Forest
|
440 |
+
rf_model = RandomForestRegressor(n_estimators=100, random_state=42)
|
441 |
+
rf_model.fit(weeks, heights)
|
442 |
+
future_weeks = np.array(range(16, 23)).reshape(-1, 1)
|
443 |
+
rf_predictions = rf_model.predict(future_weeks)
|
444 |
+
|
445 |
+
# LSTM
|
446 |
+
scaler = MinMaxScaler()
|
447 |
+
scaled_heights = scaler.fit_transform(heights.reshape(-1, 1))
|
448 |
+
X, y = [], []
|
449 |
+
for i in range(3, len(scaled_heights)):
|
450 |
+
X.append(scaled_heights[i-3:i])
|
451 |
+
y.append(scaled_heights[i])
|
452 |
+
X, y = np.array(X), np.array(y)
|
453 |
+
|
454 |
+
lstm_model = Sequential([
|
455 |
+
LSTM(50, activation='relu', input_shape=(3, 1), return_sequences=True),
|
456 |
+
LSTM(50, activation='relu'),
|
457 |
+
Dense(1)
|
458 |
+
])
|
459 |
+
lstm_model.compile(optimizer='adam', loss='mse')
|
460 |
+
lstm_model.fit(X, y, epochs=50, batch_size=1, verbose=0)
|
461 |
+
|
462 |
+
lstm_input = scaled_heights[-3:].reshape((1, 3, 1))
|
463 |
+
lstm_predictions = []
|
464 |
+
for _ in range(7):
|
465 |
+
pred = lstm_model.predict(lstm_input, verbose=0)
|
466 |
+
lstm_predictions.append(pred[0, 0])
|
467 |
+
lstm_input = np.append(lstm_input[:, 1:, :], pred.reshape(1, 1, 1), axis=1)
|
468 |
+
lstm_predictions = scaler.inverse_transform(np.array(lstm_predictions).reshape(-1, 1)).flatten()
|
469 |
+
|
470 |
+
return rf_predictions, lstm_predictions
|
471 |
+
|
472 |
+
# Cluster farms
|
473 |
+
def cluster_farms(farm_data):
|
474 |
+
features = farm_data[['مساحت', 'سن']].astype(float)
|
475 |
+
kmeans = KMeans(n_clusters=3, random_state=42)
|
476 |
+
farm_data['Cluster'] = kmeans.fit_predict(features)
|
477 |
+
return farm_data, kmeans.cluster_centers_
|
478 |
+
|
479 |
+
# Initialize Earth Engine
|
480 |
ee_initialized = initialize_earth_engine()
|
481 |
farm_df = load_farm_data()
|
482 |
coordinates_df = load_coordinates_data()
|
483 |
|
484 |
+
# Load animations
|
485 |
+
lottie_farm = load_lottie_url('https://assets5.lottiefiles.com/packages/lf20_ystsffqy.json')
|
486 |
+
lottie_analysis = load_lottie_url('https://assets3.lottiefiles.com/packages/lf20_qp1q7mct.json')
|
487 |
+
lottie_report = load_lottie_url('https://assets9.lottiefiles.com/packages/lf20_vwcugezu.json')
|
488 |
+
|
489 |
+
# Session state
|
490 |
+
if 'heights_df' not in st.session_state:
|
491 |
+
st.session_state.heights_df = pd.DataFrame(columns=['Farm_ID', 'Week', 'Measurement_Date', 'Height', 'Station1', 'Station2', 'Station3', 'Station4', 'Station5', 'Groundwater1', 'Groundwater2', 'Sheath_Moisture', 'Nitrogen', 'Variety', 'Age', 'Area', 'Channel', 'Administration'])
|
492 |
+
|
493 |
+
# Main header
|
494 |
+
st.markdown('<div class="main-header animate-load">', unsafe_allow_html=True)
|
495 |
+
st.markdown('<h1>سامانه هوشمند پایش مزارع نیشکر دهخدا</h1>', unsafe_allow_html=True)
|
496 |
+
st.markdown('<p>پلتفرم جامع مدیریت، پایش و تحلیل دادههای مزارع نیشکر با استفاده از تصاویر ماهوارهای و هوش مصنوعی</p>', unsafe_allow_html=True)
|
497 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
498 |
+
|
499 |
+
# Navigation menu
|
500 |
+
selected = option_menu(
|
501 |
+
menu_title=None,
|
502 |
+
options=["داشبورد", "نقشه مزارع", "ورود اطلاعات", "تحلیل دادهها", "گزارشگیری", "تنظیمات"],
|
503 |
+
icons=["speedometer2", "map", "pencil-square", "graph-up", "file-earmark-text", "gear"],
|
504 |
+
default_index=0,
|
505 |
+
orientation="horizontal",
|
506 |
+
styles={
|
507 |
+
"container": {"padding": "0!important", "background-color": "transparent", "margin-bottom": "20px"},
|
508 |
+
"icon": {"color": "#1a8754", "font-size": "18px"},
|
509 |
+
"nav-link": {"font-size": "16px", "text-align": "center", "margin":"0px", "--hover-color": "#e9f7ef", "border-radius": "10px"},
|
510 |
+
"nav-link-selected": {"background-color": "#1a8754", "color": "white", "font-weight": "600"},
|
511 |
+
}
|
512 |
+
)
|
513 |
|
514 |
+
# Dashboard
|
515 |
if selected == "داشبورد":
|
516 |
+
st.markdown('<div class="animate-load">', unsafe_allow_html=True)
|
517 |
+
col1, col2, col3, col4 = st.columns(4)
|
518 |
+
|
519 |
with col1:
|
520 |
+
st.markdown('<div class="metric-card">', unsafe_allow_html=True)
|
521 |
+
st.markdown(f'<div class="metric-value">{len(farm_df)}</div>', unsafe_allow_html=True)
|
522 |
+
st.markdown('<div class="metric-label">تعداد مزارع</div>', unsafe_allow_html=True)
|
523 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
524 |
+
|
525 |
with col2:
|
526 |
+
active_farms = int(len(farm_df) * 0.85)
|
527 |
+
st.markdown('<div class="metric-card">', unsafe_allow_html=True)
|
528 |
+
st.markdown(f'<div class="metric-value">{active_farms}</div>', unsafe_allow_html=True)
|
529 |
+
st.markdown('<div class="metric-label">مزارع فعال</div>', unsafe_allow_html=True)
|
530 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
531 |
+
|
532 |
with col3:
|
533 |
+
avg_height = 175
|
534 |
+
st.markdown('<div class="metric-card">', unsafe_allow_html=True)
|
535 |
+
st.markdown(f'<div class="metric-value">{avg_height} cm</div>', unsafe_allow_html=True)
|
536 |
+
st.markdown('<div class="metric-label">میانگین ارتفاع</div>', unsafe_allow_html=True)
|
537 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
538 |
+
|
539 |
+
with col4:
|
540 |
+
avg_moisture = 68
|
541 |
+
st.markdown('<div class="metric-card">', unsafe_allow_html=True)
|
542 |
+
st.markdown(f'<div class="metric-value">{avg_moisture}%</div>', unsafe_allow_html=True)
|
543 |
+
st.markdown('<div class="metric-label">میانگین رطوبت</div>', unsafe_allow_html=True)
|
544 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
545 |
+
|
546 |
+
# Interactive Dashboard Filters
|
547 |
+
st.markdown("### فیلترهای داشبورد")
|
548 |
+
col_filter1, col_filter2, col_filter3 = st.columns(3)
|
549 |
+
with col_filter1:
|
550 |
+
variety_filter = st.multiselect("واریته", ["همه"] + list(farm_df['واریته'].unique()), default="همه")
|
551 |
+
with col_filter2:
|
552 |
+
age_filter = st.multiselect("سن", ["همه"] + list(farm_df['سن'].unique()), default="همه")
|
553 |
+
with col_filter3:
|
554 |
+
channel_filter = st.multiselect("کانال", ["همه"] + list(farm_df['کانال'].unique()), default="همه")
|
555 |
+
|
556 |
+
filtered_df = farm_df
|
557 |
+
if "همه" not in variety_filter:
|
558 |
+
filtered_df = filtered_df[filtered_df['واریته'].isin(variety_filter)]
|
559 |
+
if "همه" not in age_filter:
|
560 |
+
filtered_df = filtered_df[filtered_df['سن'].isin(age_filter)]
|
561 |
+
if "همه" not in channel_filter:
|
562 |
+
filtered_df = filtered_df[filtered_df['کانال'].isin(channel_filter)]
|
563 |
+
|
564 |
+
tab1, tab2, tab3, tab4 = st.tabs(["نمای کلی", "نقشه مزارع", "نمودارها", "دادهها"])
|
565 |
+
|
566 |
+
with tab1:
|
567 |
+
st.markdown("### توزیع واریتهها و سن محصول")
|
568 |
+
col1, col2 = st.columns(2)
|
569 |
+
with col1:
|
570 |
+
variety_counts = filtered_df['واریته'].value_counts().reset_index()
|
571 |
+
variety_counts.columns = ['واریته', 'تعداد']
|
572 |
+
fig = px.pie(variety_counts, values='تعداد', names='واریته', title='توزیع واریتهها', color_discrete_sequence=px.colors.sequential.Greens_r)
|
573 |
+
st.plotly_chart(fig, use_container_width=True)
|
574 |
+
with col2:
|
575 |
+
age_counts = filtered_df['سن'].value_counts().reset_index()
|
576 |
+
age_counts.columns = ['سن', 'تعداد']
|
577 |
+
fig = px.pie(age_counts, values='تعداد', names='سن', title='توزیع سن محصول', color_discrete_sequence=px.colors.sequential.Blues_r)
|
578 |
+
st.plotly_chart(fig, use_container_width=True)
|
579 |
+
|
580 |
+
st.markdown("### اطلاعات کلی مزارع")
|
581 |
+
total_area = filtered_df['مساحت'].astype(float).sum()
|
582 |
+
col1, col2, col3 = st.columns(3)
|
583 |
+
col1.metric("تعداد کل مزارع", f"{len(filtered_df)}")
|
584 |
+
col2.metric("مساحت کل (هکتار)", f"{total_area:.2f}")
|
585 |
+
col3.metric("تعداد کانالها", f"{filtered_df['کانال'].nunique()}")
|
586 |
+
st_lottie(lottie_farm, height=300, key="farm_animation")
|
587 |
+
|
588 |
+
with tab2:
|
589 |
+
st.markdown("### نقشه مزارع")
|
590 |
+
if not coordinates_df.empty:
|
591 |
+
m = folium.Map(location=[31.45, 48.72], zoom_start=12, tiles='CartoDB positron')
|
592 |
+
for _, farm in coordinates_df.iterrows():
|
593 |
+
lat, lon = farm['عرض جغرافیایی'], farm['طول جغرافیایی']
|
594 |
+
name = farm['مزرعه']
|
595 |
+
farm_info = filtered_df[filtered_df['مزرعه'] == name]
|
596 |
+
if not farm_info.empty:
|
597 |
+
popup_text = f"""
|
598 |
+
<div style="direction: rtl;">
|
599 |
+
<h4>مزرعه {name}</h4>
|
600 |
+
<p>واریته: {farm_info['واریته'].iloc[0]}</p>
|
601 |
+
<p>سن: {farm_info['سن'].iloc[0]}</p>
|
602 |
+
<p>مساحت: {farm_info['مساحت'].iloc[0]} هکتار</p>
|
603 |
+
</div>
|
604 |
+
"""
|
605 |
+
folium.Marker([lat, lon], popup=folium.Popup(popup_text, max_width=300), tooltip=f"مزرعه {name}", icon=folium.Icon(color='green', icon='leaf')).add_to(m)
|
606 |
+
st.markdown('<div class="map-container">', unsafe_allow_html=True)
|
607 |
+
folium_static(m, width=1000, height=600)
|
608 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
609 |
+
|
610 |
+
with tab3:
|
611 |
+
st.markdown("### نمودار رشد هفتگی")
|
612 |
+
growth_data = generate_mock_growth_data(filtered_df)
|
613 |
+
chart_tab1, chart_tab2 = st.tabs(["میانگین رشد", "رشد مزارع فردی"])
|
614 |
+
with chart_tab1:
|
615 |
+
fig = go.Figure()
|
616 |
+
fig.add_trace(go.Scatter(x=growth_data['average']['weeks'], y=growth_data['average']['heights'], mode='lines+markers', name='میانگین رشد', line=dict(color='#1a8754', width=3)))
|
617 |
+
fig.update_layout(title='میانگین رشد هفتگی', xaxis_title='هفته', yaxis_title='ارتفاع (سانتیمتر)', font=dict(family='Vazirmatn'))
|
618 |
+
st.plotly_chart(fig, use_container_width=True)
|
619 |
+
with chart_tab2:
|
620 |
+
fig = go.Figure()
|
621 |
+
for i, farm_data in enumerate(growth_data['individual'][:5]):
|
622 |
+
fig.add_trace(go.Scatter(x=farm_data['weeks'], y=farm_data['heights'], mode='lines+markers', name=f"مزرعه {farm_data['farm_id']}"))
|
623 |
+
fig.update_layout(title='رشد هفتگی مزارع فردی', xaxis_title='هفته', yaxis_title='ارتفاع (سانتیمتر)', font=dict(family='Vazirmatn'))
|
624 |
+
st.plotly_chart(fig, use_container_width=True)
|
625 |
+
|
626 |
+
with tab4:
|
627 |
+
st.markdown("### دادههای مزارع")
|
628 |
+
st.dataframe(filtered_df, use_container_width=True, height=400, hide_index=True)
|
629 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
630 |
+
|
631 |
+
# Map Page
|
632 |
+
elif selected == "نقشه مزارع":
|
633 |
+
st.markdown('<div class="animate-load">', unsafe_allow_html=True)
|
634 |
+
st.markdown("## نقشه مزارع با شاخصهای ماهوارهای")
|
635 |
+
col1, col2 = st.columns([1, 3])
|
636 |
+
with col1:
|
637 |
+
st.markdown('<div class="glass-card">', unsafe_allow_html=True)
|
638 |
+
selected_farm = st.selectbox("انتخاب مزرعه", options=coordinates_df['مزرعه'].tolist(), format_func=lambda x: f"مزرعه {x}")
|
639 |
+
selected_date = st.date_input("انتخاب تاریخ", value=datetime.now())
|
640 |
+
selected_layer = st.selectbox("انتخاب شاخص", options=["NDVI", "NDMI", "EVI", "NDWI", "SoilMoisture"], format_func=lambda x: {"NDVI": "شاخص پوشش گیاهی (NDVI)", "NDMI": "شاخص رطوبت (NDMI)", "EVI": "شاخص پیشرفته گیاهی (EVI)", "NDWI": "شاخص آب (NDWI)", "SoilMoisture": "رطوبت خاک (Soil Moisture)"}[x])
|
641 |
+
heatmap_toggle = st.checkbox("نمایش نقشه حرارتی", value=False)
|
642 |
+
generate_map = st.button("تولید نقشه", type="primary", use_container_width=True)
|
643 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
644 |
+
|
645 |
+
with col2:
|
646 |
+
map_tab, stats_tab = st.tabs(["نقشه", "آمار و تحلیل"])
|
647 |
+
with map_tab:
|
648 |
+
if generate_map or 'last_map' not in st.session_state:
|
649 |
+
with st.spinner('در حال تولید نقشه...'):
|
650 |
+
m = create_ee_map(selected_farm, selected_date.strftime('%Y-%m-%d'), selected_layer, heatmap=heatmap_toggle)
|
651 |
+
if m:
|
652 |
+
st.session_state.last_map = m
|
653 |
+
folium_static(m, width=800, height=600)
|
654 |
+
st.success(f"نقشه {selected_layer} برای مزرعه {selected_farm} تولید شد.")
|
655 |
+
elif 'last_map' in st.session_state:
|
656 |
+
folium_static(st.session_state.last_map, width=800, height=600)
|
657 |
+
|
658 |
+
with stats_tab:
|
659 |
+
if 'last_map' in st.session_state:
|
660 |
+
stats = calculate_farm_stats(selected_farm, selected_layer)
|
661 |
+
col1, col2, col3, col4 = st.columns(4)
|
662 |
+
with col1: st.metric(f"میانگین {selected_layer}", stats["mean"])
|
663 |
+
with col2: st.metric(f"حداکثر {selected_layer}", stats["max"])
|
664 |
+
with col3: st.metric(f"حداقل {selected_layer}", stats["min"])
|
665 |
+
with col4: st.metric("انحراف معیار", stats["std_dev"])
|
666 |
+
|
667 |
+
if stats['stress_alerts']:
|
668 |
+
for alert in stats['stress_alerts']:
|
669 |
+
st.warning(alert)
|
670 |
+
|
671 |
+
fig = px.histogram(x=stats["histogram_data"], nbins=20, title=f"توزیع مقادیر {selected_layer}", labels={"x": f"مقدار {selected_layer}", "y": "فراوانی"})
|
672 |
+
st.plotly_chart(fig, use_container_width=True)
|
673 |
+
|
674 |
+
st.markdown("### تحلیل سری زمانی")
|
675 |
+
period = st.selectbox("بازه زمانی", ["weekly", "monthly", "seasonal"], format_func=lambda x: {"weekly": "هفتگی", "monthly": "ماهانه", "seasonal": "فصلی"}[x])
|
676 |
+
ts_data = generate_time_series_data(selected_farm, selected_layer, period)
|
677 |
+
fig = px.line(ts_data, x='Date', y=selected_layer, title=f"روند تغییرات {selected_layer}")
|
678 |
+
st.plotly_chart(fig, use_container_width=True)
|
679 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
680 |
+
|
681 |
+
# Data Entry Page
|
682 |
+
elif selected == "ورود اطلاعات":
|
683 |
+
st.markdown('<div class="animate-load">', unsafe_allow_html=True)
|
684 |
+
st.markdown("## ورود اطلاعات روزانه مزارع")
|
685 |
+
tab1, tab2 = st.tabs(["ورود دستی", "آپلود فایل"])
|
686 |
+
with tab1:
|
687 |
+
col1, col2 = st.columns(2)
|
688 |
+
with col1:
|
689 |
+
selected_week = st.selectbox("انتخاب هفته", [str(i) for i in range(1, 23)], format_func=lambda x: f"هفته {x}")
|
690 |
+
with col2:
|
691 |
+
selected_day = st.selectbox("انتخاب روز", ["شنبه", "یکشنبه", "دوشنبه", "سهشنبه", "چهارشنبه", "پنجشنبه"])
|
692 |
+
|
693 |
+
filtered_farms = farm_df[farm_df['روز'] == selected_day]
|
694 |
+
data_key = f"data_{selected_week}_{selected_day}"
|
695 |
+
if data_key not in st.session_state:
|
696 |
+
st.session_state[data_key] = pd.DataFrame({
|
697 |
+
'مزرعه': filtered_farms['مزرعه'],
|
698 |
+
'ایستگاه 1': [0] * len(filtered_farms),
|
699 |
+
'ایستگاه 2': [0] * len(filtered_farms),
|
700 |
+
'ایستگاه 3': [0] * len(filtered_farms),
|
701 |
+
'ایستگاه 4': [0] * len(filtered_farms),
|
702 |
+
'ایستگاه 5': [0] * len(filtered_farms),
|
703 |
+
'چاهک 1': [0] * len(filtered_farms),
|
704 |
+
'چاهک 2': [0] * len(filtered_farms),
|
705 |
+
'رطوبت غلاف': [0] * len(filtered_farms),
|
706 |
+
'نیتروژن': [0] * len(filtered_farms),
|
707 |
+
'میانگین ارتفاع': [0] * len(filtered_farms)
|
708 |
+
})
|
709 |
+
|
710 |
+
edited_df = st.data_editor(st.session_state[data_key], use_container_width=True)
|
711 |
+
for i in range(len(edited_df)):
|
712 |
+
stations = [edited_df.iloc[i][f'ایستگاه {j}'] for j in range(1, 6)]
|
713 |
+
valid_stations = [s for s in stations if s > 0]
|
714 |
+
if valid_stations:
|
715 |
+
edited_df.iloc[i, edited_df.columns.get_loc('میانگین ارتفاع')] = round(sum(valid_stations) / len(valid_stations), 1)
|
716 |
+
st.session_state[data_key] = edited_df
|
717 |
+
|
718 |
+
if st.button("ذخیره اطلاعات", type="primary"):
|
719 |
+
new_data = edited_df.copy()
|
720 |
+
new_data['Farm_ID'] = new_data['مزرعه']
|
721 |
+
new_data['Week'] = int(selected_week)
|
722 |
+
new_data['Measurement_Date'] = (datetime.now() - timedelta(weeks=(22 - int(selected_week)))).strftime('%Y-%m-%d')
|
723 |
+
new_data['Height'] = new_data['میانگین ارتفاع']
|
724 |
+
new_data = new_data.merge(farm_df[['مزرعه', 'واریته', 'سن', 'مساحت', 'کانال', 'اداره']], left_on='Farm_ID', right_on='مزرعه', how='left')
|
725 |
+
st.session_state.heights_df = pd.concat([st.session_state.heights_df, new_data], ignore_index=True)
|
726 |
+
st.success(f"دادههای هفته {selected_week} ذخیره شدند.")
|
727 |
+
with tab2:
|
728 |
+
uploaded_file = st.file_uploader("فایل اکسل خود را آپلود کنید", type=["xlsx", "xls", "csv"])
|
729 |
+
if uploaded_file:
|
730 |
+
df = pd.read_csv(uploaded_file) if uploaded_file.name.endswith('.csv') else pd.read_excel(uploaded_file)
|
731 |
+
st.dataframe(df)
|
732 |
+
if st.button("ذخیره فایل"): st.success("فایل ذخیره شد.")
|
733 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
734 |
+
|
735 |
+
# Data Analysis Page
|
736 |
+
elif selected == "تحلیل دادهها":
|
737 |
+
st.markdown('<div class="animate-load">', unsafe_allow_html=True)
|
738 |
+
st.markdown("## تحلیل هوشمند دادهها")
|
739 |
+
tab1, tab2, tab3, tab4 = st.tabs(["تحلیل رشد", "مقایسه واریتهها", "تحلیل رطوبت", "پیشبینی"])
|
740 |
+
|
741 |
+
with tab1:
|
742 |
+
growth_data = generate_mock_growth_data(farm_df)
|
743 |
+
chart_data = []
|
744 |
+
for farm_data in growth_data['individual']:
|
745 |
+
for i, week in enumerate(farm_data['weeks']):
|
746 |
+
chart_data.append({'Farm': farm_data['farm_id'], 'Week': week, 'Height': farm_data['heights'][i]})
|
747 |
+
chart_df = pd.DataFrame(chart_data)
|
748 |
+
chart = alt.Chart(chart_df).mark_line(point=True).encode(x='Week:Q', y='Height:Q', color='Farm:N').interactive()
|
749 |
+
st.altair_chart(chart, use_container_width=True)
|
750 |
+
|
751 |
+
with tab2:
|
752 |
+
variety_heights = {variety: np.random.normal(150, 20, 100) for variety in farm_df['واریته'].unique()}
|
753 |
+
fig = go.Figure()
|
754 |
+
for variety in variety_heights:
|
755 |
+
fig.add_trace(go.Box(y=variety_heights[variety], name=variety))
|
756 |
+
fig.update_layout(title='مقایسه ارتفاع بر اساس واریته', yaxis_title='ارتفاع (سانتیمتر)', font=dict(family="Vazirmatn"))
|
757 |
+
st.plotly_chart(fig, use_container_width=True)
|
758 |
+
|
759 |
+
with tab3:
|
760 |
+
farms = farm_df['مزرعه'].unique()[:10]
|
761 |
+
dates = pd.date_range(end=datetime.now(), periods=30)
|
762 |
+
moisture_data = [{'Farm': farm, 'Date': date, 'Moisture': max(0, min(100, np.random.uniform(50, 80) + np.random.normal(0, 5)))} for farm in farms for date in dates]
|
763 |
+
moisture_df = pd.DataFrame(moisture_data)
|
764 |
+
fig = px.line(moisture_df, x='Date', y='Moisture', color='Farm', title='روند رطوبت مزارع')
|
765 |
+
|
766 |
+
# Correlation analysis
|
767 |
+
correlation_data = [{'Moisture': m, 'Height': 100 + m * 1.5 + np.random.normal(0, 20)} for m in moisture_df['Moisture']]
|
768 |
+
corr_df = pd.DataFrame(correlation_data)
|
769 |
+
fig_corr = px.scatter(corr_df, x='Moisture', y='Height', title='همبستگی رطوبت و ارتفاع', trendline='ols')
|
770 |
+
st.plotly_chart(fig, use_container_width=True)
|
771 |
+
st.plotly_chart(fig_corr, use_container_width=True)
|
772 |
+
st.info(f"ضریب همبستگی: {corr_df['Moisture'].corr(corr_df['Height']):.2f}")
|
773 |
+
|
774 |
+
with tab4:
|
775 |
+
selected_farm = st.selectbox("انتخاب مزرعه", options=farm_df['مزرعه'].tolist(), format_func=lambda x: f"مزرعه {x}")
|
776 |
+
weeks = list(range(1, 16))
|
777 |
+
heights = [50 + i * 10 + np.random.normal(0, 5) for i in weeks]
|
778 |
+
historical_df = pd.DataFrame({'Week': weeks, 'Height': heights})
|
779 |
+
rf_preds, lstm_preds = predict_growth_advanced(selected_farm, historical_df)
|
780 |
+
future_weeks = list(range(16, 23))
|
781 |
+
|
782 |
+
fig = go.Figure()
|
783 |
+
fig.add_trace(go.Scatter(x=weeks, y=heights, mode='lines+markers', name='دادههای تاریخی'))
|
784 |
+
fig.add_trace(go.Scatter(x=future_weeks, y=rf_preds, mode='lines+markers', name='پیشبینی Random Forest', line=dict(dash='dash')))
|
785 |
+
fig.add_trace(go.Scatter(x=future_weeks, y=lstm_preds, mode='lines+markers', name='پیشبینی LSTM', line=dict(dash='dot')))
|
786 |
+
fig.update_layout(title=f'پیشبینی رشد مزرعه {selected_farm}', xaxis_title='هفته', yaxis_title='ارتفاع (سانتیمتر)', font=dict(family='Vazirmatn'))
|
787 |
+
st.plotly_chart(fig, use_container_width=True)
|
788 |
+
|
789 |
+
# Clustering
|
790 |
+
clustered_df, centers = cluster_farms(farm_df)
|
791 |
+
fig = px.scatter(clustered_df, x='مساحت', y='سن', color='Cluster', title='گروهبندی مزارع', labels={'مساحت': 'مساحت (هکتار)', 'سن': 'سن (سال)'})
|
792 |
+
st.plotly_chart(fig, use_container_width=True)
|
793 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
794 |
+
|
795 |
+
# Reporting Page
|
796 |
+
elif selected == "گزارشگیری":
|
797 |
+
st.markdown('<div class="animate-load">', unsafe_allow_html=True)
|
798 |
+
st.markdown("## گزارشگیری پیشرفته")
|
799 |
+
start_date = st.date_input("تاریخ شروع", value=datetime.now() - timedelta(days=30))
|
800 |
+
end_date = st.date_input("تاریخ پایان", value=datetime.now())
|
801 |
+
report_type = st.selectbox("نوع گزارش", ["گزارش کلی", "گزارش رشد", "گزارش رطوبت", "گزارش مقایسهای واریتهها"])
|
802 |
+
if st.button("تولید گزارش"):
|
803 |
+
with st.spinner('در حال تولید گزارش...'):
|
804 |
+
time.sleep(2)
|
805 |
+
if report_type == "گزارش کلی":
|
806 |
+
st.markdown("### گزارش کلی وضعیت مزارع")
|
807 |
+
col1, col2 = st.columns(2)
|
808 |
+
with col1:
|
809 |
+
fig = px.pie(values=farm_df['اداره'].value_counts().values, names=farm_df['اداره'].value_counts().index, title='توزیع مزارع بر اساس اداره')
|
810 |
+
st.plotly_chart(fig)
|
811 |
+
with col2:
|
812 |
+
weeks = list(range(1, 23))
|
813 |
+
heights = [100 + i * 5 + np.random.normal(0, 10) for i in weeks]
|
814 |
+
fig = px.line(x=weeks, y=heights, title='روند رشد کلی مزارع', labels={'x': 'هفته', 'y': 'ارتفاع (سانتیمتر)'})
|
815 |
+
st.plotly_chart(fig)
|
816 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
817 |
+
|
818 |
+
# Settings Page
|
819 |
+
elif selected == "تنظیمات":
|
820 |
+
st.markdown('<div class="animate-load">', unsafe_allow_html=True)
|
821 |
+
st.markdown("## تنظیمات سیستم")
|
822 |
+
tab1, tab2 = st.tabs(["تنظیمات کاربری", "تنظیمات سیستم"])
|
823 |
+
with tab1:
|
824 |
+
user_name = st.text_input("نام کاربری", value="کاربر نمونه")
|
825 |
+
user_email = st.text_input("ایمیل", value="[email protected]")
|
826 |
+
with tab2:
|
827 |
+
system_language = st.selectbox("زبان سیستم", ["فارسی", "English", "العربية"])
|
828 |
+
date_format = st.selectbox("فرمت تاریخ", ["YYYY/MM/DD", "DD/MM/YYYY", "MM/DD/YYYY"])
|
829 |
+
st.markdown('</div>', unsafe_allow_html=True)
|
830 |
+
|
831 |
+
# Footer
|
832 |
+
st.markdown("""
|
833 |
+
<footer style="position: fixed; left: 0; bottom: 0; width: 100%; background-color: #1a8754; color: white; text-align: center; padding: 10px 0;">
|
834 |
+
<p>© سامانه هوشمند پایش مزارع نیشکر کشت و صنعت دهخدا | طراحی شده توسط تیم مطالعات کاربردی توسعه</p>
|
835 |
+
</footer>
|
836 |
+
""", unsafe_allow_html=True)
|
837 |
+
st.markdown('</body>', unsafe_allow_html=True)
|